函数vTaskDelayUntil()是如何做到固定周期延时的?

博主联系方式:QQ:1256153255 ,邮箱:1256153255@qq.com

点击这里可申请RH850开发板

最直接的办法就是从函数vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,const TickType_t xTimeIncrement )的源码入手分析

#if ( INCLUDE_vTaskDelayUntil == 1 )

    void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
                          const TickType_t xTimeIncrement )
    {
        TickType_t xTimeToWake;
        BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

        configASSERT( pxPreviousWakeTime );
        configASSERT( ( xTimeIncrement > 0U ) );
        configASSERT( uxSchedulerSuspended == 0 );
        
		/*通过挂起来创建临界区,保证suspend和resume之间不会切换到其他任务*/
        vTaskSuspendAll();
        {
            /* 在这部分代码块中,tick count是不允许变化的。 
               保存系统节拍中断次数计数器 */
            const TickType_t xConstTickCount = xTickCount;

            /* 计算任务下次唤醒时间(以系统节拍中断次数表示)   */
            xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
			/* *pxPreviousWakeTime中保存的是上次唤醒时间,唤醒后需要一定时间执行任务主体代码,如果上次唤醒时间大于当前时间,说明节拍计数器溢出了 */
            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();
        }
    }

#endif /* INCLUDE_vTaskDelayUntil */

其实保证固定挂起时长的关键点就是这句代码

prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );

从源码中我们知道
xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
xConstTickCount = xTickCount;
而pxPreviousWakeTime用于指向一个变量,变量保存上次任务解除阻塞的时间。
也就是说
pxPreviousWakeTime :表示上次任务解除阻塞时间
xTimeIncrement:表示任务挂起时长
xTimeToWake :表示任务下次唤醒时间
xConstTickCount:表示当前系统节拍中断次数
我们从下图开始分析
在这里插入图片描述
①调度器启动,第1次调用vTaskDelayUntil(),任务A进入阻塞。
此时pxPreviousWakeTime 等于当前系统节拍中断计数器值xTickCount记为pxPreviousWakeTime1;xConstTickCount 也等于当前系统节拍中断计数器值xTickCount记为xConstTickCount1。
在函数vTaskDelayUntil()中计算出了下一次任务A唤醒时间xTimeToWake = *pxPreviousWakeTime + xTimeIncrement记为xTimeToWake1,此时pxPreviousWakeTime指向xTimeToWake1记为pxPreviousWakeTime2。
在函数vTaskDelayUntil()中调用prvAddCurrentTaskToDelayedList( xTimeToWake1 - xConstTickCount1, pdFALSE )来计算任务A延时时长,由上图可知此时阻塞时长等于延时时长。

阻塞时长 = 任务A下一次唤醒时刻 - 任务A上一次唤醒时刻
         = xTimeToWake1 - pxPreviousWakeTime1
         = pxPreviousWakeTime1 + xTimeIncrement - pxPreviousWakeTime1
         = xTimeIncrement
同时
阻塞时长 = 延时时长 + 任务A执行时长 = 延时时长

②阻塞时间到,任务A占用CPU开始执行
任务A执行完之后,第2次调用vTaskDelayUntil(),任务A进入阻塞。此时
xConstTickCount为当前系统节拍中断计数器值,记为xConstTickCount2
xTimeToWake记为xTimeToWake2
在函数vTaskDelayUntil()中调用prvAddCurrentTaskToDelayedList( xTimeToWake2 - xConstTickCount2, pdFALSE )来计算任务A延时时长

阻塞时长 = 任务A下一次唤醒时刻 - 任务A上一次唤醒时刻
         = xTimeToWake2 - pxPreviousWakeTime2
         = pxPreviousWakeTime2 + xTimeIncrement - pxPreviousWakeTime2
         = xTimeIncrement
同时
阻塞时长 = 延时时长 + 任务A执行时长
         = (xTimeToWake2 - xConstTickCount2)+ (xConstTickCount2 - pxPreviousWakeTime2)
         = xTimeToWake2 - pxPreviousWakeTime2
         = xTimeIncrement

③当任务A在执行过程中有中断或更高优先级任务抢占了CPU的使用权,怎任务A被挂起,待中断或更高优先级任务执行完成,再继续执行任务A
此时

阻塞时长 = 任务A下一次唤醒时刻 - 任务A上一次唤醒时刻
         = xTimeToWake3 - pxPreviousWakeTime3
         = pxPreviousWakeTime3 + xTimeIncrement - pxPreviousWakeTime3
         = xTimeIncrement
同时
阻塞时长 = 延时时长 + (任务A执行时长 + 中断执行时长)
         = (xTimeToWake3 - xConstTickCount3)+ (xConstTickCount3 - pxPreviousWakeTime3)
         = xTimeToWake3 - pxPreviousWakeTime3
         = xTimeIncrement

通过以上分析可知,使用vTaskDelayUntil()将任务A延时,最终延时时长都是xTimeIncrement,也就做到了固定周期延时。
在这个过程中任务A的阻塞时长是固定的,任务A的执行时长也是固定的,中断或其他更高优先级任务的执行时间是不固定的,因此其他任务的执行时间被占用,导致其他任务的执行时长缩短。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值