本文内容参考:
https://www.freertos.org/zh-cn-cmn-s/a00127.html
特此致谢!
在ESP-IDF项目中用到了延时。如果是在Keil、尤其是STM32接口函数中,笔者很熟悉,是delay_ms函数或者在HAL库中是HAL_Delay函数;如果是Linux应用中,笔者也很熟悉,是sleep函数。唯独在这个ESP-IDF中,由于笔者是刚接触不久,因此虽然查到了对应的延时函数是vTaskDelay函数,但是对于其细节还不甚了解。因此,特意写此文章,对于自己是学习,多余后来者则是解析。
实际上,ESP-IDF中使用的这个vTaskDelay函数也并不是它自创的,而是引用的FreeRTOS中的函数。vTaskDelay函数的细节如下:
函数原型
vTaskDelay函数在FreeRTOS的task.h中,如下:
void vTaskDelay( const TickType_t xTicksToDelay );
注意:在FreeRTOS配置中,必须将 INCLUDE_vTaskDelay 定义为 1,此函数才可用。
函数功能
vTaskDelay()会指定任务想要取消阻塞的时间,该时间是相对于vTaskDelay()被调用的时间。 例如,如果指定时间块为100个tick,那么在调用vTaskDelay()的100个tick后任务会取消阻塞。
注意:vTaskDelay()并不会因此提供一种控制周期性任务频率的好办法,因为途径代码的路径以及其它任务和中断活动将影响vTaskDelay()被调用的频率,进而会影响下一个任务执行的时间。
函数参数
- xTicksToDelay —— 调用任务应阻塞的tick周期数。
函数返回值
无。
用法示例
void vTaskFunction( void * pvParameters )
{/* Block for 500ms. */const TickType_t xDelay = 500 / portTICK_PERIOD_MS;for( ;; ){/* Simply toggle the LED every 500ms, blocking between each toggle. */vToggleLED();vTaskDelay( xDelay );}
}
vTaskDelay()按给定的tick数延迟任务。任务保持阻塞的实际时间取决于tick频率。 常量 portTICK_PERIOD_MS配合tick周期分辨率可用于从tick频率计算实际时间。
在ESP-IDF中,portTICK_PERIOD_MS的值为:
#define portTICK_PERIOD_MS ((TickType_t) (1000 / configTICK_RATE_HZ))
这里就引出来了一个问题:pertTICK_PERIOD_MS是延时1毫秒的值,那么如果延时x秒,是应该乘以这个值还是除以这个值?
官方的例程以及绝大多数代码中都是除以pertTICK_PERIOD_MS,也就是说正确的用法是除而不是乘。比如,想要延时2秒,就要vTaskDelay(2000/portTICK_PERIOD_MS);延时1秒则是vTaskDelay(1000/portTICK_PERIOD_MS)。
有人(包括笔者)会有疑问:既然portTICK_PERIOD_MS是时间单位,代表1毫秒对应的值,那么X秒不应该是乘法吗,即X * 1000 * portTICK_PERIOD_MS。笔者也正是为了解开此疑问才写的本篇文章。这里笔者推演一下:
先要弄清楚一点,实际上portTICK_PERIOD_MS的意义并不是毫秒,而是代表了1个Tick对应了多少毫秒(比如,常见的交流电为50Hz,那么1Hz就对用了0.02秒,即20毫秒)。
既然vTaskDelay函数的参数xTicksToDelay的单位是Tick,也就是时钟节拍,那么configTICK_RATE_HZ就是延时1秒所对应的Tick个数值,就是说走configTICK_RATE_HZ个节拍对应的时间是1秒。注意,这里configTICK_RATE_HZ的单位是个即频率,而其倒数则表示时间,即1个tick所用的时间。
既然走configTICK_RATE_HZ个Tick所对应的时间是1秒,那么自然地,走(configTICK_RATE_HZ/1000)个Tick所对应的时间是1毫秒,其倒数(1000/configTICK_RATE_HZ)自然就是时间单位1毫秒,而这正是portTICK_PERIOD_MS的定义:
#define portTICK_PERIOD_MS ((TickType_t) (1000 / configTICK_RATE_HZ))
既然走(configTICK_RATE_HZ/1000)个Tick所对应的时间是1毫秒,那么走1秒所对应的Tick数自然是(configTICK_RATE_HZ/1000) *1000,走X秒所对应的Tick数是X * (configTICK_RATE_HZ/1000) *1000,即X * 1000 * (configTICK_RATE_HZ/1000)。代入portTICK_PERIOD_MS,则为X * 1000 * (1/portTICK_PERIOD_MS),就是X * 1000 / portTICK_PERIOD_MS。
这就是应该为除法而不是乘法的原因。