在系统或电源复位以后,微控制器处于运行状态。当CPU不需继续运行时,可以利用多种低功耗模式来节省功耗,例如等待某个外部事件时,用户需要根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。

STM32有三种低功耗模式:

1.睡眠模式(Cortex内核停止,所有外设包括Cortex核心的外设,如NVIC,系统嘀嗒定时器Systick等仍在运行)

2.停机模式(所有的时钟都停止)

3.待机模式(1.8V电源关闭)

这三种模式的对比如下

如何有效降低睡眠模式的功耗

设计低功耗主要从以下几个方面着手:

1.关闭可以关闭的外设时钟。

2.降低系统主频。WOM

3.注意I/O的状态,因为睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态。

如何有效降低停机模式的功耗

设计低功耗主要从以下几个方面着手

1.注意I/O的状态,因为在停机状态下,所有I/O都被保持在它们运行模式时的状态

2.注意I/O和外设IC的链接。

3.测试低功耗的时候,一定不要链接调试器,更不能边调试边侧电流。

*  关于低功耗的停机模式说明:
(1) 停止模式是在Corte的深睡眠模式基础上结合了外设的时钟控制机制,在停止模式下电压调节器可运行在正常或低功耗模式。此时在1.2V供电区域的的所有时钟都被停止,PLL、HSI和HSE的RC振荡器的功能被禁止,SRAM和寄存器内容被保留下来。
 (2) 在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。
 (3) 一定要关闭滴答定时器,实际测试发现滴答定时器中断也能唤醒停机模式。
(4) 当一个中断或唤醒事件导致退出停止模式时, HSI RC振荡器被选为系统时钟。
 (5) 退出低功耗的停机模式后,需要重新配置使用HSE。

我们知道一般简单应用中处理器大量的时间都在处理空闲任务,所以我们就可以考虑处理器处理空闲任务的时候进入低功耗模式,当需要处理空闲任务的时候就进入了低功耗模式,当需要处理应用层代码的时候就将处理器从低功耗模式中唤醒。但FreeRTOS的系统时钟是由嘀嗒定时器中断来提供的,系统时钟频率越高,嘀嗒定时器中断频率也就越高。如果嘀嗒定时器中断频率太高的话会导致大量的能量和时间消耗再进出睡眠模式中,这样导致的结果就是低功耗模式的作用被大大的消弱。

为此,FreeRTOS提供了tickless模式,即当处理器进入空闲任务后关闭嘀嗒定时器中断,只有当其他中断发生或其他任务需要处理的时候才会从低功耗模式中唤醒。为此,需要解决两个问题

1.关闭嘀嗒定时器会导致系统节拍停止,系统时钟就会停止

可以记录下系统节拍中断的关闭时间,当系统节拍中断再次开启运行的时候补上这段时间

2.如何保证下一个要运行的任务能被准确的唤醒

在进入低功耗模式之前获取还有多长时间进行下一个任务

Tickless具体实现过程

1.宏configUSE_TICKLESS_IDLE

#define configUSE_TICKLESS_IDLE     1    //启用低功耗模式

2.宏portSUPPRESS_TICKS_AND_SLEEP()

使能Tickless后,符合下面两种情况会调用宏portSUPPRESS_TICKS_AND_SLEEP()来处理低功耗的工作

1.空闲任务是唯一可运行的任务,因为其他所有任务都处于阻塞态或挂起态

2.系统处于低功耗模式的时间至少大于configEXPECTED_IDLE_TIME_BEFORE_SLEEP个时钟节拍,宏configEXPECTED_IDLE_TIME_BEFORE_SLEEP默认在文件FreeRTOS.h中定义为2,重新定义大于2。

这里FreeRTOS已经帮我们实现了STM32的portSUPPRESS_TICKS_AND_SLEEP()函数,宏portSUPPRESS_TICKS_AND_SLEEP在文件portmacro.h中如下定义

#ifndef portSUPPRESS_TICKS_AND_SLEEP
extern void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime);
#define portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime)
vPortSuppressTicksAndSleep(xExpectedIdleTime)
#endif

vPortSuppressTicksAndSleep()在port.c中实现如下

