FreeRTOS 低功耗模式设计 STM32平台

1. STM32F105RBT6 的三种低功耗模式

1.1 sleep睡眠模式、stop停机模式、standby 待机模式

在这里插入图片描述

1.2 STM32中文参考手册有介绍STM32 低功耗模式的介绍

在这里插入图片描述

2. FreeRTOS 采用的是时间片轮转的抢占式任务调度机制,其低功耗设计思路一般是:

① 当运行空闲任务( IDLE任务)的时候就进入低功耗模式
② 在合适的时机,通过中断或者外部事件再唤醒MCU,退出低功耗模式
③ 对于STM32 系列单片机而言,systick 时间片如果设置的是1 ms,那么每隔1 ms 会将产生一个系统中断,可能会将MCU 从低功耗模式唤醒,如果MCU 频繁的进入、退出low power mode,MCU无法进入深度睡眠deep sleep,这是不合理的

3. FreeRTOS 给出了一种低功耗设计模式:Tickless Idle Mode

4. Tickless Idle Mode 的原理及实现

低功耗就是让单片机睡觉,只不过睡觉有很多种睡法,讲究人睡觉也讲究

4.1 情景分析

在这里插入图片描述
上图是任务调度示意图,横轴是时间轴,T1,T2,T3,T4是FreeRTOS 的时间片基准,FreeRTOS时间片一般是 1ms,产生一个systick 中断,有四个任务,分别是Task A、B、C、D
Task A、B、D 是周期性任务,例如三个LED灯,每隔100ms 亮一次
Task C 是突发性任务,例如,按键事件,按一下按键就产生一个外部事件
在两两任务之间有一个间隙,这个时候会运行空闲任务,Idle1,Idle2,Idle3,Idle4

4.1.1 Idle1 期间有一个systick 中断T1

运行完Task A后立马运行空闲任务,过了一会来了T1 中断,把MCU 又给唤醒了,然后又立马进入idle状态,过了一会后运行Task B,这个时候的T1 就是不合理的,就好像你在睡觉,然后我一脚把你踢醒,然后我跑了,你又接着睡,睡的很不爽

4.1.2 Idle2 期间可以一直睡觉,我不打你,你就可以睡的很爽了

4.1.3 Idle3 也可以一直睡觉,我也不打你,但是睡的时间太短了,就没必要睡了

干完task C,立马睡觉,然后又立马干task D,还没睡一会就又要起来干活(运行Task D),就像有点公司午睡只有半个小时一样,那还睡个毛,所以是否睡觉,什么时候睡觉,睡多久,睡多深,眯一会还是睡死,需要设计一套策略

4.1.4 和 Idle1 一样情况

5. Tickless Idle Mode 的软件设计原理

设计思想在于尽可能的让MCU 在运行空闲任务的时候进入低功耗模式,没事做就睡觉,节省粮食,少呼吸点空气吧你
① 合理的进入低功耗模式,避免频繁的在低功耗模式和运行模式之间进行切换,睡一下起来干活,睡一下起来干活很烦的,可以通过调整系统定时器中断触发时间长短来调整(systick中断),调一下systick 的值,然后测试一下功耗,调一下,测一下功耗。
Tickless:减少systick,原来会产生systick 的位置,现在不产生systick 中断,往后挪,增大时间片长度(或减小)
② 当MCU 被唤醒时,通过某种方式为系统时钟提供补偿,唤醒的两种方式:系统时钟中断,外部事件中断,可以通过运行在低功耗模式下的某种定时器来计算出MCU处于低功耗模式下的时间,在MCU唤醒后对系统时间进行软件补偿
③ 低功耗时间补偿策略,根据mcu低功耗特性和具体应用场景而定,可参考STM32 中文参考手册的低功耗模式章节

6. FreeRTOS 低功耗模式Tickless Mode 使能宏开关

置1 打开,置0 关闭

#define configUSE_TICKLESS_IDLE    1

在这里插入图片描述

7. FreeRTOS 创建空闲任务, 系统自动调度的,不需要人为调度

FreeRTOS 在启动任务调度的时候会创建一个空闲任务,没事干的时候就一直运行这个任务,摸鱼,老板来了就干活,老板一走就摸鱼任务
在这里插入图片描述

7.2 空闲任务宏定义 portTASK_FUNCTION

在这里插入图片描述

