浅析 FreeRTOS SysTick 和任务延时
概述
FreeRTOS 提供的最小时间单元为一个 SysTick,举例:
假设配置 RTOS 的 SysTick 为 100Hz,则 RTOS 能提供的最小时间单位为 1/100 s,即 10ms. 即一个 RTOS 的系统时钟为 10ms.
FreeRTOS 自带了一个 SysTick 计数器,任务调度器启用后,每个 SysTick 发生,该计数器就加一。可以通过下述函数获取任务调度器当前运行了几个 SysTick:
TickType_t xTaskGetTickCount(void)
通常,这个 API 可以用来测试 TaskCode 中的一段代码的运行时间:
TickType_t xLastWakeTime;
xLastWakeTime = xTaskGetTickCount ();
// dosomethings
code();
xLastWakeTime = xTaskGetTickCount () - xLastWakeTime;
上述示例计算 code()
共消耗了多少个 SysTick,假设当前的 SysTick 为 上述的 100Hz,当前消耗了 10个 SysTick,则实际消耗时间为 10* 10ms = 100ms.
FreeRTOS 中提供了一个宏来完成 SysTick 到 毫秒单位的转换:
#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000U ) )
将 10 SysTick 转为 ms 单位,使用 pdMS_TO_TICKS(10)
即可。
要改变RTOS 的 SysTick 的大小,可以改变宏 configTICK_RATE_HZ
的值,在 ESP32 的开发环境中,使用 idf.py menuconfig
命令在下述图形界面改变 SysTick 的值即可。
注意,SysTick 决定了 RTOS 的最小时间单位,因此,当 SysTick 为 100Hz 时,最小时间单位为 10ms,比这更小的时间需求,如延时,定时需求将不容易通过 RTOS 的系统接口实现。增大 SysTick 提高时间的精确度诚然可行,但当 SysTick 过大,系统的运算需求和负载(中断切换时间)也将变大,可能导致设备变热和运行问题。
在了解 SysTick 的基础上,我们可以研究 RTOS 两种延时函数的使用和异同:
1)相对延时:
void vTaskDelay(const TickType_t xTicksToDelay) // 参数为要延时的 SysTick 数目
2)绝对延时:
BaseType_t vTaskDelayUntil(TickType_t *const pxPreviousWakeTime, const TickType_t xTimeIncrement) // 参数为要延时起始的 SysTick 时刻,和相对起始时刻的时间间隔
两者都可以实现延时,进入延时后的函数将进入休眠状态,等指定的几个 SysTick 后,任务会被唤醒,重新进入可执行的就绪状态。注意,唤醒后的任务并不一定是立即运行的,只是进入一个就绪状态,只有其优先级足够高,才能被任务调度器赋予其 CPU 使用权,进入运行状态。
两者的不同:
1)传的参数不同。
2)进入休眠状态的时机不一样:
vTaskDelay(2) 在执行时,假设期望在 Tick2 得到执行,但此时 CPU 被其他任务抢占,则实际 vTaskDelay(2) 并没有在 Tick2 得到执行,而是在该任务重庆获取 CPU 后的 Tick4 成功调用 vTaskDelay(2) ,之后延时两个 SysTick,在Tick6 被唤醒。
vTaskDelayUntil(2,2) 则不同,它可以指定计算 SysTick 的起始时刻,指定其开始的 SysTick 为 Tick2 后,其一定在 Tick4 得到唤醒。 因此,后者延时的准确性、可控制性更好。
需求及功能解析
示例延时了两种延时的区别:
1)vTaskDelay():
static void task1_process(void *arg)
{
static const char *TASK1_TAG = "TASK1";
TickType_t last_wake_time = 0;
TickType_t ticks_before_delay = 0;
ticks_before_delay = xTaskGetTickCount();
while (1) {
esp_rom_delay_us(100*1000); // 模拟有其他任务或者系统中断抢占 CPU,导致延迟进入 vtaskdelay()
task1_flag++;
vTaskDelay(pdMS_TO_TICKS(1000));
last_wake_time = xTaskGetTickCount();
printf("%s: tick used=%dms\r\n", TASK1_TAG, TICKS_TO_MS(last_wake_time-ticks_before_delay));
ticks_before_delay = last_wake_time;
}
}
2)vTaskDelayUntil():
static void task2_process(void *arg)
{
static const char *TASK2_TAG = "TASK2";
TickType_t ticks_before_delay;;
TickType_t last_wake_time = 0;
last_wake_time = xTaskGetTickCount();
ticks_before_delay = last_wake_time;
while (1) {
esp_rom_delay_us(100*1000); // 模拟有其他任务或者系统中断抢占 CPU
task2_flag++;
vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(1000)); // 注意,使用后,last_wake_time 的值会被更新为当下的系统 systick.
printf("%s: tick used=%dms\r\n", TASK2_TAG, TICKS_TO_MS(xTaskGetTickCount() - ticks_before_delay));
ticks_before_delay = last_wake_time;
}
vTaskDelete(NULL);
}
示例解析
示例输出:
This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, Minimum free heap size: 294424 bytes
TASK2: tick used=1000ms
TASK1: tick used=1100ms
TASK3: use time=1095764us
TASK2: tick used=1000ms
TASK1: tick used=1100ms
TASK3: use time=1099870us
示例中使用 esp_rom_delay_us(100*1000) 模拟有其他任务或者系统中断抢占 CPU的情况,该函数实际是一个 while 实现的让 CPU 空转指定时间的函数。
当希望实现延时为 1000ms 的延时时。与上述的延时原理一致,使用vTaskDelayUntil() 的 Task2 每次运行的 tick used = 1000ms,即实现了较为准确的周期为 1000ms 的延时执行。而使用 vTaskDelay() 的 Task1 则因为进入睡眠的延时,导致实际延时为1100ms。
最后,Task3 演示了vTaskDelay() 中使用的参数的单位为几个 SysTick,并不是实际的 ms 单位。使用 esp_timer_get_time()
可以获得较为准确的系统时间。
讨论
尝试改变 ESP32 的 SysTick,观察系统延时精度及上诉函数返回值的变化。
总结
1)FreeRTOS 提供的最小时间单元为一个 SysTick,使用 xTaskGetTickCount() 可以获得自任务调度器启用后,系统一共运行了几个 SysTick 了。
2)FreeRTOS 提供了两种系统延时函数 vTaskDelay()、vTaskDelayUntil(),两者都可以实现延时,进入延时后的函数将进入休眠状态,等指定的几个 SysTick 后,任务会被唤醒,重新进入可执行的就绪状态。注意,唤醒后的任务并不一定是立即运行的,只是进入一个就绪状态,只有其优先级足够高,才能被任务调度器赋予其 CPU 使用权,进入运行状态。特别注意,两者进入睡眠的时机和真正延时时间上的区别。
3)SysTick 与 时间单位的转换可以使用宏 pdMS_TO_TICKS()
和 TICKS_TO_MS()
。
资源链接
1)Learning-FreeRTOS-with-esp32 系列博客介绍
2)对应示例的 code 链接 (点击直达代码仓库)文章来源:https://www.toymoban.com/news/detail-622297.html
3)下一篇:FreeRTOS 任务通知浅析文章来源地址https://www.toymoban.com/news/detail-622297.html
到了这里,关于浅析 FreeRTOS SysTick 和任务延时的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!