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_CTLR
的CWUF
位写1清零 -
执行
WFI
或WFE
指令
3. 退出待机模式
退出待机模式步骤如下:
-
WKUP引脚上升沿
-
RTC闹钟事件的上升沿
-
NRST引脚上的外部复位
-
IWDG复位
-
唤醒事件,配置一个外部EXTI线为事件模式,当CPU从WFE唤醒后,因为对应事件线的挂起位没有被置位,不必清除相应外设的中断挂起位或PFIC中断通道挂起位。
:::tip
-
WKUP引脚上升沿唤醒和RTC闹钟唤醒事件会置电源控制/状态寄存器
PWR_SCR
中的WUF
位,此寄存器从待机模式唤醒后保持不变,通过PWR_CTLR
的CWUF
位写1 清除WUF
标志。
:::
4. 参考代码
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 }
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上电后执行延时函数期间对低功耗程序进行擦除。
-
:::
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?