FreeRTOS学习笔记——FreeRTOS 时间管理

2023-05-16

在使用FreeRTOS 的过程中我们通常会在一个任务函数中使用延时函数对这个任务延时,当执行延时函数的时候就会进行任务切换,并且此任务就会进入阻塞态,直到延时完成,任务重新进入就绪态。延时函数属于FreeRTOS 的时间管理,本章我们就来学习一些FreeRTOS 的这个时间管理过程,看看在调用延时函数以后究竟发生了什么?任务是如何进入阻塞态的,在延时完成以后任务又是如何从阻塞态恢复到就绪态的,本章分为如下几部分:

12.1 FreeRTOS 延时函数
12.3 FreeRTOS 系统时钟节拍

12.1 FreeRTOS 延时函数
12.1 函数vTaskDelay()
学习过UCOSIII 的朋友应该知道,在UCOSIII 中延时函数OSTimeDly()可以设置为三种模式:相对模式、周期模式和绝对模式。在FreeRTOS 中延时函数也有相对模式和绝对模式,不过在FreeRTOS 中不同的模式用的函数不同,其中函数vTaskDelay()是相对模式(相对延时函数),函数vTaskDelayUntil()是绝对模式(绝对延时函数)。函数vTaskDelay()在文件tasks.c 中有定义,要使用此函数的话宏INCLUDE_vTaskDelay 必须为1,函数代码如下:


(1)、延时时间由参数xTicksToDelay 来确定,为要延时的时间节拍数,延时时间肯定要大于0。否则的话相当于直接调用函数portYIELD()进行任务切换。
(2)、调用函数vTaskSuspendAll()挂起任务调度器。
(3) 、调用函数prvAddCurrentTaskToDelayedList() 将要延时的任务添加到延时列表pxDelayedTaskList 或者pxOverflowDelayedTaskList() 中。后面会具体分析函数prvAddCurrentTaskToDelayedList()。
(4)、调用函数xTaskResumeAll()恢复任务调度器。

(5)、如果函数xTaskResumeAll()没有进行任务调度的话那么在这里就得进行任务调度。

(6)、调用函数portYIELD_WITHIN_API()进行一次任务调度。
12.2 函数prvAddCurrentTaskToDelayedList()
函数prvAddCurrentTaskToDelayedList()用于将当前任务添加到等待列表中,函数在文件tasks.c 中有定义,缩减后的函数如下:


(1)、读取进入函数prvAddCurrentTaskToDelayedList()的时间点并保存在xConstTickCount 中,后面计算任务唤醒时间点的时候要用到。xTickCount 是时钟节拍计数器,每个滴答定时器中断xTickCount 都会加一。
(2)、要将当前正在运行的任务添加到延时列表中,肯定要先将当前任务从就绪列表中移除。
(3)、将当前任务从就绪列表中移除以后还要取消任务在uxTopReadyPriority 中的就绪标记。也就是将uxTopReadyPriority 中对应的bit 清零。
(4) 、延时时间为最大值portMAX_DELAY , 并且xCanBlockIndefinitely 不为pdFALSE(xCanBlockIndefinitely 不为pdFALSE 的话表示允许阻塞任务)的话直接将当前任务添加到挂起列表中,任务就不用添加到延时列表中。
(5)、将当前任务添加到挂起列表xSuspendedTaskList 的末尾。
(6)、计算任务唤醒时间点,也就是(1)中获取到的进入函数prvAddCurrentTaskToDelayedList()的时间值xConstTickCount 加上延时时间值xTicksToWait。
(7)、将计算到的任务唤醒时间点值xTimeToWake 写入到任务列表中壮态列表项的相应字段中。
(8)、计算得到的任务唤醒时间点小于xConstTickCount,说明发生了溢出。全局变量xTickCount 是TickType_t 类型的,这是个32 位的数据类型,因此在用xTickCount 计算任务唤醒时间点xTimeToWake 的时候的肯定会出现溢出的现象。FreeRTOS 针对此现象专门做了处理,在FreeROTS 中定义了两个延时列表xDelayedTaskList1 和xDelayedTaskList2,并且也定义了两个指针pxDelayedTaskList 和pxOverflowDelayedTaskList 来访问这两个列表,在初始化列表函数prvInitialiseTaskLists() 中指针pxDelayedTaskList 指向了列表xDelayedTaskList1 , 指针pxOverflowDelayedTaskList 指向了列表xDelayedTaskList2。这样发生溢出的话就将任务添加到pxOverflowDelayedTaskList 所指向的列表中,如果没有溢出的话就添加到pxDelayedTaskList 所指向的列表中。

