FREERTOS空闲任务和低功耗

空闲任务

空闲任务是 FreeRTOS 必不可少的一个任务,其他 RTOS 类系统也有空闲任务,比如uC/OS。看名字就知道,空闲任务是处理器空闲的时候去运行的一个任务,当系统中没有其他就绪任务的时候空闲任务就会开始运行,空闲任务最重要的作用就是让处理器在无事可做的时候找点事做,防止处理器无聊,因此,空闲任务的优先级肯定是最低的。当然了,实际上肯定不会这么浪费宝贵的处理器资源,FreeRTOS 空闲任务中也会执行一些其他的处理。

空闲任务简介

FreeRTOS 的调度器启动以后就会自动的创建一个空闲任务,这样就可以确保至少有一个任务可以运行。但是这个空闲任务使用最低优先级,如果应用中有其他高优先级任务处于就绪态的话这个空闲任务就不会跟高优先级的任务抢占 CPU 资源。空闲任务还有另外一个重要的职责,如果某个任务要调用函数 vTaskDelete()删除自身,那么这个任务的任务控制块 TCB 和任务堆栈等这些由 FreeRTOS 系统自动分配的内存需要在空闲任务中释放掉,如果删除的是别的任务那么相应的内存就会被直接释放掉,不需要在空闲任务中释放。因此,一定要给空闲任务执行的机会!除此以外空闲任务就没有什么特别重要的功能了,所以可以根据实际情况减少空闲任务使用 CPU 的时间(比如,当 CPU 运行空闲任务的时候使处理器进入低功耗模式)

用户可以创建与空闲任务优先级相同的应用任务,当宏 configIDLE_SHOULD_YIELD 1 的话应用任务就可以使用空闲任务的时间片,也就是说空闲任务会让出时间片给同优先级的应用任务。这种机制要求FreeRTOS 使用抢占式内核。

空闲任务的创建

当调用函数 vTaskStartScheduler()启动任务调度器的时候此函数就会自动创建空闲任务。

使用动态方法创建空闲任务,空闲任务的任务函数为 prvIdleTask(),任务堆栈大小为configMINIMAL_STACK_SIZE,任务堆栈大小可以在 FreeRTOSConfig.h 中修改。任务优先级为tskIDLE_PRIORITY,宏 tskIDLE_PRIORITY 0,说明空闲任务优先级最低,用户不能随意修改空闲任务的优先级!

空闲任务函数

空闲任务的任务函数为 prvIdleTask(),但是实际上是找不到这个函数的,因为它是通过宏定义来实现的,在文件 portmacro.h 中有如下宏定义:

#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void*pvParameters )

其中 portTASK_FUNCTION()在文件 tasks.c 中有定义,它就是空闲任务的任务函数。

将第一行将展开就是 static void prvIdleTask(void *pvParameters),创建空闲任务的时候任务函数名就是 prvIdleTask()

内部大致流程如下:

(1)调用函数 prvCheckTasksWaitingTermination()检查是否有需要释放内存的被删除任务,当有任务调用函数 vTaskDelete()删除自身的话,此任务就会添加到列表 xTasksWaitingTerminatio中。函数prvCheckTasksWaitingTermination()会检查列表 xTasksWaitingTermination 是否为空,如果不为空的话就依次将列表中所有任务对应的内存释放掉(任务控制块 TCB 和任务堆栈的内存)

(2)使用抢占式内核并且 configIDLE_SHOULD_YIELD 1,说明空闲任务需要让出时间片给同优先级的其他就绪任务。

(3)检查优先级为 tskIDLE_PRIORITY(空闲任务优先级)的就绪任务列表是否为空,如果不为空的话就调用函数 taskYIELD()进行一次任务切换。

(4)如果使能了空闲任务钩子函数的话就执行这个钩子函数,空闲任务钩子函数的函数名为vApplicationIdleHook(),这个函数需要用户自行编写!在编写这个这个钩子函数的时候一定不能调用任何可以阻塞空闲任务的 API 函数。

(5)configUSE_TICKLESS_IDLE 不为 0,说明使能了 FreeRTOS 的低功耗 Tickless 模式。

(6)调用函数 prvGetExpectedIdleTime()获取处理器进入低功耗模式的时长,此值保存在变量xExpectedIdleTime 中,单位为时钟节拍数。

