首页 > 代码库 > FreeRTOS 系统时钟节拍和时间管理
FreeRTOS 系统时钟节拍和时间管理
FreeRTOS 的时钟节拍
任何操作系统都需要提供一个时钟节拍,以供系统处理诸如延时、 超时等与时间相关的事件。
时钟节拍是特定的周期性中断,这个中断可以看做是系统心跳。 中断之间的时间间隔取决于不同的应
用,一般是 1ms – 100ms。时钟的节拍中断使得内核可以将任务延迟若干个时钟节拍,以及当任务等待
事件发生时,提供等待超时等依据。时钟节拍率越快,系统的额外开销就越大。
对于 Cortex-M3 内核的 STM32F103 和 Cortex-M4 内核的 STM32F407 以及 F429,教程配套的例
子都是用滴答定时器来实现系统时钟节拍的。
? 滴答定时器 Systick
SysTick 定时器被捆绑在 NVIC 中,用于产生 SysTick 异常(异常号: 15), 滴答定时器是一个 24 位
的递减计数器,支持中断。 使用比较简单, 专门用于给操作系统提供时钟节拍。
FreeRTOS 的系统时钟节拍可以在配置文件 FreeRTOSConfig.h 里面设置:
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
如上所示的宏定义配置表示系统时钟节拍是 1KHz,即 1ms。
时间延迟
FreeRTOS 中的时间延迟函数主要有以下两个作用:
? 为周期性执行的任务提供延迟。
? 对于抢占式调度器,让高优先级任务可以通过时间延迟函数释放 CPU 使用权,从而让低优先级任务可以得到执行。
下面我们通过如下的框图来说明一下延迟函数对任务运行状态的影响,让大家有一个形象的认识。
运行条件:
? 仅对任务 Task1 的运行状态做说明。
? 调度器支持时间片调度和抢占式调度。
运行过程描述如下:
? 起初任务 Task1 处于运行态,调用 vTaskDelay 函数后进入到阻塞状态,也就是 blocked 状态。
? vTaskDelay 函数设置的延迟时间到,由于任务 Task1 不是当前就绪的最高优先级任务,所以不能进
入到运行状态,只能进入到就绪状态,也就是 ready 状态。
? 一段时间后, 调度器发现任务 Task1 是当前就绪的最高优先级任务,从而任务从就绪态切换到运行态。
? 由于时间片调度,任务 Task1 由运行态切换到就绪态。
FreeRTOS 的时间相关函数
FreeRTOS 时间相关的函数主要有以下 4 个:
? vTaskDelay ()
? vTaskDelayUntil ()
? xTaskGetTickCount()
? xTaskGetTickCountFromISR()
函数 xTaskGetTickCount
函数原型:
volatile TickType_t xTaskGetTickCount( void );
函数描述:
函数 xTaskGetTickCount 用于获取系统当前运行的时钟节拍数。
使用这个函数要注意以下问题:
1. 此函数用于在任务代码里面调用,如果在中断服务程序里面调用的话,需要使用函数
xTaskGetTickCountFromISR,这两个函数切不可混用。
eg:
函数 xTaskGetTickCountFromISR
函数原型:
volatile TickType_t xTaskGetTickCountFromISR( void );
函数描述:
函数 xTaskGetTickCountFromISR 用于获取系统当前运行的时钟节拍数。
使用这个函数要注意以下问题:
1. 此函数用于在中断服务程序里面调用, 如果在任务里面调用的话, 需要使用函数 xTaskGetTickCount,
这两个函数切不可混用。
eg:
void TIM6_IRQHandler( void )
{
TickType_t xTickCount;
xTickCount = xTaskGetTickCountFromISR();
}
函数 vTaskDelay
函数原型:
void vTaskDelay(const TickType_t xTicksToDelay ); /* 延迟时间长度 */
函数描述:
函数 vTaskDelay 用于任务的延迟。
? 参数 xTicksToDelay 用于设置延迟的时钟节拍个数,范围 1- 0xFFFFFFFF。 延迟时间的最大值在
portmacro.h 文件里面有定义:
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t )0xffffffffUL
即延迟时间的范围是:1- 0xFFFFFFFF
函数 vTaskDelayUntil
函数原型:
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, /* 存储任务上次处于非阻塞状态时刻的变量地址 */
const TickType_t xTimeIncrement ); /* 周期性延迟时间 */
函数描述:
函数 vTaskDelayUntil 用于周期性延迟。
? 第 1 个参数,存储任务上次处于非阻塞状态时刻的变量地址。
? 第 2 个参数,周期性延迟时间。
使用这个函数要注意以下问题:
1. 使用此函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1
#define INCLUDE_vTaskDelayUntil 1
函数 vTaskDelay 和 vTaskDelayUntil 的区别
函数 vTaskDelayUntil 实现的是周期性延迟,而函数 vTaskDelay 实现的是相对性延迟,反映到实际
应用上有什么区别呢,下面就给大家举一个简单的例子。
运行条件:
? 有一个 bsp_KeyScan 函数,这个函数处理时间大概耗时 2ms。
? 有两个任务,一个任务 Task1 是用的 vTaskDelay 延迟,延迟 10ms,另一个任务 Task2 是用的
vTaskDelayUntil 延迟,延迟 10ms。
? 不考虑任务被抢占而造成的影响
eg1:
static void vTaskLED(void *pvParameters) { TickType_t xLastWakeTime; const TickType_t xFrequency = 200; /* 获取当前的系统时间 */ xLastWakeTime = xTaskGetTickCount(); while(1) { bsp_LedToggle(2); /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/ vTaskDelayUntil(&xLastWakeTime, xFrequency); } }
eg2:
static void vTaskMsgPro(void *pvParameters) { TickType_t xDelay, xNextTime; const TickType_t xFrequency = 200; /* 获取xFrequency个时钟节拍后的时间 */ xNextTime = xTaskGetTickCount() + xFrequency; while(1) { bsp_LedToggle(3); /* 用vTaskDelay实现vTaskDelayUntil() */ xDelay = xNextTime - xTaskGetTickCount(); xNextTime += xFrequency; if(xDelay <= xFrequency) { vTaskDelay(xDelay); } } }
FreeRTOS 系统时钟节拍和时间管理