首页 > 其他分享 >FreeRTOS时间管理

FreeRTOS时间管理

时间:2024-04-16 21:24:02浏览次数:18  
标签:pxCurrentTCB FreeRTOS 管理 xTimeToWake 列表 任务 时间 else xConstTickCount

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()这个简单的延时函数而言的。

标签:pxCurrentTCB,FreeRTOS,管理,xTimeToWake,列表,任务,时间,else,xConstTickCount
From: https://www.cnblogs.com/bathwind/p/18139217

相关文章

  • 31.SpringBoot项目_员工管理系统总结 下
    本来想好好整理最后结束的奈何又接了个项目一个挺大的项目更能运用所以时间不够就简单整理算烂了个尾吧首先:1.人工AI取代程序员对我来说是不对的确实是骗外行的但其确实强大带来的便利但也只有程序员才能完美部署并运用它2.有多少人像我一样不喜欢wordexcelppt喜......
  • C:\Windows\servicing\Packages 是一个存储 Windows 更新程序包的目录。Windows 操
    C:\Windows\servicing目录包含了与Windows维护和更新相关的文件和子目录。让我们逐个解释一下每个子目录和文件的作用:CbsApi.dll和CbsMsg.dll:这两个DLL文件是Windows组件基础服务(CBS)的一部分。CBS是Windows中用于安装、卸载、维护和更新组件的服务。这些D......
  • 节省时间和资源:了解如何最大化渲染农场的排队管理效率
    ​在3D渲染领域,时间的价值无可替代。随著3D艺术家与制作工作室不断挑战技术极限,对高效计算资源的渴求空前增长,渲染农场因此成为了渲染任务中不可或缺的力量。其核心在于排队系统——这一动态且复杂的结构负责安排和最优化渲染任务的执行顺序与时间,确保了渲染效率和资源的充分利用......
  • vbs提示没有在该机执行windows脚本宿主的权限。请与系统管理员联系
    <p>最近在项目中使用VBS来实现图片的批量删除和批量导入功能,但不知道为什么,只要在我机器上一运行VBS文件就提示“没有在该机执行windows脚本宿主的权限。请与系统管理员联系。”的错误。下面贴出本人的解决方法,并附上图片批量导入及批量删除的VBS代码。</......
  • 自动以管理员身份运行批处理bat文件(vbs与bat两种方法)
    <p>但是,批处理脚本不会默认已管理员身份运行,一般情况下,我会将脚本命名为"XXXXXX(请右键点击,用管理员身份运行!).bat",但总有些用户会忽略这条提示,直接运行,此时由于权限不足,导致脚本运行失败。</p>我们可以使用一种变通的方法,让bat启动时,首先调用vbs脚本,......
  • allfiles.vbs 显示子目录下的所有文件的修改时间、大小、文件名、扩展名等
    <p>&#65279;有的时候将子目录下的所有文件的修改时间、大小、全限定名等信息导出到Excel表格中。</p>尝试过命令行,但不太好用——1.对于“dir/s>1.txt”,当前目录与文件列表是分开显示的,合并起来太麻烦,而且没有文件的全限定名。2.对于“dir/b/s>1......
  • 账号和权限管理
    账号和权限管理目录账号和权限管理一、用户账号和组账号的概述1、用户账号的分类2、组账号的分类3、标识号分类二、用户账号文件1、/etc/passwd1.1作用1.2字段分析2、/etc/shaow2.1作用2.2字段分析三、用户账号管理1、添加用户账号-useradd1.1作用1.2命令格式1.3常用选......
  • 获得Salesforce管理员认证,未来如何规划?看这8个职业方向!
    Salesforce最终用户构成了Salesforce就业市场的最大部分。AppExchange上约有3000家独立软件开发商(ISV),全球约有2000家Salesforce咨询公司,与Salesforce的15万名客户相比显得微不足道。每个Salesforce客户都有不同规模的团队,有些团队只有一名管理员,有些团队超过100人,团队角色包括项......
  • 西门子PLC数据类型1-位、位序列、整数、浮点数、日期时间
     本文摘于西门子官网内容一、位、位序列、整数、浮点数、日期时间基本数据类型:包括位、位序列、整数、浮点数、日期时间。此外字符也属于基本数据类型,请参见文档 String 与 WString。此外BCD码虽然不属于数据类型,但也是一种数字表示方式。1.1位和位序列注意:虽然位......
  • 修改 git log 显示的时间格式
    修改gitlog显示的日期时间格式目录修改gitlog显示的日期时间格式1、发现问题2、分析问题3、解决问题参考1、发现问题输入gitlog查看Gitcommit(提交)信息时,日期时间格式不是“年-月-日时:分:秒”❓.2、分析问题.使用vim编辑git的全局配置文件:gitconfi......