(7)xExpectedIdleTime 值要大于 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 才有效。

(8)处理 Tickless 模式,挂起任务调度器,其实就是起到临界段代码保护功能。

(9)重新获取一次时间值,这次的时间值是直接用于portSUPPRESS_TICKS_AND_SLEEP()的。

(10)调用 portSUPPRESS_TICKS_AND_SLEEP()进入低功耗 Tickless 模式。

(11)恢复任务调度器。

空闲任务钩子函数

FreeRTOS 中有多个钩子函数,钩子函数类似回调函数,当某个功能(函数)执行的时候就会调用钩子函数,至于钩子函数的具体内容那就由用户来编写。如果不需要使用钩子函数的话就什么也不用管,钩子函数是一个可选功能,可以通过宏定义来选择使用哪个钩子函数,可选的钩子函数如下表所示:

钩子函数的使用方法基本相同,用户使能相应的钩子函数,然后自行根据实际需求编写钩子函数的内容。

空闲任务钩子函数

在每个空闲任务运行周期都会调用空闲任务钩子函数,如果想在空闲任务优先级下处理某个任务有两种选择:

● 在空闲任务钩子函数中处理任务。

不管什么时候都要保证系统中至少有一个任务可以运行,因此绝对不能在空闲任务钩子函数中调用任何可以阻塞空闲任务的 API 函数,比如 vTaskDelay(),或者其他带有阻塞时间的信号量或队列操作函数。

● 创建一个与空闲任务优先级相同的任务。

创建一个任务是最好的解决方法,但是这种方法会消耗更多的 RAM

要使用空闲任务钩子函数首先要在 FreeRTOSConfig.h 中将宏 configUSE_IDLE_HOOK 改为 1,然后编写空闲任务钩子函数 vApplicationIdleHook()。通常在空闲任务钩子函数中将处理器设置为低功耗模式来节省电能,为了与 FreeRTOS 自带的 Tickless 模式做区分,这里我暂且将这种低功耗的实现方法称之为通用低功耗模式(因为几乎所有的 RTOS 系统都可以使用这种方法实现低功耗)

相对与通用低功耗模式,FreeRTOS 自带的 Tickless 模式更加合理有效,所以如果有低功耗设计需求的话大家尽量使用 FreeRTOS 再带的 Tickless 模式。当然了,如果对于功耗要求不严格的话通用低功耗模式也可以使用。

低功耗

很多应用场合对于空耗的要求很严格,比如长期无人照看的数据采集仪器,可穿戴设备等。

其实很多 MCU 都有相应的低功耗模式,以此来降低设备运行时的功耗,进行裸机开发的时候就可以使用这些低功耗模式。但是现在我们要使用操作系统,因此操作系统对于低功耗的支持也显得尤为重要,这样硬件与软件相结合,可以进一步降低系统的功耗。这样开发也会方便很多,毕竟系统已经原生支持低功耗了,我们只需要按照系统的要求来做编写相应的应用层代码即可。FreeRTOS 提供了一个叫做 Tickless 的低功耗模式,本章我们就来学习一下如何使用这个Tickless 模式。

STM32 本身就支持低功耗模式,共有三种低功耗模式:

● 睡眠(Sleep)模式。

● 停止(Stop)模式。

● 待机(Standby)模式。

参考:STM32实战总结:HAL之低功耗_stm32睡眠模式 hal-CSDN博客

具体内容自行查阅手册和相关资料,此处不赘述。

如何降低功耗?

之前讲解获取任务运行时间信息的时候可以看出,一般的简单应用中处理器大量的时间都在处理空闲任务,所以我们就可以考虑当处理器处理空闲任务的时候就进入低功耗模式,当需要处理应用层代码的时候就将处理器从低功耗模式唤醒。FreeRTOS 就是通过在处理器处 理空闲任务的时候将处理器设置为低功耗模式来降低能耗。一般会在空闲任务的钩子函数中执行低功耗相关处理,比如设置处理器进入低功耗模式、关闭其他外设时钟、降低系统主频等等。本节后面均以 STM32F103 三个低功耗模式中的睡眠模式为例讲解。

