RTOS 低功耗设计原理及实现
RTOS 低功耗设计原理及实现
文章目录
- RTOS 低功耗设计原理及实现
- 👨🏫前言
- 👨🔬Tickless Idle Mode 的原理及实现
- 👨🚀Tickless Idle Mode 的软件设计原理
- 👨💻Tickless Idle Mode 的实现
- 👨⚖️结尾
👨🏫前言
目前, 越来越多的嵌入式产品在开发中使用 RTOS 作为软件平台, 同时,开发中对低功耗的要求也越来越高, 本文会讨论一下如何在 RTOS 中处理微控制器的低功耗特性。
应用中使用的 RTOS 一般采用基于时间片轮转的抢占式任务调度机制,一般的低功耗设计思路如下:
- 当 Idle 任务运行时,进入低功耗模式;
- 在适当的条件下,通过中断或者外部事件唤醒 MCU
但是, 从第二点可以看出,每次当 OS 系统定时器产生中断时,也会将 MCU 从低功耗模式中唤醒,而频繁的进入低功耗模式/从低功耗模式中唤醒会使得 MCU 无法进入深度睡眠,对低功耗设计而言也是不合理的。
在 FreeRTOS 中给出了一种低功耗设计模式 —— Tickless Idle Mode, 这个方法可以让 MCU 更长的时间处于低功耗模式。
👨🔬Tickless Idle Mode 的原理及实现
上图是任务调度示意图,横轴是时间轴, T1, T2, T3, T4 是 RTOS 的时间片基准,有四个任务分别是 TaskA,TaskB,TaskC,TaskD:
- Task A,周期性任务
- Task B, 周期性任务
- Task C,突发性任务
- Task D,周期性任务
从图中可以看出在四个任务进行调度之间,会有四次空闲期间(此时 RTOS 会调度 Idle 任务运行, 软件设计的目标应该是尽可能使 MCU 在 Idle 任务运行时处于低功耗模式)。
1️⃣Idle1
Idle 任务运行期间,会产生一次系统时钟滴答,此时会唤醒 MCU,唤醒后 MCU 又会进入低功耗模式, 这次唤醒是无意义的。期望使 MCU 在 Idle1 期间一直处于低功耗模式, 因此适当调整系统定时器中断使得 T1 时不触发系统时钟中断, 中断触发点设置为 Task B 到来时。
2️⃣Idle2
Task C 在系统滴答到达前唤醒 MCU(外部事件),MCU 可以在 Idle2 中可以一直处于低功耗模式;
3️⃣Idle3
与 Idle2 情况相同,但 Idle3 时间很短,如果这个时间很短,那么进入低功耗模式的意义并不大,因此在进入低功耗模式时软件应该添加策略;
4️⃣Idle4
与 Idle1 情况相同。
👨🚀Tickless Idle Mode 的软件设计原理
Tickless Idle Mode 的设计思想在于尽可能地在 MCU 空闲时使其进入低功耗模式。从上述情景中可以看出软件设计需要解决的问题有:
- 合理地进入低功耗模式(避免频繁使 MCU 在低功耗模式和运行模式下进行不必要的切换);RTOS 的系统时钟源于硬件的某个周期性定时器(Cortex-M 系列内核多数采用 SysTick),RTOS 的任务调度器可以预期到下一个周期性任务(或者定时器任务) 的触发时间,如上文所述,调整系统时钟定时器中断触发时间,可以避免 RTOS 进入不必要的时间中断,从而更长的时间停留在低功耗模式中,此时 RTOS 的时钟不再是周期的而是动态的(在原有的时钟基准时将不再产生中断,即 Tickless)。
- 当 MCU 被唤醒时,通过某种方式为系统时钟提供补偿。MCU 可能被两种情况所唤醒,动态调整过的系统时钟中断或者突发性的外部事件,无论是哪一种情况,都可以通过运行在低功耗模式下的某种定时器来计算出 MCU 处于低功耗模式下的时间,在 MCU 唤醒后对系统时间进行软件补偿;
- 软件实现时,要根据具体的应用情景和 MCU 低功耗特性来处理问题。尤其是 MCU 的低功耗特性,不同 MCU 处于不同的低功耗模式下所能使用的外设(主要是定时器) 是不同的, RTOS 的系统时钟可以进行适当的调整。
👨💻Tickless Idle Mode 的实现
这里以 STM32F407 系列的 MCU 为例, 首先需要明确的是 MCU 的低功耗模式, F407 有 3 种低功耗模式:Sleep,Stop, Standby, 在 RTOS 平台时, SRAM 和寄存器的数据不应丢失, 此外需要一个定时器为 RTOS 提供系统时钟, 这里选择 Sleep 模式下进行实现。
1. 使能
#define configUSE_TICKLESS_IDLE 1
2. 空闲任务(RTOS 空闲时自动调用)
/* Idle 任务 */
void prvIdleTask( void *pvParameters )
{for( ; ; ){...#if ( configUSE_TICKLESS_IDLE != 0 ){TickType_t xExpectedIdleTime;/* 用户策略以决定是否需要进入 Tickless Mode */xExpectedIdleTime = prvGetExpectedIdleTime();if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ){vTaskSuspendAll(); // 挂起调度器{configASSERT( xNextTaskUnblockTime >= xTickCount );xExpectedIdleTime = prvGetExpectedIdleTime();if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ){/* 用户函数接口 *//* 1. 进入低功耗模式和如何退出低功耗模式 *//* 2. 系统时间补偿 */portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );}} (void) xTaskResumeAll(); // 恢复调度器}}#endif /* configUSE_TICKLESS_IDLE */...}
}
3. 低功耗模式处理(根据 MCU 的低功耗模式编写代码)
void vPortSuppressTicksAndSleep( portTickType xExpectedIdleTime )
{unsigned long ulReloadValue, ulCompleteTickPeriods,ulCompletedSysTickDecrements;portTickType xModifiableIdleTime;/* 最长睡眠时间不可以超过定时器的最大定时值 *//* 通过调整定时器的时间基准可以获得更理想的最大定时值 */if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ){xExpectedIdleTime = xMaximumPossibleSuppressedTicks;}/* 停止 SysTick */portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |portNVIC_SYSTICK_INT_BIT;/* 计算唤醒时的系统时间,用于唤醒后的系统时间补偿 */ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );if( ulReloadValue > ulStoppedTimerCompensation ){ulReloadValue -= ulStoppedTimerCompensation;}__disable_interrupt();/* 确认下是否可以进入低功耗模式 */if( eTaskConfirmSleepModeStatus() == eAbortSleep ){/* 不可以,重新启动系统定时器 */portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |portNVIC_SYSTICK_INT_BIT |portNVIC_SYSTICK_ENABLE_BIT;portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;__enable_interrupt();}else{/* 可以进入低功耗模式 *//* 保存时间补偿,重启系统定时器 */portNVIC_SYSTICK_LOAD_REG = ulReloadValue;portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |portNVIC_SYSTICK_INT_BIT |portNVIC_SYSTICK_ENABLE_BIT;/* 进入低功耗模式,可以通过 configPRE_SLEEP_PROCESSING 函数进行低功耗模式下时钟及外设的配置*/xModifiableIdleTime = xExpectedIdleTime;configPRE_SLEEP_PROCESSING( xModifiableIdleTime );if( xModifiableIdleTime > 0 ){__DSB();__WFI();__ISB();}/* 退出低功耗模式 */configPOST_SLEEP_PROCESSING( xExpectedIdleTime );portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |portNVIC_SYSTICK_INT_BIT;__disable_interrupt()__enable_interrupt();/*唤醒有两种情况:系统定时器或者外部事件(中断) */if((portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT) != 0){/* 系统定时器唤醒,时间补偿 */unsigned long ulCalculatedLoadValue;ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) – ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ){ulCalculatedLoadValue = (ulTimerCountsForOneTick - 1UL);}portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;ulCompleteTickPeriods = xExpectedIdleTime - 1UL;}else{/* 外部事件(中断)唤醒 */ulCompletedSysTickDecrements = ( xExpectedIdleTime *ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;ulCompleteTickPeriods = ulCompletedSysTickDecrements /ulTimerCountsForOneTick;portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) *ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;}/* 重启 Systick,调整系统定时器中断为正常值 */portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;portENTER_CRITICAL();{portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |portNVIC_SYSTICK_INT_BIT |portNVIC_SYSTICK_ENABLE_BIT;vTaskStepTick( ulCompleteTickPeriods );portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;}portEXIT_CRITICAL();}
}
👨⚖️结尾
STM32 家族中拥有不同的系列,特别是专为低功耗应用设计的 L 系列,为其设计 RTOS 低功耗特性实现时可以有更多的实现方式(例,某种模式下内核停止运行, 此时可以使用外部定时器或者 RTC 来代替 Systick 作为系统定时器)。
相关文章:

