FreeRTOS时间管理

FreeRTOS时间管理

主要要了解延时函数:
在这里插入图片描述
相对延时:指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束。
绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务。
函数 vTaskDelayUntil()是绝对模式(绝对延时函数)。函数 vTaskDelay()在文件 tasks.c 中有定义,要使用此函数的话宏 INCLUDE_vTaskDelay 必须为 1,

void vTaskDelay( const TickType_t xTicksToDelay )

具体的函数代码如下:

void vTaskDelay( const TickType_t xTicksToDelay )//{BaseType_t xAlreadyYielded = pdFALSE;/* A delay time of zero just forces a reschedule. */if( xTicksToDelay > ( TickType_t ) 0U ){configASSERT( uxSchedulerSuspended == 0 );vTaskSuspendAll();/*通过调用vTaskSuspendAll挂起所有任务,这是为了安全地更新任务的状态和延迟列表,防止在操作过程中发生中断导致的数据不一致。*/{traceTASK_DELAY();prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );/*调用prvAddCurrentTaskToDelayedList将当前任务添加到延迟列表中,xTicksToDelay指定了延迟的时间,pdFALSE表示此任务在延迟期满时不需要立即运行。*/}xAlreadyYielded = xTaskResumeAll();/*通过调用xTaskResumeAll尝试恢复之前挂起的任务。如果在挂起期间有任务变为就绪状态,xTaskResumeAll会返回pdTRUE,表示已经触发了任务切换,否则返回pdFALSE。*/}else{mtCOVERAGE_TEST_MARKER();}if( xAlreadyYielded == pdFALSE ){portYIELD_WITHIN_API();/*如果xTaskResumeAll返回pdFALSE,这意味着在挂起所有任务和恢复任务切换的过程中,没有其他任务变为就绪状态,从而没有自动触发任务切换。但是,当前任务通过调用vTaskDelay已经表达了它愿意让出CPU。为了确保这种意愿得到尊重,即使xTaskResumeAll没有触发任务切换,也通过调用portYIELD_WITHIN_API强制进行一次任务调度。这样做确保了调度器会重新评估哪个任务应该运行,即使当前任务的延迟时间为0,也会按照优先级选择另一个任务运行,如果有的话。*/}else{mtCOVERAGE_TEST_MARKER();}}

接下来让我们来看这个函数prvAddCurrentTaskToDelayedList(),该函数就是用于将当前任务添加到等待列表。
函数声明:static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely)声明了一个静态函数,接受两个参数:xTicksToWait(任务应该被延迟的tick数)和xCanBlockIndefinitely(一个布尔值,指示任务是否可以无限期地阻塞)

