天天看点

STM32低功耗和FreeRTOS低功耗模式ticklessFREERTOS中的低功耗:tickless模式

STM32的低功耗模式有3种:

  1. 睡眠模式:CM3内核停止,外设仍然运行。
  2. 停止模式:所有时钟都停止,SRAM 和寄存器内容保留。最低2uA左右的电流,一般不用。
  3. 待机模式:1.8V内核电源关闭,只有备份的寄存器和待机电路各维持供电。最低2uA左右的电流,一般不用。
模式 说明 进入语句 唤醒方式 对1.8V区域时钟影响 对VDD区域时钟影响 调压器
睡眠 内核及其外设停止工作,系统时钟(SysTick)等仍在工作 _WFI() 任一中断 内核时钟关,对其他时钟无影响
_WFE() 唤醒事件
停止 所有时钟都停止 PWR_EnterSTOPMode() 任一外部中断 关闭所有1.8V区域的时钟 HSI和HSE时钟关闭 开启或处于低功耗模式
待机 1.8V电源关闭 PWR_EnterSTANDBMode() WKUP引脚的上升沿、RTC闹钟事件、复位按键、IWDG复位

WFI和WFE区别:

1. WFI需要中断才能唤醒,WFE是不经过中断的。

2. WFE还会受控于一个1BIT Event Register,此寄存器软件不可访问。此功能时为多核而准备的。

    WFE在 Event Register 为0,或为1时,其功能是不一样的。WFE的E就是指这个事件寄存器。

           进一步描述:假设两个核,分别为core1和core2,

           A、core1可以执行SEV指令更改Event Register,同时它可以

                通过TXEV硬连接到core2的RXEV更改core2的Event Register。

          B、core1在执行WFE时,如果发现Event Register=1,它是不会进低功耗的,这里就是区别。

                基于以上描述WFE是多核低功耗用的,举个例子:当core1和core2抢同一个资源A时,

                core1占据A,那core2就WFE进低功耗,等待core1的SEV发过来。

       3. WFE因为可以等待事件,因此在某些不能WFI的场合可以采用WFE。比如唤醒后,

           系统需要做一些恢复操作才能响应中断,那采用WFE先唤醒系统,完成芯片所需的恢复操作。

FREERTOS中的低功耗:tickless模式

开启自带的低功耗模式(睡眠模式):将宏定义 configUSE_TICKLESS_IDLE 配置为 1 。

系统自动调用低功耗模式满足以下2个条件:

  1. 当前只有空闲任务正在运行,所有其他的任务处在挂起状态或者阻塞状态。
  2. 当系统运行于低功耗模式 的时钟节拍数大于宏定义 configEXPECTED_IDLE_TIME_BEFORE_SLEEP (系统默认是2个系统时钟节拍,用户定义必须大于2)。

函数 portSUPPRESS_TICKS_AND_SLEEP 是实现tickless的关键,此函数被空闲任务调用,在portmacro.h中。

在自带的tickless模式基础上实现停机模式,步骤如下:

在FreeRTOSConfig.h定义如下两个函数:

#define configPRE_SLEEP_PROCESSING(x)    OS_PreSleepProcessing(x)

#define configPOST_SLEEP_PROCESSING(x)    OS_PostSleepProcessing(x)
           

在port.c里面的函数vPortSuppressTicksAndSleep调用了上面两个函数。

void OS_PreSleepProcessing(uint32_t vParameter)
{
    (void)vParameter;

    /*用户可以考虑在此处加入关闭外设时钟来进一步降低功耗*/

    vParameter = 0;
    PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFE);
}

void OS_PostSleepProcessing(uint32_t vParameter)
{
    /*当上面关闭了外设,在这里恢复*/
    
    /*恢复外部时钟配置*/
    RCC_HSEConfig(RCC_HSE_ON);
    while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET){}

    RCC_PLLCmd(ENABLE);
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){}
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        
    while (RCC_GetSYSCLKSource() != 0x08){}

}
           

继续阅读