#if configUSE_TICKLESS_IDLE == 1

    __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{
uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL;
TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */
if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
{
xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
} /* Stop the SysTick momentarily. The time the SysTick is stopped for
is accounted for as best it can be, but using the tickless mode will
inevitably result in some tiny drift of the time maintained by the
kernel with respect to calendar time. */
portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; /* Calculate the reload value required to wait xExpectedIdleTime
tick periods. -1 is used because this code will execute part way
through one of the tick periods. */
ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
if( ulReloadValue > ulStoppedTimerCompensation )
{
ulReloadValue -= ulStoppedTimerCompensation;
} /* Enter a critical section but don't use the taskENTER_CRITICAL()
method as that will mask interrupts that should exit sleep mode. */
__disable_irq();
__dsb( portSY_FULL_READ_WRITE );
__isb( portSY_FULL_READ_WRITE ); /* If a context switch is pending or a task is waiting for the scheduler
to be unsuspended then abandon the low power entry. */
if( eTaskConfirmSleepModeStatus() == eAbortSleep )
{
/* Restart from whatever is left in the count register to complete
this tick period. */
portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; /* Restart SysTick. */
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; /* Reset the reload register to the value required for normal tick
periods. */
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Re-enable interrupts - see comments above __disable_irq() call
above. */
__enable_irq();
}
else
{
/* Set the new reload value. */
portNVIC_SYSTICK_LOAD_REG = ulReloadValue; /* Clear the SysTick count flag and set the count value back to
zero. */
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Restart SysTick. */
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can
set its parameter to 0 to indicate that its implementation contains
its own wait for interrupt or wait for event instruction, and so wfi
should not be executed again. However, the original expected idle
time variable must remain unmodified, so a copy is taken. */
xModifiableIdleTime = xExpectedIdleTime;
configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
if( xModifiableIdleTime > )
{
__dsb( portSY_FULL_READ_WRITE );
__wfi();
__isb( portSY_FULL_READ_WRITE );
}
configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Stop SysTick. Again, the time the SysTick is stopped for is
accounted for as best it can be, but using the tickless mode will
inevitably result in some tiny drift of the time maintained by the
kernel with respect to calendar time. */
ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG;
portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); /* Re-enable interrupts - see comments above __disable_irq() call
above. */
__enable_irq(); if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != )
{
uint32_t ulCalculatedLoadValue; /* The tick interrupt has already executed, and the SysTick
count reloaded with ulReloadValue. Reset the
portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick
period. */
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow
underflowed because the post sleep hook did something
that took too long. */
if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
{
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
} portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* The tick interrupt handler will already have pended the tick
processing in the kernel. As the pending tick will be
processed as soon as this function exits, the tick value
maintained by the tick is stepped forward by one less than the
time spent waiting. */
ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
}
else
{
/* Something other than the tick interrupt ended the sleep.
Work out how long the sleep lasted rounded to complete tick
periods (not the ulReload value which accounted for part
ticks). */
ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; /* How many complete tick periods passed while the processor
was waiting? */
ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; /* The reload value is set to whatever fraction of a single tick
period remains. */
portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
} /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG
again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
value. The critical section is used to ensure the tick interrupt
can only execute once in the case that the reload register is near
zero. */
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
portENTER_CRITICAL();
{
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
vTaskStepTick( ulCompleteTickPeriods );
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
}
portEXIT_CRITICAL();
}
} #endif /* #if configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ /*
* Setup the SysTick timer to generate the tick interrupts at the required
* frequency.
*/
#if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0 void vPortSetupTimerInterrupt( void )
{
/* Calculate the constants required to configure the tick interrupt. */
#if configUSE_TICKLESS_IDLE == 1
{
ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
}
#endif /* configUSE_TICKLESS_IDLE */ /* Configure SysTick to interrupt at the requested rate. */
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
} #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */
/*-----------------------------------------------------------*/

宏configPRE_SLEEP_PROCESSING()和configPOST_SLEEP_PROCESSING()

在真正的低功耗设计中不仅仅是将处理器设置到低功耗模式就行了,还需要做一些其他的处理,比如:

1.将处理器降低到合适的频率,因为频率越低功耗越小,甚至可以在进入低功耗以后关闭系统时钟。

2.修改时钟源,晶振的功耗肯定比处理器内部的时钟源高,进入低功耗模式以后可以切换到内部时钟源,比如STM32的内部RC振荡器

3.关闭其他外设时钟,比如IO口的时钟

4.关闭板子上其他功能模块电源,这个需要在产品硬件设计的时候就要处理好,比如可以通过MOS管来控制某个模快电源的开关,在处理器进入低功耗模式之前关闭这些模块的电源

/***************************************************************************************************************/
/* FreeRTOS与低功耗管理相关配置 */
/***************************************************************************************************************/
extern void PreSleepProcessing(uint32_t ulExpectedIdleTime);
extern void PostSleepProcessing(uint32_t ulExpectedIdleTime); #define configPRE_SLEEP_PROCESSING PreSleepProcessing //进入低功耗模式前要做的处理
#define configPOST_SLEEP_PROCESSING PostSleepProcessing //退出低功耗模式后要做的处理

宏conifgEXPECTED_IDLE_TIME_BEFORE_SLEEP

处理器工作咋在低功耗模式的时间虽说没有任何限制,但是时间太短也不行,所以需要做个限制,configEXPECTED_IDLE_TIME_BEFORE_SLEEP就是来完成这个功能的。

#ifndef configEXPECTED_IDLE_TIME_BEFORE_SLEEP
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
#endif #if configEXPECTED_IDLE_TIME_BEFORE_SLEEP < 2
#error configEXPECTED_IDLE_TIME_BEFORE_SLEEP must not be less than 2
#endif

此宏会在空闲任务函数prvIdleTask()中使用

