RISC-V MCU应用教程之低功耗之停止模式
以RISC-V MCU CH32V307为例
1. 停止模式简介
停止模式是在内核深睡眠模式(SLEEPDEEP)基础上结合了外设的时钟控制机制,并可以让电压调节器运行在低功耗模式。
此模式下,高频时钟(HSE/HSI/PLL)被关闭,SRAM和寄存器内容保持,IO引脚状态保持。
该模式唤醒后,系统可继续运行,特别注意的是,此时默认系统时钟为内部 8MHz HSI,如有需要,可调用 SystemInit()
重新初始化时钟 。
停止模式下可工作的模块:
-
独立看门狗(IWDG)
-
实时时钟(RTC)
-
低频时钟(LSI/LSE)
该模式下的唤醒时间为 HSI RC唤醒时间(23.1 us) + 电压调节器从低功耗模式唤醒时间(53.6 us 如果开启),总共约 76.7us
此模式下,电压调节器处于低功耗模式的睡眠电流约为 34uA,电压调节器处于正常状态的睡眠电流约为 110.5uA。
:::tip
为了进一步降低功耗,建议将不用的 IO 引脚初始化为下拉输入模式。
:::
2. 进入停止模式
进入停止模式的步骤:
-
配置内核寄存器
PFIC_SCTLR
控制位SLEEPDEEP=1
-
如果需要配置调压器进入低功耗模式,将电源控制寄存器
PWR_CTLR
控制位PDDS = 0
,控制位LPDS=1
, -
执行
WFI
或WFE
指令
:::tip
-
如果正在进行闪存编程,直到对内存访问完成,系统才进入停止模式;
-
如果正在进行对APB 的访问,直到对APB 访问完成,系统才进入停止模式
:::
3. 退出停止模式
退出停止模式的条件:
-
任一外部中断 EXTI0~ EXTI21,需要在外部中断寄存器中设置
-
唤醒事件:
-
配置一个外部EXTI线为事件模式,当CPU从WFE唤醒后,因为对应事件线的挂起位没有被置位,不必清除相应外设的中断挂起位或PFIC中断通道挂起位。
-
在外设的控制寄存器使能一个中断,但不在PFIC中使能,同时需要使能寄存器
PFIC_SCTLR
控制位SEVONPEND
。当CPU从WFE唤醒后,需要清除相应外设的中断挂起位或PFIC中断通道挂起位。
-
:::caution
不支持WKUP引脚上升沿唤醒
:::
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_Interrupt; 21 22 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 23 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 24 EXTI_Init(&EXTI_InitStructure); 25 26 /* when configured as event, no need to enable NVIC */ 27 NVIC_SetPriority(EXTI3_IRQn,0x00); 28 NVIC_EnableIRQ(EXTI3_IRQn); 29 30 } 31 32 int main(void) 33 { 34 35 /* Configure unused GPIO as IPD to reduce power consumption */ 36 GPIO_InitTypeDef GPIO_InitStructure = {0}; 37 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB| 38 RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE, ENABLE); 39 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; 40 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 41 42 GPIO_Init(GPIOA, &GPIO_InitStructure); 43 GPIO_Init(GPIOB, &GPIO_InitStructure); 44 GPIO_Init(GPIOC, &GPIO_InitStructure); 45 GPIO_Init(GPIOD, &GPIO_InitStructure); 46 GPIO_Init(GPIOE, &GPIO_InitStructure); 47 /***************************************/ 48 49 Delay_Init(); 50 USART_Printf_Init(256000); 51 52 wakeup_init(); 53 54 printf("ch32v307 stop test\r\n"); 55 Delay_Ms(2000); 56 57 // NVIC->SCTLR |= (1<<1); /* set SLEEPONEXIT */ 58 59 /* set regulator in low power mode,need enable rcc of pwr */ 60 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); 61 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); 62 63 // PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI); 64 65 printf("wakeup\r\n"); 66 67 while(1) 68 { 69 printf("run in main loop\r\n"); 70 Delay_Ms(1000); 71 } 72 } 73 74 75 __attribute__((interrupt("WCH-Interrupt-fast"))) 76 void EXTI3_IRQHandler(void) 77 { 78 if(EXTI_GetITStatus(EXTI_Line3)!=RESET) 79 { 80 SystemInit(); 81 printf("exti3 interrupt\r\n"); 82 EXTI_ClearITPendingBit(EXTI_Line3); /* Clear Flag */ 83 } 84 }
:::tip 当MCU处于停止模式或待机模式时,两线调试仿真接口是处于关闭状态,此时是无法用 WCH-Link 进行程序的下载。若唤醒配置不正确,MCU无法唤醒,此时就会和MCU失联。解决方式如下:
-
在主函数启动时加一个较长的延时函数,按住板子的复位键,使系统处于复位状态,然后点击 IDE 下载按钮,这时再释放复位键,这样MCU上电后执行延时函数期间对低功耗程序进行擦除。
-
选择启动方式,通过将BOOT0拉高,重新上电使MCU从SRAM启动,重新选择一个非睡眠程序下载,下载完成后将BOOT0拉低,然后重新上电即可重新下载程序。
:::
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?