/** The Idle task.** The portTASK_FUNCTION() macro is used to allow port/compiler specific language extensions. The equivalent prototype for thisfunction is:** void prvIdleTask(void *pvParameters);**/
static portTASK_FUNCTION(prvIdleTask, pvParameters)
{(void)pvParameters;/*This is the FreeRTOS idle task, which is created automatically when the scheduler is started*/for(;;){/* See if any tasks have deleted themselves - if so then the idle task is responsible for freeing the deleted* task's TCB and stack*/prvCheckTasksWaitingTermination();#if (0 == configUSE_PREEMPTION)
{/* If we are not using preemption we keep forcing a task switch to see if any other task has become available. If we are using* preemption we don't need to do this as any task becoming available will automatically get the processor anyway*/taskYIELD();
}
#endif /* configUSE_PREEMPTION */#if ((1 == configUSE_PREEMPTION) && (1 == configIDLE_SHOULD_YIELD))
{/* When using preemption tasks of equal priority will be timesliced. If a task that is sharing the idle priority is ready* to run then the idle task should yield before the end of the timeslice.* A critical region is not required here as we are just reading from the list, and an occasional incorrect value will not* matter. If the ready list at the idle priority contains more than one task then a task other than the idle task is ready* to execute.*/if (listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[tskIDLE_PRIORITY])) > (UBaseType_t)1){taskYIELD();}else{mtCOVERAGE_TEST_MARKER();}
}
#endif /* ((configUSE_PREEMPTION == 1) && (configIDLE_SHOULD_YIELD == 1)) */#if (1 == configUSE_IDLE_HOOK)
{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 usedconfigASSERT(xNextTaskUnblockTime >= xTickCount);xExpectedIdleTime = prvGetExpectedIdleTime();if (xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP){traceLOW_POWER_IDLE_BEGIN();portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime);traceLOW_POWER_IDLE_END();}else{mtCOVERAGE_TEST_MARKER();}}(void) xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}
}
#endif /* configUSE_TICKLESS_IDLE */}
}

8. 低功耗模式处理 vPortSuppressTicksAndSleep,需要根据MCU的低功耗模式编写代码vPortSuppressTicksAndSleep

在这里插入图片描述

#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 willinevitably 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 waythrough 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 containsits own wait for interrupt or wait for event instruction, and so wfi should not be executed again.  However, the original expected idletime variable must remain unmodified, so a copy is taken. */xModifiableIdleTime = xExpectedIdleTime;configPRE_SLEEP_PROCESSING( xModifiableIdleTime );if( xModifiableIdleTime > 0 ){__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 willinevitably 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 ) != 0 ){uint32_t ulCalculatedLoadValue;/* The tick interrupt has already executed, and the SysTick count reloaded with ulReloadValue.  Reset theportNVIC_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 beprocessed as soon as this function exits, the tick value maintained by the tick is stepped forward by one less than thetime 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 tickperiods (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 standardvalue.  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 */

9. STM32Lxx 系列是专门为了RTOS 低功耗设计的

STM32L系列微控制器是意法半导体(STMicroelectronics)推出的一款超低功耗微控制器产品系列,旨在满足电池供电和节能应用的需求。该系列具有优异的功耗特性,并提供了多种低功耗模式和技术,以减少功耗并延长电池寿命。

STM32L系列的主要特点包括:

① 低功耗执行核心:采用ARM Cortex-M0+/M3/M4内核,具有高性能和低功耗的特点。
② 丰富的低功耗模式:包括低功耗运行模式、低功耗睡眠模式、低功耗停止模式等,可以根据需要选择不同的模式以达到最佳功耗效果。
③ 电源管理单元(PMU):提供了灵活的电源管理功能,包括自动电源切换、可选电源监测等。
④专用硬件加速器:部分型号提供了专用的硬件加速器,如AES加密、CRC校验等,可以提高性能并降低功耗。
⑤ 丰富的外设接口:包括UART、SPI、I2C、GPIO等常用外设接口,可满足不同应用的需求。
⑥ 多样化的封装选项:提供了不同的封装选项,以适应不同的应用场景和空间限制。

总之,STM32L系列是专门为低功耗要求而设计的微控制器产品系列,适用于电池供电和节能应用,比如便携设备、智能家居、传感器节点等。

10. 降低功耗的措施

为了节省功耗,可以将未使用的 GPIO 口设置为低功耗状态。以下是一些常见的方法:
① 输入模式:将未使用的 GPIO 口设置为输入模式,不使用推挽输出或开漏输出。这样可以减少功耗,因为输入模式下的电流消耗较低。
② 禁用上拉和下拉:如果未使用的 GPIO 口被设置为输入模式,确保禁用了上拉电阻和下拉电阻。禁用这些电阻可以降低静态电流消耗。
③ 关闭输入缓冲器:对于不需要读取外部信号的 GPIO 口,可以关闭输入缓冲器以减少功耗。这可以通过配置寄存器来实现,具体方法可能因芯片型号而有所不同。
④ 关闭输出驱动器:如果未使用的 GPIO 口被设置为输出模式,可以将输出驱动器关闭或调整为较低的驱动能力。这可以降低功耗并减少与其他电路的干扰。
⑤ 动态切换电源:如果可能,将未使用的 GPIO 口连接到外部电源开关或电源选择电路上。通过动态切换电源,可以完全断开或切换到更低功耗的电源,从而实现更高的功耗节省效果。
⑥ 将未使用的模块时钟关闭
⑦ 降低主频,需要综合考虑,主频降低,性能会下将,数据处理会变慢
⑧ 降低性能,这个需要综合考虑,需要在性能要求和功耗要求之间取得平衡
⑨ 选择不同的硬件,这个需要硬件攻城狮参与选型
每个芯片的 GPIO 管理方式和功耗优化方法可能会有所不同,查阅芯片制造商提供的技术参考手册和数据手册,以获得更详细和针对性的操作指南。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/22553.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UE4 C++】08-生成抛射物来模拟攻击效果

