FreeRTOS 时间管理
1. 系统时钟节拍:FreeRTOS 的心跳
FreeRTOS 的时间管理基于系统时钟节拍(tick),它就像是 FreeRTOS 的心跳,为系统提供了最基本的时间单位。这个节拍由硬件定时器(通常是 SysTick)产生,FreeRTOS 会根据配置的节拍率(configTICK_RATE_HZ
)来设置定时器的中断频率。例如,如果 configTICK_RATE_HZ
设置为 1000,那么系统时钟节拍的周期就是 1 毫秒。
在 FreeRTOS 中,有一个全局变量 xTickCount
,它记录了系统自启动以来的时钟节拍数。每次时钟节拍中断发生时,xTickCount
的值就会加 1。
这个变量是 FreeRTOS 时间管理的核心,许多时间相关的函数都依赖于它。
2. 任务延时:
FreeRTOS 提供了两种主要的任务延时函数:
2.1 vTaskDelay
函数
vTaskDelay
函数用于让任务进入阻塞状态一段时间,以实现延时功能。
比如说,你有一个任务是每隔一段时间就去读取一下传感器的数据。那这个任务就可以在读完数据后,给自己安排一段 “小憩” 时光,也就是调用任务延迟函数。函数会告诉 FreeRTOS:“我要休息 X 个时间滴答啦,这段时间不能干活。” 这样,在这 X 个时间滴答内,这个任务就会乖乖地 “睡觉”,把 CPU 资源让给其他需要工作的任务。
延时的时间以 FreeRTOS 的时钟节拍为单位。例如:
vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1000 毫秒
在这个函数中,pdMS_TO_TICKS(1000)
将 1000 毫秒转换为对应的时钟节拍数。
2.2 vTaskDelayUntil
函数
vTaskDelayUntil
函数用于设置任务的周期性执行,它在指定的绝对时间执行任务,而不是相对于当前时间的相对延时。例如:
TickType_t xLastWakeTime = xTaskGetTickCount();
for (;;)
{
// 任务执行的代码
// 等待下一个周期
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));
}
在这个例子中,xLastWakeTime
记录了任务上次唤醒的时间,vTaskDelayUntil
会根据这个时间计算下一个唤醒时间。
3. 获取系统时间:时间的“眼睛”
FreeRTOS 提供了 xTaskGetTickCount
函数,用于获取系统自启动以来的时钟节拍数。例如:
c复制
TickType_t currentTickCount = xTaskGetTickCount();
此外,xTaskGetTickCountFromISR
函数用于在中断服务例程(ISR)中获取时钟节拍数。
4. 软件定时器:时间管理的“瑞士军刀”
FreeRTOS 的软件定时器是一种更灵活的时间管理工具,可以用于周期性执行任务或在一段时间后触发事件。
比如说,你想让某个设备每隔 10 秒就自动执行一次清理操作,这时候软件定时器就派上用场了。你可以创建一个软件定时器,设置好定时时间,再指定一个回调函数(也就是到时间后要执行的操作)。一旦定时器启动,它就会默默倒计时,时间一到,就会立刻触发回调函数。
4.1 创建定时器
使用 xTimerCreate
函数创建定时器,并指定回调函数。例如:
c复制
TimerHandle_t xTimer = xTimerCreate(
"MyTimer", // 定时器名称
pdMS_TO_TICKS(1000), // 周期为 1000 毫秒
pdTRUE, // 自动重载
NULL, // 定时器ID
vTimerCallback); // 回调函数
4.2 启动定时器
使用 xTimerStart
函数启动定时器:
c复制
if (xTimer != NULL)
{
xTimerStart(xTimer, 0);
}
4.3 定时器回调函数
在回调函数中,可以执行相应的操作。例如:
c复制
void vTimerCallback(TimerHandle_t xTimer)
{
printf("Timer expired!\n");
}
5. 时间片轮转:任务的“公平分配”
FreeRTOS 支持时间片轮转功能,通过 vApplicationTickHook
回调函数实现。用户需要在 FreeRTOSConfig.h
文件中将 configUSE_TICK_HOOK
宏定义为 1。时间片的长度由 configTICK_RATE_HZ
宏定义。
例如,以下代码展示了如何使用时间片轮转来统计系统运行时间:
c复制
uint32_t TickNum = 0;
void vApplicationTickHook(void)
{
TickNum++;
}
void vTaskPrint(void * pvParameters)
{
uint32_t TickNum_Minute = 0;
uint32_t TickNum_old = 0;
while(1)
{
if (TickNum/configTICK_RATE_HZ != 0 && TickNum_old != TickNum/configTICK_RATE_HZ)
{
TickNum_old = TickNum/configTICK_RATE_HZ;
if (TickNum_old > 59)
{
TickNum_Minute += 1;
TickNum_old = 0;
TickNum -= 60*configTICK_RATE_HZ;
}
printf("The current system runs for %d minute %d second\n\r", TickNum_Minute, TickNum/configTICK_RATE_HZ);
}
}
}
6. 实际应用案例:LED 闪烁与按键检测
以下是一个综合应用示例,展示了如何使用 FreeRTOS 的时间管理功能实现 LED 闪烁和按键检测:
c复制
void vTaskLED3(void * pvParameters)
{
while(1)
{
for (uint8_t j=0; j<10; j++)
{
R_LED(ON);
vTaskDelay(300/portTICK_RATE_MS);
R_LED(OFF);
vTaskDelay(300/portTICK_RATE_MS);
}
}
}
void vTaskKEY4(void * pvParameters)
{
while(1)
{
if (Key_GetNum() == 1)
{
printf("Idle tasks run %d times\n\r", IdleNumber);
}
if (Key_GetNum() == 2)
{
printf("Delay of %d system beats\n\r", DataDelay);
}
if (Key_GetNum() == 3)
{
printf("DelayUntil of %d system beats\n\r", DataDelayUntil);
}
vTaskDelay(100/portTICK_RATE_MS);
}
}