第十八章 低功耗Tickless模式

睡眠模式:__WFI 中断唤醒 __WFE 事件唤醒 CPU CLK关闭
停止模式:RAM保持 中断唤醒
当 STM32F103 处于休眠模式的时候 Cortex-M3 内核停止运行,但是其他外设运行正常,
比如 NVIC、SRAM 等。
休眠模式的功耗比其他两个高,但是休眠模式没有唤醒延时,应用程
序可以立即运行。
停止模式基于 Cortex-M3 的深度休眠模式与外设时钟门控,在此模式下 1.2V 域的所有时钟
都会停止,PLL、HSI 和 HSE RC 振荡器会被禁止,但是内部 SRAM 的数据会被保留。调压器
可以工作在正常模式,也可配置为低功耗模式
相比于前面两种低功耗模式,待机模式的功耗最低。待机模式是基于 Cortex-M3 的深度睡
眠模式的
其中调压器被禁止。1.2V 域断电,PLL、HSI 振荡器和 HSE 振荡器也被关闭。除了
备份区域和待机电路相关的寄存器外,SRAM 和其他寄存器的内容都将丢失
退出待机模式的话会导致 STM32F1 重启
处理器处理空闲任务的时候就进入低功耗模式
FreeRTOS 就是通过在处理器处
理空闲任务的时候将处理器设置为低功耗模式来降低能耗
一般会在空闲任务的钩子函数中执行低功耗相关处理,
比如设置处理器进入低功耗模式、关闭其他外设时钟、降低系统主频等等
FreeRTOS 的系统时钟是由滴答定时器中断来提供的
Tickless 模式,当处理器进入空闲任务周期
以后就关闭系统节拍中断(滴答定时器中断),
只有当其他中断发生或者其他任务需要处理的时候处理器才会被从低功耗模式中唤醒
关闭了系统节拍中断的话就会导致系统时钟停止运行,这是绝对不允许的
该如何解决这个问题呢?
我们可以记录下系统节拍中断的关闭时间,
当系统节拍中断再次开启运行的时候补上这段时间就行
如果处理器在进入低功耗模式之前能够获取到还有多长时间运行下一个任务那么问题就迎刃而解了
我们只需要开一个定时器,定时器的定时周期设置为这个时间值就行了
定时时间到了以后产生定时中断,处理器不就从低功耗模式唤醒了
#define configUSE_TICKLESS_IDLE (1)
portSUPPRESS_TICKS_AND_SLEEP()
空闲任务是唯一可运行的任务,因为其他所有的任务都处于阻塞态或者挂起态
系统处于低功耗模式的时间至少大于 configEXPECTED_IDLE_TIME_BEFORE_SLEEP
个时钟节拍,
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
我们可以在 FreeRTOSConfig.h 中重新定义,此宏必须大于 2
portSUPPRESS_TICKS_AND_SLEEP()有个参数,此参数用来指定还有多长时间将有任务
进入就绪态,其实就是处理器进入低功耗模式的时长(单位为时钟节拍数)
滴答定时器的计数寄存器是 24 位
__disable_irq(); //关闭全部中断
__enable_irq();//开启全部中断
在真正的低功耗设计中不仅仅是将处理器设置到低功耗模式就行了,还需要做一些其他的
处理,比如:
● 将处理器降低到合适的频率,因为频率越低功耗越小,甚至可以在进入低功耗模式以后
关闭系统时钟。
● 修改时钟源,晶振的功耗肯定比处理器内部的时钟源高,进入低功耗模式以后可以切换
到内部时钟源,比如 STM32 的内部 RC 振荡器。
● 关闭其他外设时钟,比如 IO 口的时钟。
● 关闭板子上其他功能模块电源,这个需要在产品硬件设计的时候就要处理好,比如可以
通过 MOS 管来控制某个模块电源的开关,在处理器进入低功耗模式之前关闭这些模块的电源
宏 configPRE_SLEEP_PROCESSING ()和 configPOST_SLEEP_PROCESSING()
进入低功耗硬件操作。退出低功耗硬件操作
默认情况下 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 为 2 个时钟节拍,并且最小
不能小于 2 个时钟节拍
进入低功耗的时最小时钟节拍数,设置为10,当进入低功耗时间大于10个系统时钟才允许进入
#if ( configUSE_IDLE_HOOK == 1 )
{
extern void vApplicationIdleHook( void );
/* Call the user defined function from within the idle task. This
allows the application designer to add background functionality
without the overhead of a separate task.
NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
CALL A FUNCTION THAT MIGHT BLOCK. */
vApplicationIdleHook();
}
#endif /* configUSE_IDLE_HOOK */
/* This conditional compilation should use inequality to 0, not equality
to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when
user defined low power mode implementations require
configUSE_TICKLESS_IDLE to be set to a value other than 1. */
#if ( configUSE_TICKLESS_IDLE != 0 )
{
TickType_t xExpectedIdleTime;
/* It is not desirable to suspend then resume the scheduler on
each iteration of the idle task. Therefore, a preliminary
test of the expected idle time is performed without the
scheduler suspended. The result here is not necessarily
valid. */
xExpectedIdleTime = prvGetExpectedIdleTime();
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
vTaskSuspendAll();
{
/* Now the scheduler is suspended, the expected idle
time can be sampled again, and this time its value can
be used. */
configASSERT( xNextTaskUnblockTime >= xTickCount );
xExpectedIdleTime = prvGetExpectedIdleTime();
if( xEx

最低0.47元/天 解锁文章
673

被折叠的 条评论
为什么被折叠?



