在系统或电源复位以后,微控制器处于运行状态。当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. 整理了一下angularJs的webpack模板

    github地址:https://github.com/qianxiaoning/demo-angularJs1.7.5 欢迎大家star或者fork呀~ 目录结构 src/ components/ ...

  2. P1096 4个数的全排列

    题目描述 输入4个有序的个位数.按照字典序输出它们的全排列. 输入格式 输入四个数字a,b,c,d.(0<=a,b,c,d<10) 输出格式 输出它们的全排列.每个排列占一行.而且每个排列 ...

  3. C# 使用反射获取私有属性的方法

    本文告诉大家多个不同的方法使用反射获得私有属性,最后通过测试性能发现所有的方法的性能都差不多 在开始之前先添加一个测试的类 public class Foo { private string F { ...

  4. mysql导出csv/sql/newTable/txt的方法,mysql的导入txt/sql方法...mysql备份恢复mysqlhotcopy、二进制日志binlog、直接备份文件、备份策略、灾难恢复.....................................................

    mysql备份表结构和数据 方法一. Create table new_table_nam备份到新表:MYSQL不支持: Select * Into new_table_name from old_t ...

  5. linux内核指针和错误值

    很多内部内核函数返回一个指针值给调用者. 许多这些函数也可能失败. 大部分情况, 失 败由返回一个 NULL 指针值来指示. 这个技术是能用的, 但是它不能通知问题的确切特性. 一些接口确实需要返回一 ...

  6. 2018-11-5-win10-uwp-异步转同步

    title author date CreateTime categories win10 uwp 异步转同步 lindexi 2018-11-05 10:18:40 +0800 2018-2-13 ...

  7. 2018-2-13-WPF-异常-NativeWPFDLLLoader.LoadNativeWPFDLL

    title author date CreateTime categories WPF 异常 NativeWPFDLLLoader.LoadNativeWPFDLL lindexi 2018-2-13 ...

  8. Nodejs模拟并发,尝试的两种解决方案

    一.准备数据库表 创建商品库存表 db_stock ,插入一条数据 DROP TABLE IF EXISTS `db_stock`; CREATE TABLE `db_stock` ( `id` ) ...

  9. spring-redis-session 自定义 key 和过期时间

    对于分布式应用来说,最开始遇到的问题就是 session 的存储了,解决方案大致有如下几种 使用 spring-session 它可以把 session 存储到你想存储的位置,如 redis,mysq ...

  10. 抽象类(abstract class)和接口(interface)有什么区别?

    抽象类中可以有构造器.抽象方法.具体方法.静态方法.各种成员变量,有抽象方法的类一定要被声明为抽象类,而抽象类不一定要有抽象方法,一个类只能继承一个抽象类. 接口中不能有构造器.只能有public修饰 ...