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不保持
复制代码
#include "debug.h"void wakeup_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    EXTI_InitTypeDef EXTI_InitStructure = {0};
​
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO , ENABLE);
​
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
​
    /* GPIOA.3 ----> EXTI_Line3 */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);
    EXTI_InitStructure.EXTI_Line = EXTI_Line3;
​
    /* WFI - EXTI_Mode_Interrupt, WFE - EXTI_Mode_Event*/
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; 
​
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
​
}
​
int main(void)
{
​
    /* Configure unused GPIO as IPD to reduce power consumption */
    GPIO_InitTypeDef GPIO_InitStructure = {0};   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|
             RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
​
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_Init(GPIOE, &GPIO_InitStructure);
    /***************************************/
​
​
    Delay_Init();
    USART_Printf_Init(256000);
​
    printf("ch32v307 standby test\r\n");
    Delay_Ms(2000);
​
    /* wakeup event, PA3 EXTI trigger falling */
    wakeup_init(); 
​
    /* set PWR register, need enable rcc of pwr */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); 
​
    /* WKUP or RTC wakeup event flag, clear by set PWR_CTLR CWUF */
    printf("PWR_CSR WUF = %ld \r\n",PWR->CSR & 0x1); 
    if(PWR->CSR & 0x1)
    {
        PWR->CTLR |= (1<<2); /* clear wakeup flag */
    }
​
    /* enable WKUP, PA0 */
    PWR_WakeUpPinCmd(ENABLE);
​
    /* standby mode, mcu will reset after wakeup event */
    PWR_EnterSTANDBYMode();
​
    printf("wakeup\r\n");
​
    while(1)
    {
        printf("run in main loop\r\n");
        Delay_Ms(1000);
    }
}
</details>


复制代码
参考代码 - SRAM保持,WKUP 上升沿唤醒
复制代码

​
#include "debug.h"
​
​
void ram_write_test(void)
{
    uint32_t myAddr=0x20000000+10*1024;
    uint32_t myAddr2=0x20000000+1*1024+512;
    uint32_t i=0;
    for(i=0;i<10;i++)//Write 30K RAM
    {
        *(uint32_t volatile *)(myAddr+(i<<2)) = 0x12345678;
    }
    for(i=0;i<10;i++)//Check 30K RAM
    {
        if((*(uint32_t volatile *)(myAddr+(i<<2))) != 0x12345678)
        {
            printf("Write 30K RAM Error!\r\n");
            break;
        }
    }
    printf("Write 30K RAM Successfully!\r\n");
    for(i=0;i<10;i++)//Write 2K RAM
    {
        *(uint32_t volatile *)(myAddr2+(i<<2)) = 0x12345678;
    }
    for(i=0;i<10;i++)//Check 2K RAM
    {
        if((*(uint32_t volatile *)(myAddr2+(i<<2))) != 0x12345678)
        {
            printf("Write 2K RAM Error!\r\n");
            break;
        }
    }
    printf("Write 2K RAM Successfully!\r\n");
​
}
​
​
void ram_read_test(void)
{
    uint32_t myAddr=0x20000000+10*1024;
    uint32_t myAddr2=0x20000000+1*1024+512;
    uint32_t i=0;
    printf("2K RAM:\r\n");
    for(i=0;i<10;i++)
    {
        printf("0x%08lx=0x%08lx\r\n",myAddr2+(i<<2),*(uint32_t volatile *)(myAddr2+(i<<2)));
    }
    printf("32K RAM:\r\n");
    for(i=0;i<10;i++)
    {
        printf("0x%08lx=0x%08lx\r\n",myAddr+(i<<2),*(uint32_t volatile *)(myAddr+(i<<2)));
    }
​
}
​
​
void wakeup_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    EXTI_InitTypeDef EXTI_InitStructure = {0};
​
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO , ENABLE);
​
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
​
    /* GPIOA.3 ----> EXTI_Line3 */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);
    EXTI_InitStructure.EXTI_Line = EXTI_Line3;
​
    /* WFI - EXTI_Mode_Interrupt, WFE - EXTI_Mode_Event*/
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; 
​
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
​
}
​
int main(void)
{
​
    /* Configure unused GPIO as IPD to reduce power consumption */
    GPIO_InitTypeDef GPIO_InitStructure = {0};   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|
             RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
​
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_Init(GPIOE, &GPIO_InitStructure);
    /***************************************/
​
​
    Delay_Init();
    USART_Printf_Init(256000);
​
    printf("ch32v307 standby test\r\n");
    Delay_Ms(2000);
​
    /* wakeup event, PA3 EXTI trigger falling */
    wakeup_init(); 
​
    ram_read_test();
    ram_write_test();
​
    /* set PWR register, need enable rcc of pwr */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); 
​
    /* WKUP or RTC wakeup event flag, clear by set PWR_CTLR CWUF */
    printf("PWR_CSR WUF = %ld \r\n",PWR->CSR & PWR_FLAG_WU); 
    if(PWR->CSR & PWR_FLAG_WU)
    {
        PWR->CTLR |= (1<<2); /* clear wakeup flag */
    }
​
    /* enable WKUP, PA0 */
    PWR_WakeUpPinCmd(ENABLE);
​
    /* standby mode, mcu will reset after wakeup event */
    PWR_EnterSTANDBYMode_RAM_LV();
    // PWR_EnterSTANDBYMode_RAM();
​
    printf("wakeup\r\n");
​
    while(1)
    {
        printf("run in main loop\r\n");
        Delay_Ms(1000);
    }
}
复制代码

 

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

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

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

:::

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