我们知道 FreeRTOS 的系统时钟是由滴答定时器中断来提供的,系统时钟频率越高,那么滴答定时器中断频率也就越高。18.1 小节讲过,中断是可以将 STM32F03 从睡眠模式中唤醒,周期性的滴答定时器中断就会导致 STM32F4103 周期性的进入和退出睡眠模式。因此,如果滴答定时器中断频率太高的话会导致大量的能量和时间消耗在进出睡眠模式中,这样导致的结果就是低功耗模式的作用被大大的削弱。

为此,FreeRTOS 特地提供了一个解决方法——Tickless 模式,当处理器进入空闲任务周期以后就关闭系统节拍中断(滴答定时器中断),只有当其他中断发生或者其他任务需要处理的时候处理器才会被从低功耗模式中唤醒。为此我们将面临两个问题:

问题一:关闭系统节拍中断会导致系统节拍计数器停止,系统时钟就会停止。

FreeRTOS 的系统时钟是依赖于系统节拍中断(滴答定时器中断)的,如果关闭了系统节拍中断的话就会导致系统时钟停止运行,这是绝对不允许的!该如何解决这个问题呢?我们可以记录下系统节拍中断的关闭时间,当系统节拍中断再次开启运行的时候补上这段时间就行了。这时候我们就需要另外一个定时器来记录这段该补上的时间,如果使用专用的低功耗处理器的话基本上都会有一个低功耗定时器,比如 STM32L4 系列(L 系列是 ST 的低功耗处理器)就有一个叫做 LPTIM(低功耗定时器)的定时器。STM32F103 没有这种定时器那么就接着使用滴答定时器来完成这个功能,具体实现方法后面会讲解。

问题二:如何保证下一个要运行的任务能被准确的唤醒?

即使处理器进入了低功耗模式,但是我的中断和应用层任务也要保证及时的响应和处理。中断自然不用说,本身就可以将处理器从低功耗模式中唤醒。但是应用层任务就不行了,它无法将处理器从低功耗模式唤醒,无法唤醒就无法运行!这个问题看来很棘手,既然应用层任务无法将处理器从低功耗模式唤醒,那么我们就借助其他的力量来完成这个功能。如果处理器在进入低功耗模式之前能够获取到还有多长时间运行下一个任务那么问题就迎刃而解了,我们只需要开一个定时器,定时器的定时周期设置为这个时间值就行了,定时时间到了以后产生定时中断,处理器不就从低功耗模式唤醒了。这里似乎又引出了一个新的问题,那就是如何知道还有多长时间执行下一个任务?这个时间也就是低功耗模式的执行时间,值得庆辛的是 FreeRTOS已经帮我们完成了这个工作。

Tickless 具体实现

1

宏 configUSE_TICKLESS_IDLE

要想使用 Tickless 模式,首先必须将 FreeRTOSConfig.h 中的宏

configUSE_TICKLESS_IDLE 设置为 1,代码如下:

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

2

宏 portSUPPRESS_TICKS_AND_SLEEP()

使能Tickless 模式以后当下面两种情况都出现的时候 FreeRTOS 内核就会调用宏 portSUPPRESS_TICKS_AND_SLEEP()来处理低功耗相关的工作。

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

● 系统处于低功耗模式的时间至少大于configEXPECTED_IDLE_TIME_BEFORE_SLEEP个时钟节拍,宏 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 默认在文件 FreeRTOS.h 中定义为 2,我们可以在 FreeRTOSConfig.h 中重新定义,此宏必须大于 2!

portSUPPRESS_TICKS_AND_SLEEP()有个参数,此参数用来指定还有多长时间将有任务进入就绪态,其实就是处理器进入低功耗模式的时长(单位为时钟节拍数),因为一旦有其他任务进入就绪态处理器就必须退出低功耗模式去处理这个任务。

portSUPPRESS_TICKS_AND_SLEEP()应该是由用户根据自己所选择的平台来编写的,此宏会被空闲任务调用来完成具体的低功耗工作。但是!如果使用 STM32 的话编写这个宏的工作就不用我们来完成了,因为 FreeRTOS 已经帮我们做好了,有没有瞬间觉得好幸福啊。当然了你也可以自己去重新编写,不使用 FreeRTOS 提供的,如果自己编写的话需要先将 上一步FreeRTOSConfig.h文件中的configUSE_TICKLESS_IDLE 设置为2。

具体处理过程可参考相关视频和资料,此处不赘述。

