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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PTA图论的搜索题

目录 7-1 列出连通集 题目 输入格式: 输出格式: 输入样例: 输出样例: AC代码 7-2 六度空间 题目 输入格式: 输出格式: 输入样例: 输出样例: 思路 AC代码 7-3 地下迷宫探索 题目 输入格式: 输出格式: 输入样例1: 输出样例1: 输入样例2: 输出样例2: 思路 …

MySQL 试图

视图功能在 5.0 以后的版本启用 视图是一张虚表。数据表确实包含了具体数据并且保存到硬盘中的实表。视图使用数据检索语句动态生 成的一张虚表。每一次数据服务重启或者系统重启之后&#xff0c;在数据库服务启动期间&#xff0c;会使用创建视图的语 句重新生成视图中的数据&…

这家物流装备公司突破天际:销售额飙升至10亿美元,引领仓储机器人革命!...

导语 大家好&#xff0c;我是智能仓储物流技术研习社的社长&#xff0c;老K。专注分享智能仓储物流技术、智能制造等内容。 新书《智能物流系统构成与技术实践》 法国的Exotec公司在仓储自动化领域取得了显著成就&#xff0c;其销售额已超过10亿美元&#xff0c;成为全球物料搬…

考研数学|《1800》《1000》《660》《880》如何搭配❓

这几本书都是不同阶段对应的习题册 我觉得最舒服的使用就是方式就是基础阶段用《1800题基础部分》然后强化阶段主要刷《880题》并且强化阶段带着刷《660题》 上面是我的使用方式。之所以没有刷《1000题》是因为这本习题册的难度对我来说还是太大了&#xff0c;并且计算量很大…

上海计算机学会 2023年10月月赛 乙组T3 树的连通子图(树、树形dp)

第三题&#xff1a;T3树的连通子图 标签&#xff1a;树、树形 d p dp dp题意&#xff1a;给定一棵 n n n个结点的树&#xff0c; 1 1 1号点为这棵树的根。计算这棵树连通子图的个数&#xff0c;答案对 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007取余数。题解&#xff1…

HTML内联框架

前言&#xff1a; 我们有时候打开网页时会有广告窗的出现&#xff0c;而这些窗口并不是来自于本站的&#xff0c;而是来自于外部网页&#xff0c;只是被引用到了自己网页中而已。这一种技术可以通过内联来实现。 标签介绍&#xff1a; HTML 内联框架元素 (<iframe>) 表示…

基于Springboot的影城管理系统

基于SpringbootVue的影城管理系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页展示 电影信息 电影资讯 后台登录页 后台首页 用户管理 电影类型管理 放映…

RAG (Retrieval Augmented Generation) 结合 LlamaIndex、Elasticsearch 和 Mistral

作者&#xff1a;Srikanth Manvi 在这篇文章中&#xff0c;我们将讨论如何使用 RAG 技术&#xff08;检索增强生成&#xff09;和 Elasticsearch 作为向量数据库来实现问答体验。我们将使用 LlamaIndex 和本地运行的 Mistral LLM。 在开始之前&#xff0c;我们将先了解一些术…

vue3 生命周期(生命周期钩子 vs 生命周期选项 vs 缓存实例的生命周期)

vue3 支持两种风格书写&#xff1a;选项式 API 和组合式 API 若采用组合式 API &#xff0c;则使用生命周期钩子若采用选项式 API &#xff0c;则使用生命周期选项两者选用一种即可&#xff0c;不建议同时使用&#xff0c;避免逻辑紊乱。 生命周期钩子 在 setup 中使用 onBefo…

Vue 阶段练习:记事本

将 Vue快速入门 和 Vue 指令的学习成果应用到实际场景中&#xff08;如该练习 记事本&#xff09;&#xff0c;我们能够解决实际问题并提升对 Vue 的技能掌握。 目录 功能展示 需求分析 我的代码 案例代码 知识点总结 功能展示 需求分析 列表渲染删除功能添加功能底部统计…

3D目标检测实用技巧(二)- 实现点云(or 体素)向图像平面的投影并可视化

