当前位置: 首页 > news >正文

FreeRTOS的低功耗Tickless模式

一、摘要

        在电池供电的物联网(IoT)、可穿戴设备等嵌入式应用中,功耗优化至关重要。FreeRTOS 作为流行的实时操作系统,其传统的周期性系统心跳(tick)中断虽然简化了任务调度,却成为深度休眠的障碍——它频繁唤醒CPU,导致功耗居高不下。Tickless 模式正是 FreeRTOS 为突破这一瓶颈而设计的核心低功耗技术。Tickless 模式的核心思想在于打破“心跳”的周期性束缚。它允许处理器在系统空闲时,预测下一个需要执行任务或处理事件的时间点,并据此动态配置一个更长的休眠时间。在此期间,系统心跳中断被完全禁止,处理器得以进入更深层次、功耗更低的睡眠状态(如WFIWFE或特定MCU的STOP/SLEEP模式)。只有当预测的唤醒时间到达,或更高优先级的外部中断发生时,处理器才会被唤醒。本文将基于STM32F103单片机来讲解如何使用FreeRTOS中的低功耗Tickless模式。

二、STM32F103的低功耗模式

        F1的低功耗模式分为三种:睡眠模式、停止模式及待机模式。进入睡眠模式有两种指令:WFI(全称为wait for interrupt--等待中断)和 WFE(全称为wait for event--等待事件),CMSIS(Cortex 微控制器软件接口标准)提供了两个函数来操作指令 WFI 和 WFE,我们可以 直接使用这两个函数:__WFI 和__WFE。FreeRTOS 系统会使用 WFI 指令进入休眠模式。

        如果使用 WFI 指令进入休眠模式的话那么任意一个中断都会将 MCU 从休眠模式中唤醒, 如果使用 WFE 指令进入休眠模式的话那么当有事件发生的话就会退出休眠模式,比如配置一个中断线为事件。当 STM32F103 处于休眠模式的时候 Cortex-M3 内核停止运行,但是其他外设运行正常, 比如 NVIC、SRAM 等。休眠模式的功耗比其他两个高,但是休眠模式没有唤醒延时,应用程 序可以立即运行。接下来本文后续将以STM32F103中三个低功耗模式中的睡眠模式作为讲解,因此其他两种模式可查阅--STM32中文参考手册V10第四章。

三、FreeRTOS中的Tickless模式降低功耗详解

        一般而言,在RTOS中为了降低功耗。运行空闲任务时需要进入低功耗模式,在处理应用层任务时需要将处理器从低功耗模式中唤醒。一般会在空闲任务的钩子函数中执行低功耗相关处理,比如设置处理器进入低功耗模式、关闭其他外设时钟、降低系统主频等。

        但FreeRTOS中的时钟是由滴答定时器(一般频率为1000Hz)来提供的,这会导致当处理器进入低功耗模式时会被滴答定时器中断频繁唤醒,导致低功耗模式作用大大降低。因此引入了Tickless模式--当处理器进入空闲任务周期 以后就关闭滴答定时器中断,只有当其他中断发生或者其他任务需要处理的时 候处理器才会被从低功耗模式中唤醒。但这将面临两个问题: 其一是关闭系统节拍中断会导致系统节拍计数器停止,系统时钟就会停止。 FreeRTOS 的系统时钟是依赖于系统节拍中断(滴答定时器中断)的,如果关闭了系统节拍中断的话就会导致系统时钟停止运行,这是绝对不允许的!解决方法为--记录下系统节拍中断的关闭时间,当系统节拍中断再次开启运行的时候补上这段时间,因此这段时间也称为补偿时间。这时候就需要另外一个定时器来记录这段补偿时间,而F103是继续采用滴答定时器来完成这个功能。

        其二是当处理器进入睡眠模式之后,除了及时响应中断外,还需要响应应用层的就绪任务,但是中断可以唤醒睡眠模式,应用层任务却不能主动将处理器从睡眠中唤醒。为了解决上述问题,采用记录处理器在进入低功耗模式之前的待运行任务阻塞时间(也称为低功耗持续时间),FreeRTOS中提供了prvGetExpectedIdleTime()函数来获取这段时间,接下来讲解具体实现。

三、FreeRTOS的Tickless模式的具体实现

        首先,要想使用 Tickless 模式,首先必须将 FreeRTOSConfig.h 中的宏 configUSE_TICKLESS_IDLE 设置为 1,代码如下:

在使能Tickless模式之后,当FreeRTOS运行到空闲任务时,会调用如下代码块去处理低功耗功能。

首先,第一行代码是在第三节提到的获取待运行任务阻塞时间函数,其关键源码如下:

其中,xTickCount是时钟节拍计数。

接下来判断运行时间是否大于宏configEXPECTED_IDLE_TIME_BEFORE_SLEEP,该宏默认为2个时钟节拍,代表是低功耗的最低持续时间,当然你也可以定义的更低,但是如果只有一个时钟节拍的低功耗持续时间,这没有意义。