3

宏 configPRE_SLEEP_PROCESSING ()和 configPOST_SLEEP_PROCESSING()

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

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

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

● 关闭其他外设时钟,比如 IO 口的时钟。

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

有关产品低功耗设计的方法还有很多,大家可以上网查找一下,上面列举出的这几点在处理器进入低功耗模式之前就要完成处理。

FreeRTOS 为我们提供了一个宏来完成这些操作,它就是 configPRE_SLEEP_PROCESSING(),这个宏的具体实现内容需要用户去编写。如果在进入低功耗模式之前我们降低了处理器频率、关闭了某些外设时钟等的话,那在退出低功耗模式以后就需要恢复处理器频率、重新打开外 设时钟等,这个操作在宏configPOST_SLEEP_PROCESSING()中完成,同样的这个宏的具体内容也需要用户去编写。这两个宏会被函数 vPortSuppressTicksAndSleep()调用,我们可以在 FreeRTOSConfig.h 定义这两个宏,如下:

函数 PreSleepProcessing()PostSleepProcessing()可以在任意一个 C 文件中编写。

4

宏 configEXPECTED_IDLE_TIME_BEFORE_SLEEP

处理器工作在低功耗模式的时间虽说没有任何限制,1 个时钟节拍也行,滴答定时器所能计时的最大值也行。但是时间太短的话意义也不大啊,就 1 个时钟节拍,我这刚进去就得出来!

所以我们必须对工作在低功耗模式的时间做个限制,不能太短了,宏 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 就是用来完成这个功能的。此宏默认在文件 FreeRTOS 中有定义,如下:

默认情况下 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 为 2 个时钟节拍,并且最小不能小于 2 个时钟节拍。如果要修改这个值的话可以在文件 FreeRTOSConfig.h 中对其重新定义。

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

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

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

相关文章

slab分配器

什么是slab分配器? 用户态程序可以使用malloc及其在C标准库中的相关函数申请内存;内核也需要经常分配内存,但无法使用标准库函数;linux内核中,伙伴分配器是一种页分配器,是以页为单位的,但这个…

基于 Echarts + Python Flask ,我搭建了一个动态实时大屏监管系统

一、效果展示 1. 动态实时更新数据效果图 2. 鼠标右键切换主题 二、确定需求方案 支持Windows、Linux、Mac等各种主流操作系统;支持主流浏览器Chrome,Microsoft Edge,360等;服务器采用python语言编写,配置好python环…

计算机设计大赛 题目: 基于深度学习的疲劳驾驶检测 深度学习

文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 🔥 优…

Orange3数据预处理(公式组件)

公式 为您的数据集添加新特征。 输入 数据:输入数据集 输出 数据:带有额外特征的数据集 公式组件允许通过使用用户定义的表达式来计算新列。结果列可以是分类的、数值的或文本的。 对于数值变量,只需提供名称和表达式。 1.构建变量列表…

学点儿Java_Day7_在实体类当中IDEA无法进行单元测试(@Test没有启动按钮)

在敲代码体会继承和访问修饰符的时候忽然遇到了单元测试不管用的情况,表现为没有启动按钮   经过一番折腾,发现我的测试是在具有构造函数的实体类Person当中进行的,当我把所有的构造函数删除后,启动按钮又出来了,加…

水电能源智能化监控系统

水电能源智能化监控系统是利用现代信息技术,对水电站的运行状态、设备性能、环境参数等进行实时监测和管理的一种智能化系统。随着我国水电能源事业的快速发展,水电能源智能化监控系统在水电能源行业中的应用越来越广泛,为我国水电能源事业的…

关于继承是怎么样的?那当然是很好理解之

本文描述了关于继承的大部分知识,但是并不全,每篇博客之间的知识都有互串,所以需要把几篇文章合起来看,学会融会贯通! 温馨提示:使用PC端观看,效果更佳! 目录 1.继承是什么 2.什…

【位运算】【 数学】【 哈希映射】2857. 统计距离为 k 的点对

本文涉及知识点 位运算 数学 哈希映射 LeetCode 2857. 统计距离为 k 的点对 给你一个 二维 整数数组 coordinates 和一个整数 k ,其中 coordinates[i] [xi, yi] 是第 i 个点在二维平面里的坐标。 我们定义两个点 (x1, y1) 和 (x2, y2) 的 距离 为 (x1 XOR x2) …