FreeRTOS低功耗模式的更多相关文章

  1. FreeRTOS 低功耗之 tickless 模式

    以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 本身支持的低功耗模式 tickless 实现方法,tickless 低功 ...

  2. FreeRTOS 低功耗之停机模式

    以下转载自安富莱电子: http://forum.armfly.com/forum.php STM32F103 如何进入停机模式在 FreeRTOS 系统中,让 STM32 进入停机模式比较容易,调用 ...

  3. FreeRTOS 低功耗之睡眠模式

    以下转载自安富莱电子: http://forum.armfly.com/forum.php 低功耗是 MCU 的一项重要的指标,比如某些可穿戴的设备,其携带的电量有限,如果整个电路消耗的电量特别大的话 ...

  4. android wifi SWOL低功耗模式

    1 睡眠模式RX代码流程 ar_wal_rx_patch.c::patch_rx_process_recv_status//调用rx_ctxt->data_ind_handler -> d ...

  5. msp430f149的低功耗模式

    430的低功耗确实很强啊,虽然和VR单片机比起来速度慢了好多.在CPU进行工作时,如果没有什么事情干,就得进入低功耗模式啦,LMPX(0~4)这几种模式的具体事项就是如下的,得记住了. 一,运行模式M ...

  6. android蓝牙的调试(博通蓝牙工作 and 低功耗模式)

    首先结合项目从整体上去把握这部分: 蓝牙模块中一个比较核心的文件是bluetooth.c, 在我们上电的时候, 会调用这个文件中bt_enable()这个函数, 在这个函数里面先调用set_bluet ...

  7. 【Debug】串口发送数据时部分字节被拉长,出现帧错误,原因MCU进入低功耗模式导致串口时钟停了!

    串口发送数据时部分字节被拉长,出现帧错误,原因MCU进入低功耗模式导致串口时钟停了!

  8. MSP430 G2553 低功耗模式LPMx

    MSP430除了正常运行时的active模式外,还支持五种低功耗模式(Low-power mode),分别为LPM0.LPM1.LPM2.LPM3.LPM4,由状态寄存器中的CPUOFF.OSCOFF ...

  9. STM32的低功耗模式

    一 待机模式standby和STOP模式的区别: 进入低功耗模式:都一样,都是先关闭相应时钟,关闭相应外设,配置相应所有IO口(浮动输入),然后配置相应的唤醒中断源,中断影响的O口,然后调用相应函数进 ...

随机推荐

  1. 如何读取redis中的key值中的结果

    redis的值有5种类型,不同的类型有不同的命令来获取: 字符直接 get key 队列 左端弹出一个元素  LPOP key 哈希 HGET key field 集合 SMEMBERS key 返回 ...

  2. 【js】react-native Could not find iPhone 6 simulator 和 Entry, ":CFBundleIdentifier", Does Not Exist 两种报错解决办法

    一.在运行rn app应用时,react-native run:ios 报错出现   Could not find iPhone 6 simulator  解决办法: 1.react-native r ...

  3. P1027 三角形的周长

    题目描述 有n根棍子,棍子i的长度为Ai.现在想要从中选出3根棍子组成周长尽可能长的三角形.请输出最大周长,若无法组成三角形则输出0. 输入格式 第一行是一个正整数n(3<=n<=1000 ...

  4. linux Completions 机制

    内核编程的一个普通模式包括在当前线程之外初始化某个动作, 接着等待这个动作结束. 这个动作可能是创建一个新内核线程或者用户空间进程, 对一个存在着的进程的请求, 或 者一些基于硬件的动作. 在这些情况 ...

  5. 2019年7月20日针对iPhone7/7P有锁机的爆破限制

    背景 2019年7月20号苹果更改了激活策略,致使卡贴机一夜回到解放前,目前只能使用tmsi或者tmsi+iccid模式激活手机,但是缺点是移动联通信号真的不稳定,漏接电话,无法开启热点等等毛病.尤其 ...

  6. tf.truncates_normal()

    转载自:https://blog.csdn.net/uestc_c2_403/article/details/72235565 tf.truncated_normal(shape, mean, std ...

  7. 【51.64%】【POJ 1330】Nearest Common Ancestors

    Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 26416 Accepted: 13641 Description A roote ...

  8. <Standard Template Library>标准模板库专项复习总结(一)

    看了看博客园的申请时间也一年多了...想想自己一年多以来一直处于各种划水状态,现在又要面临ACM的冲击... 还是要抓紧时间赶紧复习一下了- -毕竟校园新生赛还是有奖金的.. 1.栈 先进后出(LIF ...

  9. Linux 创建网络会话

    Linux 创建网络会话 RHEL7系统支持网络会话功能,允许用户在多个配置文件中快速切换(非常类似于firewalld防火墙服务中的区域技术).如果我们在公司网络中使用笔记本电脑时需要手动指定网络的 ...

  10. UVW平台运动控制算法以及matlab仿真

    UVW平台运动控制算法以及matlab仿真   最近公司同事因为对某视觉对位平台的运动控制算法有疑问,所以来请教我.由于我也是第一次接触到UVW自动对位平台(也可以叫XXY自动对位平台),于是找了一些 ...