vTaskDelay()和vTaskDelayUntil()

一、概述

FreeRTOS提供了两个系统延时函数:相对延时函数vTaskDelay()和绝对延时vTaskDelayUntil()。

这两个延时函数和自己实现的延时函数不同,这两个延时函数一旦被调用,当前任务会立马进入阻塞状态,而自己写的延时函数(以for循环等形式实现的软件延时)会被当做有效任务而一直执行。

  • 相对延时是指每次延时都是从任务执行函数vTaskDelay()开始,延时指定的时间结束;
  • 绝对延时是指每隔指定的时间,执行一次调用vTaskDelayUntil()函数的任务。换句话说:任务以固定的频率执行。

二、vTaskDelay()

调用vTaskDelay()函数后,任务会进入阻塞状态,vTaskDelay()函数的参数xTicksToDelay表示延时多少个系统节拍时钟周期。在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelay 必须设置成1,此函数才能有效。

const portTickType xDelay = pdMS_TO_TICKS(500);
vTaskDelay(xDelay); 

vTaskDelay(pdMS_TO_TICKS(500)); //延迟500ms

vTaskDelay()指定的延时时间是从调用vTaskDelay()后开始计算的相对时间。

比如vTaskDelay(100),那么从调用vTaskDelay()后,任务进入阻塞状态,经过100个系统时钟节拍周期,任务解除阻塞。因此,vTaskDelay()并不适用与周期性执行任务的场合。此外,其它任务和中断活动,会影响到vTaskDelay()的调用(比如调用前高优先级任务抢占了当前任务),因此会影响任务下一次执行的时间。

三、vTaskDelayUntil()

API函数vTaskDelayUntil()可用于固定频率的延时,它用来延时一个绝对时间。
在文件FreeRTOSConfig.h中,宏INCLUDE_vTaskDelayUntil 必须设置成1,此函数才有效。

这个函数不同于vTaskDelay()函数的一个重要之处在于:vTaskDelay()指定的延时时间是从调用vTaskDelay()之后(执行完该函数)开始算起的,而vTaskDelayUntil()指定一个绝对时间,每当时间到达,则解除任务阻塞。

void vTaskDelayUntil( TickType_t * pxPreviousWakeTime, TickType_t xTimeIncrement );
参数说明
pxPreviousWakeTime指针,指向一个变量(指针说明这个变量即可以当做输入类型的,也可以当做输出类型的)。该变量保存任务最后一次解除阻塞的时间。第一次使用前,该变量必须初始化为当前时间。之后这个变量会在vTaskDelayUntil()函数内自动更新
xTimeIncrement周期循环时间。当时间等于(*pxPreviousWakeTime + xTimeIncrement)时,任务解除阻塞。如果不改变参数xTimeIncrement的值,调用该函数的任务会按照固定频率执行。

如果指定的唤醒时间已经达到,vTaskDelayUntil()立刻返回(不会有阻塞)。因此,使用vTaskDelayUntil()周期性执行的任务,无论任何原因(比如,任务临时进入挂起状态)停止了周期性执行,使得任务少运行了一个或多个执行周期,那么需要重新计算所需要的唤醒时间。这可以通过传递给函数的指针参数pxPreviousWake指向的值与当前系统时钟计数值比较来检测,在大多数情况下,这并不是必须的。

使用示例:

总共有四个任务:

idle task:优先级0

task1 :coutinuous task,优先级1

task2:coutinuous task,优先级1

task3:使用vTaskDelayUntil函数的周期性任务,优先级2

其中,task1、task2和task3的任务内容如下:

void vPeriodicTask( void *pvParameters )//task3
{
    TickType_t xLastWakeTime;
    const TickType_t xDelay3ms = pdMS_TO_TICKS( 3 );
    xLastWakeTime = xTaskGetTickCount();
    for( ;; )
    {
         vPrintString( "Periodic task is running\r\n" );

         vTaskDelayUntil( &xLastWakeTime, xDelay3ms );
    } 
}

void vContinuousProcessingTask( void *pvParameters )//task2  task1
{
    char *pcTaskName;

    pcTaskName = ( char * ) pvParameters;
    for( ;; )
    {

        vPrintString( pcTaskName );
    }
 }

直接结果:

 分析:

 

四、总结

vTaskDelayUntil函数比vTaskDelay函数多了一个记录任务本次被唤醒的时刻的变量,因此如果想要实现控制任务能够周期性运行的话,vTaskDelayUntil函数是一种比较简单的方法。

对于绝对延时函数,如果任务不是最高优先级,则仍然能周期性的将任务解除阻塞,但是解除阻塞的任务不一定能获得CPU权限,因此任务主体代码也不会总是精确周期性执行。

如果要想精确周期性执行某个任务,可以使用系统节拍钩子函数vApplicationTickHook(),它在系统节拍中断服务函数中被调用,因此这个函数中的代码必须简洁。

 

ref:

https://blog.csdn.net/zhzht19861011/article/details/51705148

https://blog.csdn.net/zhzht19861011/article/details/50454591

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值