步骤 新建一个C类&#xff0c;父类为Actor&#xff0c;命名为“ASMagicProjectile” 在“ASMagicProjectile.h”中添加如下代码&#xff1a; 在“ASMagicProjectile.cpp”中添加如下代码&#xff1a; 编译后在虚幻编辑器中新建一个蓝图&#xff0c;选择父类为我们刚创建的C类…

Java设计模式之行为型-迭代器模式(UML类图+案例分析)

目录 一、基础概念 二、UML类图 三、角色设计 四、案例分析 五、总结 一、基础概念 迭代器模式是一种常用的设计模式&#xff0c;它主要用于遍历集合对象&#xff0c;提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不暴露该对象的内部表示。 举个简单的…

分布式微服务架构下网络通信的底层实现原理

在分布式架构中&#xff0c;网络通信是底层基础&#xff0c;没有网络&#xff0c;也就没有所谓的分布式架构。只有通过网络才能使得一大片机器互相协作&#xff0c;共同完成一件事情。 同样&#xff0c;在大规模的系统架构中&#xff0c;应用吞吐量上不去、网络存在通信延迟、…

Android Framework岗位面试真题分享

Handler是Android中的消息处理机制&#xff0c;是一种线程间通信的解决方案&#xff0c;同时你也可以理解为它天然的为我们在主线程创建一个队列&#xff0c;队列中的消息顺序就是我们设置的延迟的时间&#xff0c;如果你想在Android中实现一个队列的功能&#xff0c;不妨第一时…

【UE】运行游戏时就获取鼠标控制

问题描述 我们经常在点击运行游戏后运行再在视口界面点击一下才能让游戏获取鼠标控制。其实只需做一个设置就可以在游戏运行后自动获取鼠标控制。 解决步骤 点击编辑器偏好设置 如下图&#xff0c;点击“播放”&#xff0c;再勾选“游戏获取鼠标控制” 这样当你运行游戏后直…

shardingsphere mybatisplus properties和yml配置实现

shardingsphere mybatisplus properties和yml配置实现 目录结构 model package com.oujiong.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.util.Date;/*** user表*/ TableName("user") Data public class Use…

开发工具VSCODE的使用记录

vscode简介 Visual Studio Code&#xff08;简称“VS Code” [1] &#xff09;是Microsoft在2015年4月30日Build开发者大会上正式宣布一个运行于 Mac OS X、Windows和 Linux 之上的&#xff0c;针对于编写现代Web和云应用的跨平台源代码编辑器&#xff0c; [2] 可在桌面上运行…

python详解(8)——进阶(2):初步算法

目录 &#x1f3c6;一、前言 &#x1f3c6;二、时间复杂度 &#x1f3c6;三、递推 &#x1f6a9;1.简介 &#x1f6a9;2.爬楼梯 &#x1f6a9;3、猴子吃桃 &#x1f3c6;四、递归 &#x1f6a9;1、简介 &#x1f6a9;2、递归求斐波那契数列 &#x1f6a9;3、递归求阶乘 &#x…

“开放合作 共享未来”华秋联手伙伴共创硬件生态,助力物联网硬件加速创新

2023年7月11日&#xff0c;华秋携产品与方案亮相慕尼黑上海电子展&#xff08;electronica China&#xff09;&#xff0c;并与5家生态伙伴签署硬件生态共创战略协议&#xff0c;通过“硬件软件供应链”的合作模式&#xff0c;发挥各自行业优势&#xff0c;共同推动电子产业的创…

springboot时间管理系统

通过前面的功能分析可以将时间管理系统的功能分为管理员&#xff0c;用户两个部门&#xff0c;系统的主要功能包括首页&#xff0c;个人中心&#xff0c;系统公告管理&#xff0c;用户管理&#xff0c;时间分类管理&#xff0c;事件数据管理&#xff0c;目标数据管理&#xff0…

k8s 持久化存储

我们继续来查看 k8s 的卷&#xff0c;上一次我们分享了将磁盘挂载到容器中&#xff0c;empyDir 和 gitRepo 都是会随着 pod 的启动而创建&#xff0c;随着 pod 的删除而销毁 那么我们或许会有这样的需求&#xff0c;期望在 pod 上面读取节点的文件或者使用节点的文件系统来访问…

uniapp下上传图片后图片裁剪加图片旋转,支持H5和app

效果图 代码如下 <template><view class"container" v-show"isShow"><view><view class"cropper-content"><view v-if"isShowImg" class"uni-corpper":style"width: cropperInitW px;he…