RTOS 低功耗设计原理及实现
RTOS 低功耗设计原理及实现 文章目录 RTOS 低功耗设计原理及实现👨🏫前言👨🔬Tickless Idle Mode 的原理及实现👨🚀Tickless Idle Mode 的软件设计原理👨💻Tickless Idle Mo…...

PaddleOCR C++编译出错解决方案
文章目录 前言一、环境准备1、主要环境2、源码下载3、C推理库下载 二、报错信息1.静态库调用错误2.ld returned 1 exit status 总结 前言 最近,想尝试下PaddleOCR的C推理,但是过程不如人所愿,除了很多问题,这里捡重点的说下吧&…...
89、简述RabbitMQ的架构设计
简述RabbitMQ的架构设计 BrokerQueueExchangeRoutingKeyBinding信道架构设计图Broker RabbitMQ的服务节点 Queue 队列,是RabbitMQ的内部对象,用于存储消息。RabbitMQ中消息只能存储在队列中。生产者投递消息到队列,消费者从队列中获取消息并消费。多个消费者可以订阅同一…...
63 | 图像处理
文章目录 Python图像处理什么是图像处理?Python图像处理库安装Pillow库加载和显示图像调整图像大小裁剪图像调整图像亮度、对比度和色彩平衡应用滤镜练习题Python图像处理 什么是图像处理? 图像处理是指使用计算机算法来改变图像的外观或特征。它可以用于许多不同的应用程序…...

