FreeRTOS时间管理

news2025/1/13 7:53:43

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,
    确保当任务被移动到延迟列表时,任何之前的延迟中止请求都被清除。*/
    #endif
    if( 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.coloradmin.cn/o/1609281.html

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

相关文章

springcloud第4季 springcloud-alibaba之sentinel

一 sentinel介绍 1.1 sentinel作用 sentinel是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障服务的稳定性。 1.2 组成部分 sen…

A Geolocation Databases Study(2011年)第五部分:Evalution Model

下载地址:A Geolocation Databases Study | IEEE Journals & Magazine | IEEE Xplore 被引次数:195 Shavitt Y, Zilberman N. A geolocation databases study[J]. IEEE Journal on Selected Areas in Communications, 2011, 29(10): 2044-2056. 5. Discussion 在我们讨…

Jenkins的安装和部署

文章目录 概述Jenkins部署项目的流程jenkins的安装启动创建容器进入容器浏览器访问8085端口 Jenkins创建项目创建example项目 概述 Jenkins&#xff1a;是一个开源的、提供友好操作界面的持续集成&#xff08;CLI&#xff09;工具&#xff0c;主要用于持续、自动构建的一些定时…

什么是知乎知+广告推广?

知乎作为中国领先的知识分享社区和高质量用户群体汇聚地&#xff0c;其广告价值日益凸显&#xff0c;其中&#xff0c;“知”作为知乎官方推出的创新广告形式&#xff0c;正逐渐成为品牌与消费者深度连接的重要桥梁。知广告推广不仅局限于传统意义上的硬性推广&#xff0c;更强…

C语言中与内存操作有关的一些函数

前提 最近在使用C语言在开发项目时&#xff0c;要对内存进行操作。刚开始写的时候有一点迷糊&#xff0c;看了一些东西后才发现为什么说指针是C语言的灵魂&#xff0c;因为它可以对内存直接进行操作&#xff0c;多么帅的事情&#xff0c;真的是太帅了。 malloc 声明在头文件…

混合现实(MR)开发框架

混合现实&#xff08;MR&#xff09;开发框架为开发者提供了构建MR应用程序所需的基本工具和功能。它们通常包括3D引擎、场景图、输入系统、音频系统、网络功能以及支持同时处理现实世界和虚拟世界信息的功能。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&…

【C语言】深入解析选择排序算法

一、算法原理二、算法性能分析三、C语言实现示例四、总结 一、算法原理 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法。它的工作原理是不断地选择剩余元素中的最小&#xff08;或最大&#xff09;元素&#xff0c;放到已排序的序列的末尾&#xff…

windows10环境下conda迁移到linux环境

网上给出的方案错误百出&#xff0c;记录一下正确方案。 1 创建yaml文件 创建到终端所在路径下 conda activate 环境名 conda env export --no-build >环境名.yaml2 新操作系统中创建新的conda环境 conda env create -f 环境名.yaml3 删除不兼容的包 终端报错 Could n…

西宁市初中生地会考报名照片尺寸要求及手机自拍方法

西宁市初中生地会考即将到来&#xff0c;对于参加考试的同学们来说&#xff0c;准备一张符合规格的报名照片是整个报名流程中不可或缺的一环。一张规范的证件照不仅展示了学生的精神面貌&#xff0c;同时也是顺利报名的重要条件之一。本文将详细介绍西宁市初中生地会考报名所需…

基于Springboot的社区帮扶对象管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的社区帮扶对象管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

可视化看板有那么多应用场景,该如何快速搭建?可视化工具该如何选择?

在当今的信息化时代&#xff0c;数据已经成为了现代决策的核心。无论是企业战略规划、运营管理&#xff0c;还是个人生活决策&#xff0c;数据都扮演着至关重要的角色。随着数据分析技术和工具的不断进步&#xff0c;数据在决策中的作用将变得更加突出&#xff0c;对组织和个人…

Stable Diffusion 模型分享:ChilloutMix(真实、亚洲面孔)chilloutmix_NiPrunedFp32Fix

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 相信近来吸引大家想一试 Stable Diffusion 图像生…

查看linux的主机配置脚本

废话不说 直接上指令 curl -Lso- bench.sh | bash 等待后&#xff0c;结果如图&#xff1a; 使用后没有问题&#xff0c;看情况使用 出事概不负责 介意勿用&#xff01;&#xff01;&#xff01;

实验室三大常用仪器3---交流毫伏表的使用方法(笔记)

目录 函数信号发生器、示波器、交流毫伏表如果连接 交流毫伏表的使用方法 测量值的读数问题 实验室三大常用仪器1---示波器的基本使用方法&#xff08;笔记&#xff09;-CSDN博客 实验室三大常用仪器2---函数信号发生器的基本使用方法&#xff08;笔记&#xff09;-CSDN博客…

H5 台球猜位置小游戏

刷到抖音有人这样玩&#xff0c;就写了一个这样的小游戏练习一下H5的知识点。 小游戏预览 w(&#xff9f;Д&#xff9f;)w 不开挂越急越完成不了&#xff0c;&#x1f47f;确认15次也没全对… 知识点 获取坐标位置的DOM元素&#xff0c;感觉应该是新的吧&#xff0c;以前的…

基于STM32F103RCT6最小系统原理图和PCB

目录 1、原理图 2、PCB 3、3D图 资料下载地址&#xff1a;基于STM32F103RCT6最小系统原理图和PCB 1、原理图 2、PCB 3、3D图

亚信安全入选中国数据安全市场图谱

近日&#xff0c;全球领先的IT市场研究和咨询公司IDC发布了《IDC Market Glance&#xff1a;中国数据安全市场图谱&#xff0c;2024》报告&#xff08;以下简称“报告”&#xff09;&#xff0c;报告展示了中国数据安全市场的构成和格局&#xff0c;遴选出不同细分市场领域的主…

vmware安装ubantu系统

镜像下载 官网地址 :https://ubuntu.com/ 历史版本下载 https://old-releases.ubuntu.com/releases/ 虚拟机系统安装 虚拟机上右键设置 选择ubantu的iso镜像 进入启动页 等待安装好系统 到此系统安装成功

【面试经典 150 | 二叉树层序遍历】二叉树的锯齿形层序遍历

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;层序遍历双端队列 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等…

决策树原理及应用

目录 一、决策树概述 1.1决策树的组成 1.2构建决策树 1.3决策树的剪枝 1.4决策树的优点和缺点 二、决策树在计算机视觉中的应用 三、基于决策树的图像分类实例 一、决策树概述 决策树是一种非常流行的机器学习算法&#xff0c;它用树状图的形式来表示决策过程。决策树可…