007任务以绝对频率运行vTaskDelayUntil
以下为作者本人关于esp32的FreeRtos学习笔记,供自己学习参考。
平台:Ubuntu 20.04 LTS + Arduino IDE
window平台上的代码完全一致,因为我自己电脑上的win11系统arduino编译速度过于缓慢(无法忍受(* ̄︿ ̄)),遂转到ubuntu。
vTaskDelayUntil函数详解
1. 函数功能:
vTaskDelayUntil
是FreeRTOS操作系统中的一个函数,用于实现任务的周期性执行。它使任务进入阻塞状态,直到指定的绝对时刻到来,任务才会重新变为就绪状态并有可能被执行。
2. 使用方法:
vTaskDelayUntil
函数接受两个参数:
- 第一个参数是一个指向
TickType_t
类型的指针,该指针存储了上一个唤醒时间。通常,在任务开始时,这个值会被初始化为当前的系统tick数,以确保任务可以立即执行。随着任务的周期性执行,这个值会被更新为下一个期望的唤醒时间。 - 第二个参数是一个
TickType_t
类型的值,表示从上一个唤醒时间开始需要等待的tick数。这个值通常以FreeRTOS的tick周期为单位,可以使用pdMS_TO_TICKS
宏将毫秒时间转换为tick数。
3. 工作原理:
当调用vTaskDelayUntil
函数时,任务会进入阻塞状态,并释放其占用的CPU资源。系统会根据提供的唤醒时间计算任务的下一个执行时间,并在该时间到达时将任务重新标记为就绪状态。如果系统中有其他任务处于就绪状态,并且它们的优先级高于该任务,那么它们将会先被执行。当系统调度器再次选择任务执行时,如果该任务已经到达其唤醒时间并且处于就绪状态,那么它将会被执行。
4. 优点与用途:
vTaskDelayUntil
函数的主要优点是它可以实现固定频率的延时,确保任务在指定的时间到达后执行。这使得它非常适合于需要周期性执行的任务,如传感器数据采集、定时任务等。此外,由于它使用绝对时间进行延时,因此不受其他任务和中断活动的影响,从而保证了任务执行的稳定性和可靠性。
5. 总结:
vTaskDelayUntil
是FreeRTOS中一个非常有用的函数,它可以帮助开发者精确控制任务的执行时间,实现任务的周期性执行。通过合理地使用vTaskDelayUntil
函数,可以优化任务的调度和执行效率,提高系统的整体性能。
按照以下结构来使用vTaskDelayUntil()
函数。
首先,需要确保FreeRTOS已经正确地在你的Arduino平台上安装和配置。这通常涉及到下载和安装适用于Arduino的FreeRTOS库,然后根据你的硬件配置修改FreeRTOS的配置文件。
示例
以下是在Arduino环境中使用vTaskDelayUntil()
函数的一个示例结构:
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
// 假设你已经配置了FreeRTOS,并设置了configTICK_RATE_HZ等参数 // 任务函数
void myTask(void *pvParameters) {
TickType_t xLastWakeTime = 0; // 初始化上次唤醒时间为0
const TickType_t xDelay = 1000 / portTICK_PERIOD_MS; // 延迟1秒
for (;;){
// 在这里执行你的任务代码
// ...
// 使用vTaskDelayUntil()进行延时
vTaskDelayUntil(&xLastWakeTime, xDelay);//这里xLastWakeTime会自动更新
}
}
void setup() {
// 初始化Arduino和其他必要的设置
// 创建并启动FreeRTOS任务
xTaskCreate(myTask, "MyTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
// 开始FreeRTOS调度器
vTaskStartScheduler();
// 注意:如果调度器正确启动,下面的代码将不会被执行
// 通常这里不需要任何代码,因为调度器会接管控制
}
void loop() {
/*在使用FreeRTOS的情况下,通常不需要Arduino的loop()函数,因为所有任务都由FreeRTOS调度器管理*/
}
在这个示例中,myTask
函数是你的任务函数,它使用vTaskDelayUntil()
来进行延时。xLastWakeTime
变量被初始化为0,表示任务从未运行过。然后,任务进入一个无限循环,在每次迭代中执行一些代码,然后使用vTaskDelayUntil()
来延迟一段时间。
setup()
函数用于初始化Arduino和创建FreeRTOS任务。vTaskStartScheduler()
函数启动FreeRTOS调度器,之后任务将由调度器管理。
-
vTaskDelayUntil()
的自动更新-
当任务因为调用
vTaskDelayUntil()
而被挂起(进入阻塞状态)时,系统会记录下当前的时间(即任务被挂起的时间),并存储在xLastWakeTime
中。当任务的时间片到期,即达到了设定的延迟时间后,任务会被重新唤醒,并继续执行。此时,xLastWakeTime
会被自动更新为任务被唤醒时的系统时间。 -
这种自动更新的机制,使得
vTaskDelayUntil()
函数能够精确地控制任务的延迟时间。在每次调用vTaskDelayUntil()
时,任务会计算出下一次应该被唤醒的时间(即xLastWakeTime
加上设定的延迟时间),并在该时间到达时被唤醒。这种机制使得任务可以在需要的时候精确地进入休眠状态,从而节省CPU资源,提高系统的效率。 -
需要注意的是,
xLastWakeTime
的自动更新是由FreeRTOS系统内部实现的,开发者在编写任务代码时不需要手动去更新这个参数。只需要在调用vTaskDelayUntil()
时传入正确的xLastWakeTime
和延迟时间参数即可。
-
/*
程序: 绝对任务延迟
API:
vTaskDelayUntil(&xLastWakeTime, xFrequency)
最后一次的唤醒时间是指针类型。
本函数会自动更新xLastWakeTime为最后一次唤醒的时间
所以无需手动在while循环内对其手动赋值
xTaskGetTickCount()
Tick Coun 和 Arduino Millis一样
uint32_t类型 49天后overflow
*/
void showStockTask(void *ptParam) {
static float stockPrice = 99.57; //股票价格
//最后一次唤醒的tick count,第一次使用需要赋值
//以后此变量会由vTaskDelayUntil自动更新
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = 3000; // 间隔 3000 ticks = 3 seconds
for (;;) {
//恰恰算算,经过思考,既然我们叫做LastWakeTime,那么 vTaskDelayUntil 应该放在循环的第一句话
//如果放在循环的最后一句话,应该改为xLastSleepTime 才更加合适
// 看懂的朋友, 请鼓掌
// 哦,我无法听到掌声,干脆帮我按住 点赞三秒 对我的视频进行强力推荐吧
vTaskDelayUntil(&xLastWakeTime, xFrequency);
//验证当前唤醒的时刻tick count
Serial.println(xTaskGetTickCount());
//验证xLastWake Time是否被vTaskDelayUntil更新
//Serial.println(xLastWakeTime);
// ------- 很复杂的交易股票计算,时间不定 ---------
stockPrice = stockPrice * (1 + random(1, 20) / 100.0);
vTaskDelay(random(500, 2000));
Serial.print("Stock Price : $");
Serial.println(stockPrice);
//使用vTaskDelay试试看会如何
//vTaskDelay(xFrequency);
}
}
void setup() {
Serial.begin(115200);
xTaskCreate(showStockTask, "Show Stock Price", 1024 * 6, NULL, 1, NULL);
}
void loop()
{
}
笔记参考B站大佬 孤独的二进制 的视频教程ESP32 FreeRTOS