(9)、如果发生了溢出的话就将当前任务添加到pxOverflowDelayedTaskList 所指向的列表中。

(10)、如果没有发生溢出的话就将当前任务添加到pxDelayedTaskList 所指向的列表中。
(11)、xNextTaskUnblockTime 是个全局变量,保存着距离下一个要取消阻塞的任务最小时间点值。 当xTimeToWake 小于xNextTaskUnblockTime 的话说明有个更小的时间点来了。
(12)、更新xNextTaskUnblockTime 为xTimeToWake。
12.3 函数vTaskDelayUntil()
函数vTaskDelayUntil()会阻塞任务,阻塞时间是一个绝对时间,那些需要按照一定的频率运行的任务可以使用函数vTaskDelayUntil()。此函数再文件tasks.c 中有如下定义:


参数:
pxPreviousWakeTime: 上一次任务延时结束被唤醒的时间点,任务中第一次调用函数vTaskDelayUntil 的话需要将pxPreviousWakeTime 初始化进入任务的while()循环体的时间点值。在以后的运行中函数vTaskDelayUntil()会自动更新pxPreviousWakeTime。xTimeIncrement:任务需要延时的时间节拍数(相对于pxPreviousWakeTime 本次延时的节拍数)。
(1)、挂起任务调度器。
(2)、记录进入函数vTaskDelayUntil()的时间点值,并保存在xConstTickCount 中。
(3)、根据延时时间xTimeIncrement 来计算任务下一次要唤醒的时间点, 并保存在xTimeToWake 中。可以看出这个延时时间是相对于pxPreviousWakeTime 的,也就是上一次任务被唤醒的时间点。pxPreviousWakeTime、xTimeToWake、xTimeIncrement 和xConstTickCount 的关系如图12.3.1 所示。


如12.3.1 中 (1)为任务主体,也就是任务真正要做的工作, (2)是任务函数中调用vTaskDelayUntil()对任务进行延时,(3)为其他任务在运行。任务的延时时间是xTimeIncrement,这个延时时间是相对于pxPreviousWakeTime 的,可以看出任务总的执行时间一定要小于任务的延时时间xTimeIncrement!也就是说如果使用vTaskDelayUntil()的话任务相当于任务的执行周期永远都是xTimeIncrement,而任务一定要在这个时间内执行完成。这样就保证了任务永远按照一定的频率运行了,这个延时值就是绝对延时时间,因此函数vTaskDelayUntil()也叫做绝对延时函数。
(4)、根据图12.3.1 可以看出,理论上xConstTickCount 要大于pxPreviousWakeTime 的,但是也有一种情况会导致xConstTickCount 小于pxPreviousWakeTime,那就是xConstTickCount 溢出了!
(5)、既然xConstTickCount 都溢出了,那么计算得到的任务唤醒时间点肯定也是要溢出的,并且xTimeToWake 肯定也是要大于xConstTickCount 的。这种情况如图12.3.2 所示:


(6)、如果满足(5)条件的话就将pdTRUE 赋值给xShouldDelay,标记允许延时。
(7)、还有其他两种情况,一:只有xTimeToWake 溢出,二:都没有溢出。只有xTimeToWake溢出的话如图12.3.3 所示:



都不溢出的话就如图12.3.1 所示,这两种情况都允许进行延时。
(8)、将pdTRUE 赋值给xShouldDelay,标记允许延时。
(9)、更新pxPreviousWakeTime 的值,更新为xTimeToWake,为本函数的下一次执行做准备。
(10)、经过前面的判断,允许进行任务延时。
(11)、调用函数prvAddCurrentTaskToDelayedList()进行延时。函数的第一个参数是设置任务的阻塞时间,前面我们已经计算出了任务下一次唤醒时间点了,那么任务还需要阻塞的时间就是下一次唤醒时间点xTimeToWake 减去当前的时间xConstTickCount。而在函数vTaskDelay()中只是简单的将这参数设置为xTicksToDelay。
(12)、调用函数xTaskResumeAll()恢复任务调度器。函数vTaskDelayUntil()的使用方法如下:


其实使用函数vTaskDelayUntil()延时的任务也不一定就能周期性的运行,使用函数vTaskDelayUntil()只能保证你按照一定的周期取消阻塞,进入就绪态。如果有更高优先级或者中断的话你还是得等待其他的高优先级任务或者中断服务函数运行完成才能轮到你。这个绝对延时只是相对于vTaskDelay()这个简单的延时函数而言的。

12.2 FreeRTOS 系统时钟节拍
不管是什么系统,运行都需要有个系统时钟节拍,前面已经提到多次了,xTickCount 就是FreeRTOS 的系统时钟节拍计数器。每个滴答定时器中断中xTickCount 就会加一,xTickCount 的具体操作过程是在函数xTaskIncrementTick()中进行的,此函数在文件tasks.c 中有定义,如下:




(1)、判断任务调度器是否被挂起。
(2)、将时钟节拍计数器xTickCount 加一,并将结果保存在xConstTickCount 中,下一行程序会将xConstTickCount 赋值给xTickCount,相当于给xTickCount 加一。
(3)、xConstTickCount 为0,说明发生了溢出!
(4)、如果发生了溢出的话使用函数taskSWITCH_DELAYED_LISTS 将延时列表指针pxDelayedTaskList 和溢出列表指针pxOverflowDelayedTaskList 所指向的列表进行交换,函数taskSWITCH_DELAYED_LISTS()本质上是个宏,在文件tasks.c 中有定义,将这两个指针所指向的列表交换以后还需要更新xNextTaskUnblockTime 的值。
(5)、变量xNextTaskUnblockTime 保存着下一个要解除阻塞的任务的时间点值,如果xConstTickCount 大于xNextTaskUnblockTime 的话就说明有任务需要解除阻塞了。
(6)、判断延时列表是否为空。
(7)、如果延时列表为空的话就将xNextTaskUnblockTime 设置为portMAX_DELAY。
(8)、延时列表不为空,获取延时列表第一个列表项对应的任务控制块。
(9)、获取(8)中获取到的任务控制块中的壮态列表项值。
(10)、任务控制块中的壮态列表项值保存了任务的唤醒时间点,如果这个唤醒时间点值大于当前的系统时钟(时钟节拍计数器值),说明任务的延时时间还未到。

(11)、任务延时时间还未到,而且xItemValue 已经保存了下一个要唤醒的任务的唤醒时间点,所以需要用xItemValue 来更新xNextTaskUnblockTime。

(12)、任务延时时间到了,所以将任务先从延时列表中移除。
(13)、检查任务是否还等待某个事件,比如等待信号量、队列等。如果还在等待的话就任务从相应的事件列表中移除。因为超时时间到了!
(14)、将任务从相应的事件列表中移除。
(15)、任务延时时间到了,并且任务已经从延时列表或者事件列表中已经移除。所以这里需要将任务添加到就绪列表中。
(16)、延时时间到的任务优先级高于正在运行的任务优先级,所以需要进行任务切换了,标记xSwitchRequired 为pdTRUE,表示需要进行任务切换。
(17)、如果使能了时间片调度的话,还要处理跟时间片调度有关的工作,具体过程参考9.6小节。
(18)、如果使能了时间片钩子函数的话就执行时间片钩子函数vApplicationTickHook(),函数的具体内容由用户自行编写。
(19)、如果调用函数vTaskSuspendAll()挂起了任务调度器的话在每个滴答定时器中断就不不会更新xTickCount 了。取而代之的是用uxPendedTicks 来记录调度器挂起过程中的时钟节拍数。这样在调用函数xTaskResumeAll()恢复任务调度器的时候就会调用uxPendedTicks 次函数xTaskIncrementTick(),这样xTickCount 就会恢复,并且那些应该取消阻塞的任务都会取消阻塞。函数xTaskResumeAll()中相应的处理代码如下:


(20)、uxPendedTicks 是个全局变量,在文件tasks.c 中有定义,任务调度器挂起以后此变量用来记录时钟节拍数。
(21)、有时候调用其他的API 函数会使用变量xYieldPending 来标记是否需要进行上下文切换,后面具体遇到具体分析。
(22)、返回xSwitchRequired 的值,xSwitchRequired 保存了是否进行任务切换的信息,如果为pdTRUE 的话就需要进行任务切换,pdFALSE 的话就不需要进行任务切换。函数xPortSysTickHandler()中调用xTaskIncrementTick()的时候就会判断返回值,并且根据返回值决定是否进行任务切换。




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS学习笔记——FreeRTOS 时间管理 的相关文章

  • 硬件工程师必会模块之MOS管构成的基本门逻辑电路—看芯片手册框图必备技能

    本文你可以获得什么 xff1f MOS管构成的缓冲器Buffer和漏极开路们OD门是数字电路非常重要的概念 xff0c 怎么构成的 xff1b 反相器 xff0c 线与逻辑怎么玩 xff0c 又怎么用呢 xff1f 根据原理图 xff0c
  • 关于信号量的作用范围

    最近在搞信号量的时候产生了怀疑 xff0c 信号量的作用范围和信号量在任务的位置时候有关系呢 xff1f 答案是有的 xff0c 其实信号量的作用范围是指的从信号量开始到程序的跳转点为止 xff0c 比如一个任务中 xff0c 把信号量放在
  • UNIX 环境高级编程之我见

    UNIX环境高级编程 xff08 第二版 xff09 xff08 人民邮电出版社 xff09 美 W Richard Stevens amp Stephen A Rago 著 本书的主要结构分为以下几个部分 xff1a xff08 1 xf
  • xShell连接ubuntu不成功

    操作环境 xff1a xShell6 43 vm15 43 ubuntu18 04 xShell中以root身份连接虚拟机中的ubuntu时 xff0c 连接不上 xff0c 见下图 xff1a 密码输入的也是对的 xff0c 也能够双向p
  • mac brew install

    brew cask install myprogram base darren 64 Darren 2 project brew cask install docker Error Unknown command cask brew ins
  • mac/linux 系统批量计算文件md5命令

    find type f print0 xargs 0 md5
  • 链表-设计链表

    leetcode 707 设计链表 注意 xff1a 面向对象的思想 这是创建一个自己的链表 xff0c dummyhead和 size初始化都在构造函数中 xff0c 同时自己的链表类也需要定义自己的节点 struct LinkNode链
  • KVM虚拟机使用桥接方式时和宿主机无法通信的解决方案

    KVM虚拟机使用桥接方式时和宿主机无法通信的解决方案 应用场景 虚拟机客户机安装完成后 xff0c 需要为其设置网络接口 xff0c 以便和主机网络 xff0c 客户机之间的网络通信 事实上 xff0c 如果要在安装时使用网络通信 xff0
  • android 电池充电状态记录

    摘抄源码记录下 http androidxref com 9 0 0 r3 xref frameworks native include batteryservice BatteryServiceConstants h This file
  • python 输入三个变量,然后按小到大输出(解析)

    python 实例解析 xff08 1 xff09 vim 2 python py x 61 int input 39 please input x 39 y 61 int input 39 please input y 39 z 61 i
  • Linux系统调用Hook姿势总结

    http www cnblogs com LittleHann p 3854977 html 主题 Linux 相关学习资料 http xiaonieblog com post 61 121 http hbprotoss github io
  • 利用WireShark进行DNS协议分析

    一 准备工作 系统是Windows 8 1Pro 分析工具是WireShark1 10 8 Stable Version 使用系统Ping命令发送ICMP报文 二 开始工作 打开CMD exe键入 ping www oschina net
  • DNS协议详解及报文格式分析

    DNS协议详解及报文格式分析 Posted on 2017 06 18 by Jocent No Comments 目录 一 DNS协议理论知识 1 1 域名结构1 2 域名服务器1 3 域名解析过程 二 DNS协议报文格式 2 1 头部2
  • 解密微信数据库文件解析

    图解说明 xff1a 微信大量数据存储在本地比如 xff1a 联系人 xff08 包含好友地区 电话 通过那种方式添加 xff09 聊天内容 xff08 图片 文字 语音 视频 位置 名片 其他app分享链接 xff09 聊天室 收藏信息
  • qgroundcontrol二次开发环境搭建

    参考考qgroundcontrol官方文档 xff0c 做一些准备工作 xff1a https dev qgroundcontrol com master en getting started index html 1 按官方文档下载qgr
  • Android通过注解来初始化控件

    Android通过注解来初始化控件 对于Android大神的思想与能力我只能膜拜与学习了 xff0c 这里就从大神哪里学来的通过注解来初始化控件简单的概述一下 xff0c 省去了那么繁琐的findViewById步骤 xff08 找到了还要
  • KEIL MDK 工程中头文件包含的路径详解

    xff08 参考工程详见https download csdn net download tianzhijiaoxin 87464281 xff09 文章目录 前言一 include lt h gt 与include 34 h 34 的区别
  • 51-单片机---定时器0和定时器1---8位自动重装载(模式2)-16位定时计数(模式1)

    16位定时计数 xff08 工作方式1 xff09 初始化函数 void timer init TMOD 61 0x01 TH0 61 0x4C TL0 61 0x00 EA 61 1 ET0 61 1 TR0 61 1 初始化定时器运行
  • 算法基础21-图的最短路问题通解

    最短路问题通解 单源最短路所有边权都是正数朴素Dijkstra算法堆优化版Dijkstra算法 存在负权边bellman fordspfaspfa判断是否存在负权环路 单源最短路小结 多源汇最短路floyd 总结 想一想各种最短路算法模版和
  • Visual Studio 2008学习过程(之二)与MATLAB混合编程

    Visual Studio 2008学习过程 之二 MATLAB混合编程 上一篇我写的是我初识VisualStudio2008的过程 后来我又用它开发了几个小程序 至于怎么做的 过两天我再写 这篇文章我就写写VS和MATLAB联合开发程序的