Stable Diffusion - 扩展 Roop 换脸 (Face Swapping) 插件的配置与使用
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/131856141 官网:GitHub - roop,参考论文:RobustSwap: A Simple yet Robust Face Swapping Model against Attr…...
opencv实现替换证件照颜色
程序可以实现蓝色底片变为红色底片(但有点小bug) 修改自:opencv:HSV颜色模型_opencv hsv_君浪的博客-CSDN博客 相关文章:OpenCV Mat数据类型指针ptr的使用_cv::mat ptr_AoboSir的博客-CSDN博客 【OpenCV】HSV颜色识…...

Elasticsearch【全文检索、倒排索引、应用场景、对比Solr、数据结构】(一)-全面详解(学习总结---从入门到深化)
目录 Elasticsearch介绍_全文检索 Elasticsearch介绍_倒排索引 Elasticsearch介绍_Elasticsearch的出现 Elasticsearch介绍_Elasticsearch应用场景 Elasticsearch介绍_Elasticsearch对比Solr Elasticsearch介绍_Elasticsearch数据结构 Elasticsearch介绍_全文检索 Elasti…...

了解 3DS MAX 3D摄像机跟踪设置:第 2 部分
推荐: NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 转到合成>新合成以创建新合成。 将“宽度”和“高度”值分别设置为 1280 和 720。将帧速率设置为 25,将持续时间设置为 12 秒。单…...
MySQL 判断 JSON 数组是否相等
文章目录 1.问题2.使用 JSON_CONTAINS 与 JSON_LENGTH参考文献 1.问题 JSON(JavaScript Object Notation)是流行的互联网应用服务之间的数据交换格式。 MySQL 从 5.7 版本开始支持 RFC 7159 定义的 JSON 规范,主要有 JSON 对象 和 JSON 数组…...

