RISC-V MCU应用教程之低功耗待机模式

以RISC-V MCU CH32V307为例。

1. 待机模式简介

待机模式(Standby),芯片的运行功耗最低

该模式下,内核处于深睡眠模式(Sleep Deep), 电压调节器进入停机模式。

待机模式唤醒后,MCU将执行电源复位

该模式下的唤醒时间约 8.9ms (LDO稳定时间 + HSI RC时钟唤醒 + 代码加载时间)。

此模式下的睡眠电流约为1~2uA

可以通过配置PWR_CTLR寄存器控制该模式下部分SRAM不掉电,使用变量数据保持,睡眠电流增加约 1.5~2uA 配置方式如下:

控制位名称说明复位值
19 R30K_VBATEN VBAT供电时,Standby模式下前 30K RAM是否带电: 1:带电 0:不带电 0
18 R2K_VBATE VBAT供电时,Standby模式下前 30K RAM是否带电: 1:带电 0:不带电 0
17 R30K_STYEN Standby模式下前 30K RAM是否带电: 1:带电 0:不带电 0
16 R2K_STYEN Standby模式下前 2K RAM是否带电: 1:带电 0:不带电 0

2.进入待机模式

进入待机模式步骤如下:

  • 配置 PFIC_SCTLR 系统控制寄存器控制位 SLEEPDEEP = 1

  • 配置 PWR_CTLR 电源控制寄存器控制位 PDDS = 1

  • 清除电源控制/状态寄存器 PWR_SCR 中的 WUF 位,PWR_CTLRCWUF 位写1清零

  • 执行 WFIWFE指令

 

3. 退出待机模式

退出待机模式步骤如下:

  • WKUP引脚上升沿

  • RTC闹钟事件的上升沿

  • NRST引脚上的外部复位

  • IWDG复位

  • 唤醒事件,配置一个外部EXTI线为事件模式,当CPU从WFE唤醒后,因为对应事件线的挂起位没有被置位,不必清除相应外设的中断挂起位或PFIC中断通道挂起位。

:::tip

  • WKUP引脚上升沿唤醒和RTC闹钟唤醒事件会置电源控制/状态寄存器 PWR_SCR 中的 WUF 位,此寄存器从待机模式唤醒后保持不变,通过PWR_CTLRCWUF 位写1 清除WUF标志。

:::

4. 参考代码

 参考代码 - 外部事件唤醒和WKUP引脚唤醒,SRAM不保持
复制代码
 1 #include "debug.h"
 2 
 3 void wakeup_init(void)
 4 {
 5     GPIO_InitTypeDef GPIO_InitStructure = {0};
 6     EXTI_InitTypeDef EXTI_InitStructure = {0};
 7 
 8     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO , ENABLE);
 9 
10     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
11     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
12     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
13     GPIO_Init(GPIOA, &GPIO_InitStructure);
14 
15     /* GPIOA.3 ----> EXTI_Line3 */
16     GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);
17     EXTI_InitStructure.EXTI_Line = EXTI_Line3;
18 
19     /* WFI - EXTI_Mode_Interrupt, WFE - EXTI_Mode_Event*/
20     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; 
21 
22     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
23     EXTI_InitStructure.EXTI_LineCmd = ENABLE;
24     EXTI_Init(&EXTI_InitStructure);
25 
26 }
27 
28 int main(void)
29 {
30 
31     /* Configure unused GPIO as IPD to reduce power consumption */
32     GPIO_InitTypeDef GPIO_InitStructure = {0};   
33     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|
34              RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE, ENABLE);
35     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
36     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
37 
38     GPIO_Init(GPIOA, &GPIO_InitStructure);
39     GPIO_Init(GPIOB, &GPIO_InitStructure);
40     GPIO_Init(GPIOC, &GPIO_InitStructure);
41     GPIO_Init(GPIOD, &GPIO_InitStructure);
42     GPIO_Init(GPIOE, &GPIO_InitStructure);
43     /***************************************/
44 
45 
46     Delay_Init();
47     USART_Printf_Init(256000);
48 
49     printf("ch32v307 standby test\r\n");
50     Delay_Ms(2000);
51 
52     /* wakeup event, PA3 EXTI trigger falling */
53     wakeup_init(); 
54 
55     /* set PWR register, need enable rcc of pwr */
56     RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); 
57 
58     /* WKUP or RTC wakeup event flag, clear by set PWR_CTLR CWUF */
59     printf("PWR_CSR WUF = %ld \r\n",PWR->CSR & 0x1); 
60     if(PWR->CSR & 0x1)
61     {
62         PWR->CTLR |= (1<<2); /* clear wakeup flag */
63     }
64 
65     /* enable WKUP, PA0 */
66     PWR_WakeUpPinCmd(ENABLE);
67 
68     /* standby mode, mcu will reset after wakeup event */
69     PWR_EnterSTANDBYMode();
70 
71     printf("wakeup\r\n");
72 
73     while(1)
74     {
75         printf("run in main loop\r\n");
76         Delay_Ms(1000);
77     }
78 }
复制代码

 

 参考代码 - SRAM保持,WKUP 上升沿唤醒