static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,const BaseType_t xCanBlockIndefinitely )
{TickType_t xTimeToWake;const TickType_t xConstTickCount = xTickCount;/*获取当前的tick计数(xTickCount),这是系统启动以来经过的tick数。*/#if ( INCLUDE_xTaskAbortDelay == 1 ){pxCurrentTCB->ucDelayAborted = pdFALSE;}/*重置延迟中止标志(如果启用了INCLUDE_xTaskAbortDelay):这部分代码通过将pxCurrentTCB->ucDelayAborted设置为pdFALSE,确保当任务被移动到延迟列表时,任何之前的延迟中止请求都被清除。*/#endifif( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );}/*将当前任务从就绪列表中移除以后还要取消任务在 uxTopReadyPriority中的就绪标记。也就是将 uxTopReadyPriority 中对应的 bit 清零。*/else{mtCOVERAGE_TEST_MARKER();}#if ( INCLUDE_vTaskSuspend == 1 ){/*这部分代码检查任务是否请求无限期等待(xTicksToWait == portMAX_DELAY)。portMAX_DELAY通常定义为可表示的最大延时,意味着任务希望无限期挂起。同时,它检查xCanBlockIndefinitely标志,确保任务允许无限期阻塞。如果两个条件都满足,任务会被加入到挂起任务列表(xSuspendedTaskList)的末尾。这意味着任务将不会被调度,直到明确地被唤醒。*/if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ){listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );}/*如果任务不是无限期挂起,那么它请求有限的延时。这部分代码计算任务应当被唤醒的时间点(xTimeToWake),并将这个时间设置为任务状态列表项的值。*/else{xTimeToWake = xConstTickCount + xTicksToWait;listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );if( xTimeToWake < xConstTickCount ){vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );}else{vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );/*这里检查是否存在时间溢出的情况。如果xTimeToWake小于当前的xConstTickCount,说明发生了溢出,任务被插入到溢出延时任务列表(pxOverflowDelayedTaskList)。否则,任务插入到正常的延时任务列表(pxDelayedTaskList)。*/if( xTimeToWake < xNextTaskUnblockTime ){xNextTaskUnblockTime = xTimeToWake;}/*当任务进入阻塞状态时(例如,等待一个事件或延时),它的唤醒时间会被计算并设置。这时,系统会检查这个唤醒时间是否早于当前的xNextTaskUnblockTime:如果早于:这意味着系统中有一个新的最早唤醒时间,因此需要更新xNextTaskUnblockTime为这个新时间。这样可以确保调度器能够在正确的时间唤醒任务。如果晚于或等于:xNextTaskUnblockTime不需要更新,因为已经存在一个更早或相同时间的任务需要被唤醒。*/else{mtCOVERAGE_TEST_MARKER();}}}}#else /* INCLUDE_vTaskSuspend */{xTimeToWake = xConstTickCount + xTicksToWait;/* The list item will be inserted in wake time order. */listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );if( xTimeToWake < xConstTickCount ){vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );}else{vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );if( xTimeToWake < xNextTaskUnblockTime ){xNextTaskUnblockTime = xTimeToWake;}else{mtCOVERAGE_TEST_MARKER();}}( void ) xCanBlockIndefinitely;}#endif /* INCLUDE_vTaskSuspend */
}

1: if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )在FreeRTOS中,uxListRemove函数用于从列表中移除一个项,并返回该项所在列表中的剩余项数。这个函数的返回值在某些情况下用于判断是否需要进行额外的操作,比如更新调度器的状态或做一些清理工作。具体到if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )这行代码的意义,我们来详细解释一下:

  1. 移除任务从就绪列表:这行代码的主要目的是从就绪列表中移除当前任务的状态列表项(pxCurrentTCB->xStateListItem)。在FreeRTOS中,每个任务都有一个与之关联的列表项,用于将该任务链接到不同的任务列表中,例如就绪列表、延迟列表等。当任务需要被延迟或阻塞时,它必须首先从就绪列表中移除。

  2. 判断列表项是否是列表中的最后一个:通过检查uxListRemove的返回值是否为0,这行代码实际上是在判断移除操作后,原列表是否为空。如果返回值为0,意味着在移除当前任务之前,它是列表中的唯一任务项。这种情况下,就绪列表变为空,可能需要进行一些额外的操作,比如调整就绪任务的优先级位图。

  3. 调整优先级位图:如果当前任务是其优先级队列中的唯一任务,移除它后,该优先级队列变为空。在这种情况下,需要调用portRESET_READY_PRIORITY宏(或类似的操作),来在就绪优先级位图中清除相应优先级的位。这是因为,如果一个优先级队列为空,那么调度器在选择下一个要运行的任务时,就不应该考虑这个优先级了。

  4. 保持调度器的正确性:这个判断和随后的操作确保了调度器能够正确地反映当前系统的状态,避免在选择下一个要运行的任务时,考虑到已经没有任务的优先级队列。

2:listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );在FreeRTOS中,listSET_LIST_ITEM_VALUE是一个宏,用于设置列表项的值。这个宏通常用于与任务控制块(TCB)相关的列表项,以跟踪特定的信息,如任务的唤醒时间。
这行代码的作用是设置当前任务(由pxCurrentTCB指向)的状态列表项(xStateListItem)的值为xTimeToWake。这里,xTimeToWake是计算出的任务应当被唤醒的时间点。

  • pxCurrentTCB: 是指向当前任务控制块(Task Control Block)的指针。每个任务在FreeRTOS中都有一个TCB,其中包含了管理和调度任务所需的所有信息。

  • xStateListItem: 是TCB中的一个成员,是一个ListItem_t结构体。这个结构体用于将任务链接到不同的列表中,例如就绪列表、延时列表等。通过这种方式,FreeRTOS的调度器可以管理和调度多个任务。

  • listSET_LIST_ITEM_VALUE: 这个宏接受两个参数,第一个参数是列表项的地址,第二个参数是要设置的值。在这个上下文中,它用于设置任务的唤醒时间。这个值随后用于确定何时将任务从延时列表移动到就绪列表,以便调度器可以重新调度该任务。

函数 vTaskDelayUntil()

函数 vTaskDelayUntil()会阻塞任务,阻塞时间是一个绝对时间,那些需要按照一定的频率运行的任务可以使用函数 vTaskDelayUntil()。此函数再文件 tasks.c 中有如下定义:

    BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,const TickType_t xTimeIncrement )/*这是xTaskDelayUntil函数的定义,接受两个参数:pxPreviousWakeTime是指向上一次唤醒时间的指针,xTimeIncrement是两次唤醒之间的时间间隔。*/{TickType_t xTimeToWake;BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;/*定义局部变量xTimeToWake来存储下一次唤醒的时间点,xAlreadyYielded用于指示是否已经进行了任务切换,xShouldDelay标志是否需要延迟。*/configASSERT( pxPreviousWakeTime );configASSERT( ( xTimeIncrement > 0U ) );configASSERT( uxSchedulerSuspended == 0 );vTaskSuspendAll();//调用vTaskSuspendAll来暂停所有任务调度,这是为了防止在更新计数器和计算下一次唤醒时间时发生中断。{/* Minor optimisation.  The tick count cannot change in this* block. */const TickType_t xConstTickCount = xTickCount;//获取当前的tick计数并保存到xConstTickCount中,这个值在这个代码块中不会改变,用于后续的时间计算。/* 计算下一次唤醒的时间点,即上一次唤醒时间加上时间间隔。 */xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;/* 计算下一次唤醒的时间点,即上一次唤醒时间加上时间间隔。 */if( xConstTickCount < *pxPreviousWakeTime ){if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ){xShouldDelay = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ){xShouldDelay = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}*pxPreviousWakeTime = xTimeToWake;if( xShouldDelay != pdFALSE ){traceTASK_DELAY_UNTIL( xTimeToWake );prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );}//如果需要延迟,记录跟踪信息并将当前任务添加到延迟列表中,等待直到它的唤醒时间到达。else{mtCOVERAGE_TEST_MARKER();}}xAlreadyYielded = xTaskResumeAll();if( xAlreadyYielded == pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}return xShouldDelay;}

下面我们要分析计数器溢出的几种情况,为了更好理解溢出的几种情况。可以根据下面这个图更好去理解这个过程:
在这里插入图片描述

            if( xConstTickCount < *pxPreviousWakeTime ){if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ){xShouldDelay = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}

根据图 12.3.1 可以看出,理论上 xConstTickCount 要大于 pxPreviousWakeTime 的,但是也有一种情况会导致 xConstTickCount 小于 pxPreviousWakeTime,那就是 xConstTickCount 溢出了!既然 xConstTickCount 都溢出了,那么计算得到的任务唤醒时间点肯定也是要溢出的,并且 xTimeToWake 肯定也是要大于 xConstTickCount 的。接下来就是分情况去讨论:
在这里插入图片描述
还有其他两种情况,一:只有 xTimeToWake 溢出,二:都没有溢出。只有 xTimeToWake溢出的话如图 12.3.3 所示:
在这里插入图片描述
其实使用函数 vTaskDelayUntil()延时的任务也不一定就能周期性的运行,使用函数vTaskDelayUntil()只能保证你按照一定的周期取消阻塞,进入就绪态。如果有更高优先级或者中断的话你还是得等待其他的高优先级任务或者中断服务函数运行完成才能轮到你。这个绝对延时只是相对于 vTaskDelay()这个简单的延时函数而言的。

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

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

相关文章

软件杯 深度学习图像修复算法 - opencv python 机器视觉

文章目录 0 前言2 什么是图像内容填充修复3 原理分析3.1 第一步&#xff1a;将图像理解为一个概率分布的样本3.2 补全图像 3.3 快速生成假图像3.4 生成对抗网络(Generative Adversarial Net, GAN) 的架构3.5 使用G(z)生成伪图像 4 在Tensorflow上构建DCGANs最后 0 前言 &#…

vagrant 安装虚拟机,docker, k8s

第一步&#xff1a;安装虚拟机 1、安装 vagrant 本机是 mac, 但是这一步不影响&#xff0c;找对应操作系统的安装方式就行了。 vagrant 下载地址 brew install vagrant 2、下载 VirtualBox 虚拟机 VirtualBox 下载地址 找到对应系统下载&#xff0c;安装就可以。 尽量把…

Web3D智慧医院平台(HTML5+Threejs)

智慧医院的建设将借助物联网、云计算、大数据、数字孪生等技术&#xff0c;以轻量化渲染、极简架构、三维可视化“一张屏”的形式&#xff0c;让医院各大子系统管理既独立又链接&#xff0c;数据相互融合及联动。 建设医院物联网应用的目标对象&#xff08;人、物&#xff09;都…

移动硬盘盒支持PD充电:优势解析与实际应用探讨

随着科技的飞速发展&#xff0c;数据存储和传输的需求日益增长&#xff0c;移动硬盘盒作为便携式存储设备的重要载体&#xff0c;其功能和性能也在不断提升。近年来&#xff0c;越来越多的移动硬盘盒开始支持PD&#xff08;Power Delivery&#xff09;充电技术&#xff0c;这一…

QT串口助手

QT应用程序打包教程&#xff08;超简单&#xff09;&#xff1a;QT如何打包生成独立可执行.exe文件 .cpp文件&#xff1a; // 这里包含所需要的头文件mainwindow.h和ui_mainwindow.h以及Qt提供的QMessageBox和QTextStream类 #include "mainwindow.h" #include &qu…

【Image captioning】论文阅读九—Self-Distillation for Few-Shot Image Captioning_2022

摘要 大规模图像字幕数据集的开发成本高昂,而大量未配对的图像和文本语料库可能有助于减少手动注释的工作。在本文中,我们研究了只需要少量带注释的图像标题对的少样本图像标题问题。我们提出了一种基于集成的自蒸馏方法,允许使用不成对的图像和字幕来训练图像字幕模型。该…

【HCIP】OSPF的高级特性

OSPF的高级特性1 --- 不规则区域 一、OSPF不规则区域类型 产生原因&#xff1a;区域划分不合理&#xff0c;导致的问题 1、非骨干区域无法和骨干区域保持连通 2、骨干区域被分割 造成后果&#xff1a;非骨干区域没和骨干区域相连&#xff0c;导致ABR将不会帮忙转发区域间的路由…

使用 code-server 搭建在线的 VS Code 编辑器

文章目录 前言安装体验后记 前言 VS Code 是一个非常流行的代码编辑器&#xff0c;安装各种拓展下也可以当作全功能的IDE使用。VS Code本身是基于Web方案构建的&#xff0c;完全可以搭建服务器&#xff0c;然后通过浏览器访问。事实上官方就是这么设计的。现在打开任何一个Git…

mp3转m4a怎么转?4种方法无损转换音频~

M4A文件格式&#xff0c;或称MPEG-4 Audio&#xff0c;崭露头角于音频时代。其诞生旨在提供更高保真度和更高效的音频压缩&#xff0c;为多媒体应用和苹果设备赋能。 M4A格式与MP3格式的优缺点对比 M4A与MP4格式密不可分&#xff0c;均属于MPEG-4标准。相较MP3&#xff0c;M4A…

“中医显示器”是人体健康监测器

随着科技的进步&#xff0c;现代医学设备已经深入到了人们的日常生活中。然而&#xff0c;在这个过程中&#xff0c;我们不应忘记我们的医学根源&#xff0c;中医。我们将中医的望、闻、问、切四诊与现代科技相结合&#xff0c;通过一系列的传感器和算法将人体的生理状态以数字…

通过Maven导入本地jar包

1.创建lib文件夹&#xff0c;把jar包放到文件夹里面 2.在pom里导入依赖 导入完成

滤波器笔记(杂乱)

线性相位是时间平移&#xff0c;相位不失真 零、基础知识 1、用相量表示正弦量 https://zhuanlan.zhihu.com/p/345546880 https://www.zhihu.com/question/347763932/answer/1103938667 A s i n ( ω t θ ) ⇔ A e j θ ⇔ A ∠ θ Asin(\omega t\theta) {\Leftrightarrow…

VMware 虚拟机中的 Ubuntu 16.04 设置 USB 连接

VMware 虚拟机中的 Ubuntu 16.04 设置 USB 连接 1. VMware USB Arbitration Service2. 可移动设备 USB 口连接主机3. 虚拟机 -> 可移动设备 -> 连接 (断开与主机的连接)4. 状态栏 -> 断开连接 (连接主机)References 1. VMware USB Arbitration Service 计算机 -> …

lv_micropython for ESP32-S2/S3/C3

一、更新文件 lv_binding_micropython:GitHub - kdschlosser/lv_binding_micropython at esp32-s-c-h_support 下载lv_binding_micropython分支&#xff1a; git clone -b esp32-s-c-h_support https://github.com/kdschlosser/lv_binding_micropython.git 替换文件&#x…

NodeJS特点

NodeJS特点 web服务器的主要特点是&#xff1a;事件驱动&#xff0c;非阻塞I/O&#xff0c;单线程&#xff0c;跨平台自身非常简单&#xff0c;通过通信协议来组织许多node&#xff0c;通过拓展来达成构建大型网络应用的目的。每一个node进程都构成这个网络的一个节点适用于io…

4个步骤:如何使用 SwiftSoup 和爬虫代理获取网站视频

摘要/导言 在本文中&#xff0c;我们将探讨如何使用 SwiftSoup 库和爬虫代理技术来获取网站上的视频资源。我们将介绍一种简洁、可靠的方法&#xff0c;以及实现这一目标所需的步骤。 背景/引言 随着互联网的迅速发展&#xff0c;爬虫技术在今天的数字世界中扮演着越来越重要…

海外云手机为什么适合社媒运营?

如今&#xff0c;社媒营销如果做得好&#xff0c;引流效果好的账号&#xff0c;可以用来带货变现&#xff0c;而外贸、品牌出海也同样都在做社媒营销&#xff0c;Tik Tok、facebook、ins等热门的海外社媒平台都是行业密切关注的&#xff0c;必要的时候&#xff0c;大家会使用海…

Redis从入门到精通(十九)多级缓存(四)Nginx共享字典实现本地缓存

文章目录 前言6.5 实现多级缓存6.5.6 Nginx本地缓存6.5.6.1 代码实现6.5.6.2 功能测试 6.6 缓存同步6.6.1 缓存同步策略6.6.2 异步通知策略 前言 Redis多级缓存系列文章&#xff1a; Redis从入门到精通(十六)多级缓存(一)Caffeine、JVM进程缓存 Redis从入门到精通(十七)多级缓…

05节-51单片机-模块化编程

1.两种编程方式的对比 传统方式编程&#xff1a; 所有的函数均放在main.c里&#xff0c;若使用的模块比较多&#xff0c;则一个文件内会有很多的代码&#xff0c;不利于代码的组织和管理&#xff0c;而且很影响编程者的思路 模块化编程&#xff1a; 把各个模块的代码放在不同的…

数字革命的先锋:Web3对社会的影响

引言 在信息技术飞速发展的当下&#xff0c;Web3作为一个新兴的互联网模式&#xff0c;正在逐渐改变我们的生活方式、商业模式和社会结构。本文将深入探讨Web3的核心特点、它在各个领域中的应用以及对社会产生的深远影响。 1. Web3的核心特点 1.1 去中心化 Web3强调去中心化…