uni-app个人中心
一. 介绍uni-app: uni-app 是基于Vue.js框架开发的一个跨平台移动应用开发框架,可以同时支持多个平台(如iOS、Android、Web等)的应用开发。采用了统一的语法和组件规范,可以大大简化跨平台开发的工作,提高…...

只需3步,使用Stable Diffusion无限生产AI数字人视频
效果演示 先看效果,感兴趣的可以继续读下去。 没有找到可以上传视频的地方,大家打开这个网盘链接观看:https://www.aliyundrive.com/s/CRBm5NL3xAE 基本方法 搞一张照片,搞一段语音,合成照片和语音,同…...

Mysql执行计划字段解释
文章目录 一、前言二、如何查看执行计划三、执行计划各字段解释四、select_type4.1、SIMPLE(简单查询)4.1.1、简单的单表查询4.1.2、多表连接查询 4.2、PRIMARY(主查询)4.2.1、包含复杂子查询的外层查询4.2.2、UNION语句中的第一个…...

Linux -- 线程
文章目录 1. 线程概念1.1 概念1.2 理解(Linux OS角度)1.3 见一见 2. 线程优缺点3. 线程使用3.1 认识线程库3.2 使用3.2.1 线程创建3.2.2 线程等待3.2.3 线程退出3.2.4 线程取消3.2.5 获取线程id3.2.6 线程分离 3.3 理解线程库3.4 证明线程栈3.5 线程局部…...
Android:实时更新时间
心想着也就是更新精确到分钟,不用精确到秒,定时器就没有必要,系统是有广播Intent.ACTION_TIME_TICK可以直接用 动态注册广播 主方法里面调用一下 //要先设置一下当前时间,不然刷新时间会等到1分钟后再刷新 tv_HM.setText(getHM…...

24 鼠标常用事件
鼠标进入:enterEvent鼠标离开:leaveEvent鼠标按下:mousePressEvent鼠标释放:mouseRelaseEvent鼠标移动:mouseMoveEvent 提升为自定义控件MyLabel 代码: //mylabel.h #ifndef MYLABEL_H #define MYLABEL_H#…...

了解 3DS MAX 3D摄像机跟踪设置:第 4 部分
推荐: NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 转到合成>新合成以创建新合成。 将“宽度”和“高度”值分别设置为 1280 和 720。将帧速率设置为 25,将持续时间设置为 12 秒。单…...
nginx吞吐量调优
调整worker_processes和worker_connections: worker_processes:设置为服务器的CPU核心数或更高。例如,如果服务器有8个CPU核心,可以将worker_processes设置为8。worker_connections:设置每个worker进程所能处理的最大连…...
Python操作Excel文件,修改Excel样式(openpyxl)
秋风阁-北溪入江流 文章目录 安装依赖库openpyxlopenpyxl的操作加载文件,获取sheet加载文件load_workbook获取sheet 遍历单元格迭代遍历索引遍历 单元格行高和列宽的修改Excel列号与字母的转换Excel行高修改Excel列宽修改 Excel表格文字对齐属性设置修改单元格框线保…...
AutoSAR系列讲解(实践篇)7.6-实验:配置SWCRTE(下)
阅读建议: 实验篇是重点,有条件的同学最好跟着做一遍,然后回头对照着AutoSAR系列讲解(实践篇)7.5-OS原理进阶_ManGo CHEN的博客-CSDN博客理解其配置的目的和意义。本篇是接着AutoSAR系列讲解(实践篇)7.4-实验:配置SWC&RTE_ManGo CHEN的博客-CSDN博客的实验篇接着做…...

【node】使用express+gitee搭建图床,并解决防盗链问题
首先创建一个gitee的项目,详细步骤我就不一一说明 注解:大家记得将这个项目开源,还有记得获取自己的私钥,私钥操作如下: node依赖下载: "axios": "cors": "express"…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...