复制代码
  1 #include "debug.h"
  2 
  3 
  4 void ram_write_test(void)
  5 {
  6     uint32_t myAddr=0x20000000+10*1024;
  7     uint32_t myAddr2=0x20000000+1*1024+512;
  8     uint32_t i=0;
  9     for(i=0;i<10;i++)//Write 30K RAM
 10     {
 11         *(uint32_t volatile *)(myAddr+(i<<2)) = 0x12345678;
 12     }
 13     for(i=0;i<10;i++)//Check 30K RAM
 14     {
 15         if((*(uint32_t volatile *)(myAddr+(i<<2))) != 0x12345678)
 16         {
 17             printf("Write 30K RAM Error!\r\n");
 18             break;
 19         }
 20     }
 21     printf("Write 30K RAM Successfully!\r\n");
 22     for(i=0;i<10;i++)//Write 2K RAM
 23     {
 24         *(uint32_t volatile *)(myAddr2+(i<<2)) = 0x12345678;
 25     }
 26     for(i=0;i<10;i++)//Check 2K RAM
 27     {
 28         if((*(uint32_t volatile *)(myAddr2+(i<<2))) != 0x12345678)
 29         {
 30             printf("Write 2K RAM Error!\r\n");
 31             break;
 32         }
 33     }
 34     printf("Write 2K RAM Successfully!\r\n");
 35 
 36 }
 37 
 38 
 39 void ram_read_test(void)
 40 {
 41     uint32_t myAddr=0x20000000+10*1024;
 42     uint32_t myAddr2=0x20000000+1*1024+512;
 43     uint32_t i=0;
 44     printf("2K RAM:\r\n");
 45     for(i=0;i<10;i++)
 46     {
 47         printf("0x%08lx=0x%08lx\r\n",myAddr2+(i<<2),*(uint32_t volatile *)(myAddr2+(i<<2)));
 48     }
 49     printf("32K RAM:\r\n");
 50     for(i=0;i<10;i++)
 51     {
 52         printf("0x%08lx=0x%08lx\r\n",myAddr+(i<<2),*(uint32_t volatile *)(myAddr+(i<<2)));
 53     }
 54 
 55 }
 56 
 57 
 58 void wakeup_init(void)
 59 {
 60     GPIO_InitTypeDef GPIO_InitStructure = {0};
 61     EXTI_InitTypeDef EXTI_InitStructure = {0};
 62 
 63     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO , ENABLE);
 64 
 65     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
 66     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
 67     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 68     GPIO_Init(GPIOA, &GPIO_InitStructure);
 69 
 70     /* GPIOA.3 ----> EXTI_Line3 */
 71     GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);
 72     EXTI_InitStructure.EXTI_Line = EXTI_Line3;
 73 
 74     /* WFI - EXTI_Mode_Interrupt, WFE - EXTI_Mode_Event*/
 75     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; 
 76 
 77     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
 78     EXTI_InitStructure.EXTI_LineCmd = ENABLE;
 79     EXTI_Init(&EXTI_InitStructure);
 80 
 81 }
 82 
 83 int main(void)
 84 {
 85 
 86     /* Configure unused GPIO as IPD to reduce power consumption */
 87     GPIO_InitTypeDef GPIO_InitStructure = {0};   
 88     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|
 89              RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE, ENABLE);
 90     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
 91     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
 92 
 93     GPIO_Init(GPIOA, &GPIO_InitStructure);
 94     GPIO_Init(GPIOB, &GPIO_InitStructure);
 95     GPIO_Init(GPIOC, &GPIO_InitStructure);
 96     GPIO_Init(GPIOD, &GPIO_InitStructure);
 97     GPIO_Init(GPIOE, &GPIO_InitStructure);
 98     /***************************************/
 99 
100 
101     Delay_Init();
102     USART_Printf_Init(256000);
103 
104     printf("ch32v307 standby test\r\n");
105     Delay_Ms(2000);
106 
107     /* wakeup event, PA3 EXTI trigger falling */
108     wakeup_init(); 
109 
110     ram_read_test();
111     ram_write_test();
112 
113     /* set PWR register, need enable rcc of pwr */
114     RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); 
115 
116     /* WKUP or RTC wakeup event flag, clear by set PWR_CTLR CWUF */
117     printf("PWR_CSR WUF = %ld \r\n",PWR->CSR & PWR_FLAG_WU); 
118     if(PWR->CSR & PWR_FLAG_WU)
119     {
120         PWR->CTLR |= (1<<2); /* clear wakeup flag */
121     }
122 
123     /* enable WKUP, PA0 */
124     PWR_WakeUpPinCmd(ENABLE);
125 
126     /* standby mode, mcu will reset after wakeup event */
127     PWR_EnterSTANDBYMode_RAM_LV();
128     // PWR_EnterSTANDBYMode_RAM();
129 
130     printf("wakeup\r\n");
131 
132     while(1)
133     {
134         printf("run in main loop\r\n");
135         Delay_Ms(1000);
136     }
137 }
复制代码

:::tip 当MCU处于停止模式或待机模式时,两线调试仿真接口是处于关闭状态,此时是无法用 WCH-Link 进行程序的下载。若唤醒配置不正确,MCU无法唤醒,此时就会和MCU失联。解决方式如下:

  • 在主函数启动时加一个较长的延时函数,按住板子的复位键,使系统处于复位状态,然后点击 IDE 下载按钮,这时再释放复位键,这样MCU上电后执行延时函数期间对低功耗程序进行擦除。

  • 选择启动方式,通过将BOOT0拉高,重新上电使MCU从SRAM启动,重新选择一个非睡眠程序下载,下载完成后将BOOT0拉低,然后重新上电即可重新下载程序。

:::

 
posted @   Zhu_zzzzzz  阅读(257)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示