接下来调用函数portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ),该函数用来完成具体的低功耗工作,关键源码如下所示,稍微有点长:

如上图所示的MaximumPossibleSuppressedTicks代表低功耗的最长持续时间,以时钟节拍为单位。其值是0xffffff/(72000000/1000)≈233(滴答定时器为24位向下计数寄存器)。最后一行代码是停止滴答定时器。

如上图,接下来需要根据待运行任务的阻塞时间去重新计算滴答定时器的重装值reload,这样在时间到达以后会触发滴答定时器中断,即可从睡眠模式中唤醒,达到应用层任务唤醒睡眠模式的目的。这个重装值位ulReloadValue,为当前的滴答定时器计数值加上待运行时间×一个时钟节拍的reload值(ulTimerCountsForOneTick=72M/1000,这是因为滴答定时器频率为1000Hz,则一次节拍的reload值为72M/1000)。最后关闭中断,为了延迟ISR的执行。

如上图,接下来判断当前是否存在就绪任务,如果存在,不可执行低功耗模式,需重新启动滴答定时器。

否则,可以执行低功耗模式,如上图:设置滴答定时器,然后调用configPRE_SLEEP_PROCESSING( xModifiableIdleTime );函数,这是一个宏,由用户自行编写。主要是进入低功耗模式前的准备工作,如关闭系统时钟等操作,可以写在任意一个.c文件下,比如笔者的定义低功耗准备函数如下图所示:

想要成功跑成功该函数,还需要在内核裁剪文件下进行配置,如下图:

继续回到if( xModifiableIdleTime > 0 )的位置继续讲解,如果低功耗持续时间大于0,则调用__wfi();函数去进入睡眠模式。

当发生中断时,需要去判断是滴答定时器中断唤醒还是其他中断唤醒,代码如下所示:

如果是滴答定时器唤醒的,可以看到补偿时间为任务待运行时间,如果是其他中断唤醒的,补偿时间为实际消耗时间。最后调用函数vTaskStepTick( ulCompleteTickPeriods );对FreeRTOS的时钟进行补偿,如下图:

其中,15处的关键源码为xTickCount += xTicksToJump;即给 FreeRTOS 的系统时钟节拍计数器 xTickCount 加上补偿时间。到此,这个处理具体的低功耗工作函数源码讲解完毕。

总结

        FreeRTOS Tickless模式通过动态暂停系统心跳(Tick)中断,允许处理器在空闲时段进入更深层次、功耗极低的睡眠状态,并精确预测下一个任务唤醒时间点来设置单次长定时唤醒,从而最大限度地减少了因周期性Tick中断导致的无谓唤醒和功耗浪费,显著降低了系统在空闲时的平均功耗。

最后,更多FreeRTOS干货欢迎加入嵌入式学习交流场地,里边分享项目/资料/面经等。

参考文献

        [1] FreeRTOS开发手册_V1.1.

http://www.xdnf.cn/news/1034821.html

相关文章:

  • RLHF调参实战手册:实用Trick、现象排查与解决思路(持续更新)
  • 动态BGP服务器的用途都有什么?
  • Softhub软件下载站实战开发(二):项目基础框架搭建
  • 萌系盲盒陷维权风暴,Dreams委托David律所已立案,速避雷
  • 历史数据分析——贵州茅台
  • LeetCode[106]从中序和后序遍历序列构造二叉树
  • Sngine 4.0.4海外社交平台PHP源码 – 多语言支持短视频和博客订阅(源码下载)
  • [学习] 多项滤波器在信号插值和抽取中的应用:原理、实现与仿真(完整仿真代码)
  • 使用Three.js创建炫酷的3D玻璃质感动态效果
  • 大小端的区别
  • MiniCPM4端侧AI模型
  • 机器学习算法_支持向量机
  • 图数据库(TuGraph)
  • DataX 框架学习笔记
  • GDI 区域检测与边框宽度的关系
  • 实习记录1
  • ImportError: DLL load failed while importing win32api: 找不到指定的模块
  • 18.vue.js的scoped样式隔离?原理和使用?(1)
  • 在线五子棋
  • 【Docker基础】Docker核心概念:命名空间(Namespace)与资源隔离联系
  • 从0开始学习R语言--Day23--稳健回归
  • 电路问题处理:SGMII链路中的AC耦合电容摆放位置
  • 轮廓 裂缝修复 轮廓修复 填补孔洞 源代码
  • 「Flink」Flink项目搭建方法介绍
  • 【飞牛os0.9.9系统使用docker 挂载cgroup2异常问题】
  • 傅里叶级数从三角函数形式到复指数形式的完整推导步骤
  • 位运,模拟,分治,BFS,栈和哈希表
  • Ant Design 版本演进详解:从 1.x 到 5.x 的发展历程
  • 【项目实训#09】智能代码文件助手模式前后端设计与实现
  • 读取配置文件到Settings对象的完整实现