随机推荐

  • HIVE迁移教程X86架构到ARM架构(CPU:鲲鹏920)

    centos8的hive迁移教程 1安装新的centos8环境 2 安装实验所需软件 2 1 安装OpenJDK yum span class token function install span java 1 8 0 openjdk 配
  • C++滑动窗口算法

    滑动窗口算法在一个特定大小的字符串或数组上进行操作 xff0c 而不在整个字符串和数组上操作 xff0c 这样就降低了问题的复杂度 xff0c 从而也达到降低了循环的嵌套深度 如下题 给你两个长度相同的字符串 xff0c s 和 t 将 s
  • C++和js交互方案对比

    c 43 43 和js交互方案对比 一 xff1a nodejs技术 nodejs技术是基于V8引擎的一套前后端交互技术 nan h为c 43 43 提供了与js交互的一系列V8 API 参考链接 缺点 xff1a 在Node js中 xf
  • C++实现HTTP服务

    一个多平台的系统基本架构 xff08 如下图 xff09 xff0c 数据库部分我们以后可以使用HDFS和MapReduce进行分布式存储 xff0c 之前大致介绍了js和c 43 43 交互的几种方式对比 xff0c 考虑到拓展性和访问效
  • gstreamer获取视频采集卡的数据

    gstreamer获取视频采集卡的视频数据 gstreamer可以用于采集硬件视频数据 xff0c 转码 xff0c 播放 xff0c 传输等 xff0c 但由于框架相对于FFmpeg较为小众 xff0c 所以资料较少 xff0c 整理一份
  • 使用c++来实现一个简单的数据库功能

    使用c 43 43 来实现一个简单的文件数据库功能 功能点1 xff1a 建表 1 创建和表同名的文件 2 在文件中存储表的信息 xff0c 包括attribute的name xff0c 数量 xff0c 类型 xff0c 是否唯一 3 添
  • 【性能优化】cpu时间抖动问题的解决修复

    问题描述 xff1a 边缘设备的cpu在低占有率时 xff0c 进程运行时间抖动较大 xff0c 在高占有率时 xff0c 运行时间抖动更稳定 低占有率运行情况图 xff1a 相同处理逻辑循环中 xff0c 两次处理的时间间隔 xff1a
  • pointRCNN 结果可视化

    由于pointRCNN源码的训练和inference很详细 xff0c 但是没有可视化的代码 xff0c 本文介绍其3d框结果的可视化方法 1 跑通pointRCNN https github com sshaoshuai PointRCN
  • C/C++学习笔记——C提高: 函数指针和递归函数

    函数指针 函数类型 通过什么来区分两个不同的函数 xff1f 一个函数在编译时被分配一个入口地址 xff0c 这个地址就称为函数的指针 xff0c 函数名代表函数的入口地址 函数三要素 xff1a 名称 参数 返回值 C语言中的函数有自己特
  • 算法基础22-最小生成树

    最小生成树 primkruskal prim 链接 acwing上一个关于prim很好的题解 prim 算法干的事情是 xff1a 给定一个无向图 xff0c 在图中选择若干条边把图的所有节点连起来 要求边长之和最小 在图论中 xff0c
  • C/C++学习笔记——C提高:预处理

    预处理的基本概念 C语言对源程序处理的四个步骤 xff1a 预处理 编译 汇编 链接 预处理是在程序源代码被编译之前 xff0c 由预处理器 xff08 Preprocessor xff09 对程序源代码进行的处理 这个过程并不对程序的源代
  • C/C++学习笔记——C提高:动态库的封装和使用

    库的基本概念 库是已经写好的 成熟的 可复用的代码 每个程序都需要依赖很多底层库 xff0c 不可能每个人的代码从零开始编写代码 xff0c 因此库的存在具有非常重要的意义 在我们的开发的应用中经常有一些公共代码是需要反复使用的 xff0c
  • FreeRTOS学习笔记——基础知识与移植(STM32F103)

    1 1 前后台系统 xff1a 早期嵌入式开发没有嵌入式操作系统的概念 xff0c 直接操作裸机 xff0c 在裸机上写程序 xff0c 比如用51单片机基本就没有操作系统的概念 通常把程序分为两部分 xff1a 前台系统和后台系统 简单的
  • STM32开源代码——MAX30100程序

    正点原子精英开发板 模块化封装 xff0c 入口函数简明 xff0c 易上手操作 展示main c代码 xff0c 完整代码请下载 xff08 数据打印到串口在MAX30100 PulseOximeter c xff09 点击下载代码 in
  • STM32开源代码——2.8寸TFTLCD屏虚拟键盘触摸程序

    正点原子精英开发板 模块化封装 xff0c 入口函数简明 xff0c 易上手操作 展示main c代码 xff0c 完整代码请下载 点击下载代码 include 34 sys h 34 include 34 delay h 34 inclu
  • STM32开源代码——光敏传感器

    正点原子精英开发板 模块化封装 xff0c 入口函数简明 xff0c 易上手操作 展示main c代码 xff0c 完整代码请下载 点击下载代码 include 34 led h 34 include 34 delay h 34 inclu
  • 个人项目——机智云开源APP基础修改教程(Android)

    之前写过一篇STM32接入机智云的教程 xff0c 最后说要有时间给大家写一篇修改机智云开源Demo APP的教程 xff0c 刚好楼主考完直流传动 xff0c 然后帮小学弟的一个32项目接入了机智云 xff0c 然后打算帮他修改一下Dem
  • FreeRTOS学习笔记——FreeRTOS任务创建和删除实验(静态方法)

    6 3 任务创建和删除实验 静态方法 6 3 1 实验程序设计 1 实验目的 上一小节我们讲了使用函数xTaskCreate 来创建任务 xff0c 本节在上一小节的基础上做简单的修改 xff0c 使用函数xTaskCreateStatic
  • FreeRTOS学习笔记——FreeRTOS 系统内核控制函数

    FreeRTOS 中有一些函数只供系统内核使用 xff0c 用户应用程序一般不允许使用 xff0c 这些API 函数就是系统内核控制函数 本章我们就来学习一下这些内核控制函数 xff0c 本章分为如下几部分 xff1a 10 1 内核控制函
  • FreeRTOS学习笔记——FreeRTOS 时间管理

    在使用FreeRTOS 的过程中我们通常会在一个任务函数中使用延时函数对这个任务延时 xff0c 当执行延时函数的时候就会进行任务切换 xff0c 并且此任务就会进入阻塞态 xff0c 直到延时完成 xff0c 任务重新进入就绪态 延时函数