| 本帖最后由 Alden 于 2023-8-24 18:24 编辑 
 #技术资源# #申请原创#
 
 部分应用中,会有功耗要求,在功耗要求比较高的场景,要提高使用电池供电设备的待机时间。
 就需要关闭耗电高的器件、比如LCD屏幕、LED灯等,MCU也可以配置到低功耗状态来进一步降低。
 
 APM32F103系列低功耗模式有三种:睡眠模式、停止模式和待机模式。通过关闭内核、时钟源、设置调压器来降低功耗。
 每种低功耗模式的功耗、唤醒启动时间、唤醒方式、唤醒后数据的保存存在差异;功耗越低,唤醒时间越长,唤醒方式越少,唤醒后保存的数据越少,用户可以根据需求选择最合适的低功耗模式。下图是三种低功耗模式的差异
 
 
   
 
 APM32F103系列查看数据手册,可以看到各工作模式下的功耗差异。
 根据主频和外设使用情况的不同:
 Run mode:19.4~32.9mA
 Sleep mode:5.2~21.5mA
 Stop mode:20uA左右
 Standby mode:4uA左右
 可以看到各低功耗模式的功耗差异还是非常大的,特别的是standby模式,可以满足绝大部分低功耗需求。
 
 Standby模式功耗最低,但待机时内核停止工作,外设也停止工作,内核寄存器、内存的数据会丢失。唤醒后相当于程序复位从头开始执行。
 唤醒的方式可以通过WKUP 引脚的上升沿, RTC 闹钟、唤醒、入侵事件或 NRST 引脚外部复位及 IWDT
 
 一般常用的就是通过WKUP 引脚或RTC来唤醒MCU。接下来简单测试下这两种方式。
 WKUP唤醒的配置比较简单,只需要配置PMU_CSTS的WKUPCFG位即可。
 
   对应库函数为:    PMU_EnableWakeUpPin();
 然后在进入standby前清除唤醒标志位,避免标志位干扰即可:
 PMU_ClearStatusFlag(PMU_FLAG_WUE);
 PMU_EnterSTANDBYMode();
 
 
 而RTC的唤醒首先需要对RTC进行初始化。
 
 这里使用LSI作为时钟源,Standby唤醒虽然主程序会从头运行,但RTC的配置不受影响,所以根据PMU_CSTS的待机标志,可以不用重复配置RTC,节省初始化时间。void RTC_Init(void)
{
    if(PMU_ReadStatusFlag(PMU_FLAG_SB) == SET)
    {
        APM_MINI_LEDOn(LED3);
        PMU_ClearStatusFlag(PMU_FLAG_SB);
        RTC_WaitForSynchro();
    }
                
    else
    {
        BAKPR_Reset();
                          RCM_EnableLSI();
        while(RCM_ReadStatusFlag(RCM_FLAG_LSIRDY) == RESET);
        RCM_ConfigRTCCLK(RCM_RTCCLK_LSI);
        RCM_EnableRTCCLK();
        RTC_WaitForSynchro();
                          RTC_ConfigPrescaler(40000);
        RTC_WaitForLastTask();
    }
}
再配置SysTick中断和按键PA1的中断,分别做系统运行闪烁指示和按键进入Standby的判断。
 同时在EINT1_IRQHandler中进入standby前,进行RTC闹钟配置,让MCU进standby唤醒3秒后自动唤醒。
 
 对应main中进行初始化配置。void SysTick_Handler(void)
{
    APM_MINI_LEDToggle(LED2);
}
void EINT1_IRQHandler(void)
{
    if(EINT_ReadIntFlag(KEY1_BUTTON_EINT_LINE) != RESET)
    {
        EINT_ClearIntFlag(KEY1_BUTTON_EINT_LINE);
        APM_MINI_LEDOn(LED2);
        RTC_ClearStatusFlag(RTC_FLAG_SEC);
        while(RTC_ReadStatusFlag(RTC_FLAG_SEC) == RESET);
        RTC_ConfigAlarm(RTC_ReadCounter()+ 3);
        RTC_WaitForLastTask();
                                PMU_ClearStatusFlag(PMU_FLAG_WUE);
        PMU_EnterSTANDBYMode();
    }
}
 运行效果为:int main(void)
{
    RCM_EnableAPB1PeriphClock((RCM_APB1_PERIPH_T)(RCM_APB1_PERIPH_PMU | RCM_APB1_PERIPH_BAKR));
    APM_MINI_LEDInit(LED2);
    APM_MINI_LEDInit(LED3);
    APM_MINI_PBInit(BUTTON_KEY1, BUTTON_MODE_EINT);
    APM_MINI_LEDOn(LED2);
    APM_MINI_LEDOff(LED3);
    PMU_EnableWakeUpPin();
    PMU_EnableBackupAccess();
    RTC_Init();
    SysTick_Init();
    while(1)
    {
                          if(PMU_ReadStatusFlag(PMU_FLAG_WUE) == SET)
    {
        APM_MINI_LEDOn(LED3);
    }
                else
                {
                        APM_MINI_LEDOff(LED3);
                }
    }
}
上电LED2闪烁 ,表示MCU在运行状态。
 按下PA1的按键,LED2熄灭,MCU进入standby模式。
 3秒后MCU自动唤醒,或者给PA0一个上升沿信号也可以唤醒。
 唤醒后,LED2继续闪烁,LED3常亮,表示进入过了standby模式。
 
 而我main中多了一句:
 if(PMU_ReadStatusFlag(PMU_FLAG_WUE) == SET)
 {
 APM_MINI_LEDOn(LED3);
 }
 
 这是为了验证用户手册说的:
 
   实测也是运行状态下,如果PA0也就是WKUP引脚是高电平,这个标志位就会置位,并且由于标志位一直置位,如果保持PA0一直是高电平的状态进入standby,RTC闹钟无法唤醒MCU,需要PA0有个上升沿型号才行。
 所以,应用中使用WUKP需要保持空闲状态低电平,需要唤醒时拉高,避免标志位不对。
 补充下standby模式下的功耗,只有3uA,还是相当不错的。
 
   
 |