1 、FreeRTOS 的 时钟 节拍
任何操作系统都需要提供一个时钟节拍,以供系统处理诸如延时、超时等与时间相关的事件。
时钟节拍是特定的周期性中断,这个中断可以看做是系统心跳。中断之间的时间间隔取决于不同的应用,一般是 1ms – 100ms。时钟的节拍中断使得内核可以将任务延迟若干个时钟节拍,以及当任务等待事件发生时,提供等待超时等依据。时钟节拍率越快,系统的额外开销就越大。
FreeRTOS 的系统时钟节拍可以在配置文件 FreeRTOSConfig.h 里面设置:
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
如上所示的宏定义配置表示系统时钟节拍是 1KHz,即 1ms。
2 、FreeRTOS 的 时 间 管 理
时间管理功能是 FreeRTOS 操作系统里面最基本的功能,同时也是必须要掌握好的。
2.1 时间延迟
FreeRTOS 中的时间延迟函数主要有以下两个作用:
上面就是一个简单的任务运行状态的切换过程。
2.2 FreeRTOS 的时间相关函数
FreeRTOS 时间相关的函数主要有以下 4 个:
vTaskDelay ()
vTaskDelayUntil ()
xTaskGetTickCount()
xTaskGetTickCountFromISR()
下面我们对这 4 个函数依次进行说明:
2.3 函数 vTaskDelay
使用举例:
2.4 函数 vTaskDelayUntil
函数原型:
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, /* 存储任务上次处于非阻塞状态时刻的变量地址 */
const TickType_t xTimeIncrement ); /* 周期性延迟时间 */
函数描述:
函数 vTaskDelayUntil 用于周期性延迟。
第 1 个参数,存储任务上次处于非阻塞状态时刻的变量地址。
第 2 个参数,周期性延迟时间。
使用这个函数要注意以下问题:
1. 使用此函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1
#define INCLUDE_vTaskDelayUntil 1
2. 用户要注意此函数跟 vTaskDelay 的区别,2.7 小节详细讲解。
使用举例:
2.5 函 数 xTaskGetTickCount
函数原型:
volatile TickType_t xTaskGetTickCount( void );
函数描述:
函数 xTaskGetTickCount 用于获取系统当前运行的时钟节拍数。
使用这个函数要注意以下问题:
1. 此函数用于在任务代码里面调用,如果在中断服务程序里面调用的话,需要使用函数xTaskGetTickCountFromISR,这两个函数切不可混用。
使用举例:
2.6 函 数 xTaskGetTickCountFromISR
函数原型:
volatile TickType_t xTaskGetTickCountFromISR( void );
函数描述:
函数 xTaskGetTickCountFromISR 用于获取系统当前运行的时钟节拍数。
使用这个函数要注意以下问题:
1. 此函数用于在中断服务程序里面调用,如果在任务里面调用的话,需要使用函数 xTaskGetTickCount,
这两个函数切不可混用。
使用举例:
/**********************************************************************************************************
* 函 数 名: TIM6_IRQHandler
* 功能说明: TIM6 中断服务程序。
* 形 参: 无
* 返 回 值: 无
**********************************************************************************************************/
void TIM6_IRQHandler( void)
{
TickType_t xTickCount;
xTickCount=xTaskGetTickCountFromISR();
}
2.7 函数 vTaskDelay 和 vTaskDelayUntil 的区别
函数 vTaskDelayUntil 实现的是周期性延迟,而函数 vTaskDelay 实现的是相对性延迟,反映到实际
应用上有什么区别呢,下面就给大家举一个简单的例子。
运行条件:
有一个 bsp_KeyScan 函数,这个函数处理时间大概耗时 2ms。
有两个任务,一个任务 Task1 是用的 vTaskDelay 延迟,延迟 10ms,另一个任务 Task2 是用的
vTaskDelayUntil 延迟,延迟 10ms。
不考虑任务被抢占而造成的影响。
实际运行过程效果:
Task1:
bsp_KeyScan+ vTaskDelay (10) ---> bsp_KeyScan + vTaskDelay (10)
|----2ms + 10ms 为一个周期------| |----2ms + 10ms 为一个周期----|
这个就是相对性的含义
Task2:
bsp_KeyScan + vTaskDelayUntil ---------> bsp_KeyScan + vTaskDelayUntil
|----10ms 为一个周期(2ms 包含在 10ms 内)---| |----10ms 为一个周期------|
这就是周期性的含义。
下面我们通过函数 vTaskDelay 来实现 vTaskDelayUntil,会有一个更加全面的认识:
/**********************************************************************************************************
* 函 数 名: vTaskMsgPro
* 功能说明: 消息处理,这里是用作 LED 闪烁
* 形 参: pvParameters 是在创建该任务时传递的形参
* 返 回 值: 无
* 优 先 级: 3
**********************************************************************************************************/
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);
}
}
}
3、实验 例程
实验目的:
1. 学习 FreeRTOS 的周期性延迟和相对性延迟函数。
2. 注意相对性延迟函数 vTaskDelay 和周期性延迟函数 vTaskDelayUntil 的区别。
实验内容:
1. K1 按键按下,串口打印任务执行情况(波特率 115200,数据位 8,奇偶校验位无,停止位 1)。
2. K2 键按下,串口打印系统时钟节拍数。
static void vTaskTaskUserIF(void *pvParameters)
{
uint8_t ucKeyCode;
uint8_t pcWriteBuffer[500];while(1)
{
ucKeyCode=bsp_GetKey();if (ucKeyCode !=KEY_NONE)
{switch(ucKeyCode)
{/*K1键按下 打印任务执行情况*/
caseKEY_DOWN_K1:
printf("=================================================\r\n");
printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n");
vTaskList((char *)&pcWriteBuffer);
printf("%s\r\n", pcWriteBuffer);
printf("\r\n任务名 运行计数 使用率\r\n");
vTaskGetRunTimeStats((char *)&pcWriteBuffer);
printf("%s\r\n", pcWriteBuffer);break;/*K1键按下 打印系统时钟节拍数*/
caseKEY_DOWN_K2:
printf("当前的系统时钟节拍数 = %d\r\n", xTaskGetTickCount());break;/*其他的键值不处理*/
default:break;
}
}
vTaskDelay(20);
}
}