一、引言 受Focals Conv的启发&#xff0c;该论文中通过将点云投影到图片中清晰展现出点云学习后的情况&#xff1a; 本次实现的是体素向图像投影并显示&#xff0c;实现出来的效果如下&#xff1a; 二、 实现细节 1、体素投影到图像坐标系 这里我们参考的是VirConv的投影函…

通过matlab分别对比PSO,反向学习PSO,多策略改进反向学习PSO三种优化算法

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 粒子群优化算法 (PSO) 4.2 反向学习粒子群优化算法 (OPSO) 4.3 多策略改进反向学习粒子群优化算法 (MSO-PSO) 5.完整程序 1.程序功能描述 分别对比PSO,反向学习PSO,多策略改进反向学…

百货商场用户画像描绘与价值分析

目录 内容概述数据说明实现目标技术点主要内容导入模块1.项目背景1.1 项目背景与挖掘目标 2.数据探索与预处理2.1 结合业务对数据进行探索并进行预处理2.2 将会员信息表和销售流水表关联与合并 3 统计分析3.1 分析会员的年龄构成、男女比例等基本信息3.2 分析会员的总订单占比&…

Python 入门指南(四)

原文&#xff1a;zh.annas-archive.org/md5/97bc15629f1b51a0671040c56db61b92 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十章&#xff1a;哈希和符号表 我们之前看过列表&#xff0c;其中项目按顺序存储并通过索引号访问。索引号对计算机来说很有效。它们是整…

使用 Docker 部署 SurveyKing 调查问卷系统

1&#xff09;SurveyKing 介绍 SurveyKing 是一款功能强大、操作简便的开源问卷系统。它不仅满足了用户对问卷调查的基本需求&#xff0c;还提供了丰富的逻辑设置和灵活的问题设置&#xff0c;使得问卷制作更加智能化和个性化。此外&#xff0c;SurveyKing 还具有快速部署和安全…

笔记本电脑上的聊天机器人: 在英特尔 Meteor Lake 上运行 Phi-2

对应于其强大的能力&#xff0c;大语言模型 (LLM) 需要强大的算力支撑&#xff0c;而个人计算机上很难满足这一需求。因此&#xff0c;我们别无选择&#xff0c;只能将它们部署至由本地或云端托管的性能强大的定制 AI 服务器上。 为何需要将 LLM 推理本地化 如果我们可以在典配…

elmentui树形表格使用Sortable拖拽展开行时拖拽bug

1、使用elemntui的el-table使用Sortable进行拖拽&#xff0c;如下 const el this.$el.querySelector(.el-table__body-wrapper tbody) Sortable.create(el, {onEnd: (event) > {const { oldIndex, newIndex } event//拿到更新前后的下标即可完成数据的更新} })2、但是我这…

docker 环境变量设置实现方式

1、前言 docker在当前运用的越来广泛&#xff0c;很多应用或者很多中间软件都有很多docker镜像资源&#xff0c;运行docker run 启动镜像资源即可应用。但是很多应用或者中间件有很多配置参数。这些参数在运用过程怎么设置给docker 容器呢&#xff1f;下面介绍几种方式 2 、do…

Day91:API攻防-接口安全SOAPOpenAPIRESTful分类特征导入项目联动检测

目录 API分类特征-SOAP&OpenAPI&RESTful API分类特征 API常见漏洞 API检测流程 API检测项目-Postman&APIKit&XRAY 工具自动化-SOAP - WSDL Postman 联动burpxray APIKit插件(可联动xray) 工具自动化-OpenApi - Swagger Postman 联动burpxray APIKit…

HarmonyOS开发实例:【分布式邮件】

概述 基于TS扩展的声明式开发范式编程语言编写的一个分布式邮件系统&#xff0c;可以由一台设备拉起另一台设备&#xff0c;每次改动邮件内容&#xff0c;都会同步更新两台设备的信息。效果图如下&#xff1a; 搭建OpenHarmony开发环境 完成本篇Codelab我们首先要完成开发环境…