STM32最小核心板使用HAL库实现UART接口通讯(中断方式)

正式环境里需要串联电阻,或设计过滤电路。核心板是STM32F103C8T6 这里使用了UART3的接口,具体使用MX创建项目就不放了,百度下都有 /*USART3 GPIO ConfigurationPB10 ------> USART3_TXPB11 ------> USART3_RX */ 因为是串口…

开发技术-FeignClient 对单个接口设置超时时间

1. 背景 FeignClient 调用某个接口,3s 没有结果就需要停止,处理后续业务。 2. 方法 FeignClient 自定义 name 属性 FeignClient(name "aaa" , url "xxx") public interface TestApi {ResponseBodyPOSTMapping(value "xx…

以码会友|PR大征集!2024共绘 MoonBit 新篇章!

首先,感谢 MoonBit 社区所有的贡献者在过去一个月积极的参与和贡献!为了感谢与鼓励更多的MoonBit Contributor,我们以码会友,邀请你加入“一起成为MoonBit Contributor!” 的活动! 活动内容也可以点击文章…

【第十三章】改进神经网络学习方式-其他正则化技术

L1正则化 除了L2正则化之外,还有许多正则化技术。事实上,已经开发出了如此多的技术,以至于我不可能总结它们。在本节中,我简要介绍了三种减少过拟合的其他方法:L1正则化、dropout和人为增加训练集大小。我们不会像之前…

在ComfyUI中,IP-Adapter的一大堆模型应该怎么放?

🎁背景介绍 IP-Adapter有一大堆的模型,那么这个模型在ComfyUI中,这些模型到底应该怎么放呢?这篇文章简单介绍一下。 首先,大家需要到huggingface上找到对应的模型,把所有的模型先下载下来。 huggingface…

技术工作报告-基于linux的信息转二维码图像方法的研究

一、摘要: 本报告旨在介绍基于Linux的信息转二维码图像方法的研究。通过对二维码技术的背景和相关研究的调研,我们提出了一种基于Linux平台的信息转二维码图像方法,并进行了实验验证。本方法可以在Linux系统上实现高效、准确的信息转二维码图…

数据分析的具体流程

1.导入 表格导入数据时要注意数据的格式问题非表格导入 可以先将文档放入word中 将换行符(^p)替换为|||,选择特殊格式中的段落标记 进行全部替换 以每一列最后的数据/平,作为换行的标志 将所整理的信息导入excel,对数据进行分列 选…

大数据 - Spark系列《十四》- spark集群部署模式

Spark系列文章: 大数据 - Spark系列《一》- 从Hadoop到Spark:大数据计算引擎的演进-CSDN博客 大数据 - Spark系列《二》- 关于Spark在Idea中的一些常用配置-CSDN博客 大数据 - Spark系列《三》- 加载各种数据源创建RDD-CSDN博客 大数据 - Spark系列《…

Git版本管理工具

前言: 本文记录学习使用 Git 版本管理工具的学习笔记,通过阅读参考链接中的博文和实际操作,快速的上手使用 Git 工具。 本文参考了引用链接博文里的内容。 引用: Git使用教程-配置管理 git reset详解-CSDN博客 3、Git使用不完全指南&am…

二维数组应用案例

大家好: 衷心希望各位点赞。 您的问题请留在评论区,我会及时回答。 考试成绩统计 案例描述:有三名同学(张三、李四、王五),在一次考试中成绩分别如下表,请分别输出三名同学的总成绩。 成绩表 语…

全面整理!机器学习常用的回归预测模型(表格数据)

文章目录 一、前言二、线性模型三、非线性模型 🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、前言 回归预测建模的核心是学习输入 X X X 到输出 y y y (其中 y y y 是连续值向量)的映射关系。条件期望 E ( Y ∣ X x…

直播预约丨《袋鼠云大数据实操指南》No.1:从理论到实践,离线开发全流程解析

近年来,新质生产力、数据要素及数据资产入表等新兴概念犹如一股强劲的浪潮,持续冲击并革新着企业数字化转型的观念视野,昭示着一个以数据为核心驱动力的新时代正稳步启幕。 面对这些引领经济转型的新兴概念,为了更好地服务于客户…