FreeRTOS-软件定时器
- 一般来说,每个MCU都会有定时器,这些都属于硬件定时器,但是有些时候硬件定时器可能会不够用,FreeRTOS提供了软件定时器,并且当内存足够的时候,软件定时器可以设置很多歌,当然,相比于硬件定时器,软件定时器精度和功能上都会稍差些,但是在一些对精度等条件要求不高的场合中足够了。
- 在使用软件定时器时,可以设置一段时间,经过一段时间后,系统就会自动调用软件定时器所对应的回调函数,但是要注意的是,因为软件定时器回调函数是在定时器服务任务中执行的(当做任务主体的一部分),所以在软件定时器回调函数中一定不要调用任何会阻塞任务的API函数。
软件定时器配置
- 实际上,软件定时器也是基于FreeRTOS中的队列来完成各项操作的,这个队列叫做定时器命令队列,定时器命令队列是提供给FreeRTOS的软件定时器使用的,用户不能直接访问。
- 在使用软件定时器前,也需要做一些相应的配置,软件定时器的配置主要有四部分。
- configUSE_TIMERS 如果要使用软件定时器的话要设置该宏为1,在启动调度器的时候就会自动创建软件定时器任务
- configTIMER_TASK_PRIORITY 软件定时器优先级,这里决定了软件定时器回调函数的优先级
- configTIMER_QUEUE_LENGTH 软件定时器队列长度,决定了能够创建多少个软件定时器
- configTIMER_TASK_STACK_DEPTH 软件定时器任务堆栈大小
- 此外,软件定时器还可以设置为单次定时器和周期定时器,当设置为单次定时器时,定时器启动后,当定时时间到达后,就会调用一次回调函数,调用完毕后定时器就会停止,当需要定时器再次启动时就需要复位定时器,然后定时器再经过定时周期后调用回调函数。周期定时器则当开启后就会周期性的调用回调函数。
- 因为定时器回调函数是根据任务上下文切换来调用的,所以定时器的精度取决于系统的时钟节拍,当系统时钟节拍周期越小,则定时器精度越高,相应地,系统开销也就越大。
相关API
- 在本章不会重点分析定时器相关API的源码。只是将定时器API函数的使用方法作一个讲述。
xTimerCreate()
- 函数功能:动态创建定时器
- 函数声明:TimerHandle_t xTimerCreate( const char * const pcTimerName,const TickType_t xTimerPeriodInTicks,const UBaseType_t uxAutoReload,void * const pvTimerID,TimerCallbackFunction_t pxCallbackFunction )
- 输入参数pcTimerName:定时器名
- 输入参数xTimerPeriodInTicks:定时器定时周期
- 输入参数uxAutoReload:是否设置为周期定时器,pdTRUE为周期定时器,pdFALSE为单次定时器
- 输入参数pvTimerID:设置定时器ID,不同ID定时器可能会有相同的定时器名,所以设置不同ID加以区分
- 输入参数pxCallbackFunction:定时器回调函数,该函数需要用户自定义
- 返回参数TimerHandle_t:定时器句柄
xTimerReset()
- 函数功能:复位定时器,如果是单次定时器,当调用一次关闭后,调用该函数可以重新打开
- 函数声明: xTimerReset(xTimer, xTicksToWait )
- 输入参数xTimer: 复位定时器句柄
- 输入参数xTicksToWait:定时器复位阻塞时间,因为复位定时器时本质是向队列发送消息,所以肯定会涉及到阻塞时间
- 返回参数pdFAIL或pdTRUR,返回pdFAIL是说明定时器复位失败
xTimerStart()
- 函数功能:开启定时器
- 函数声明:xTimerStart( xTimer, xTicksToWait )
- 输入参数xTimer: 开启定时器句柄
- 输入参数xTicksToWait:定时器复位阻塞时间,因为开启定时器时本质是向队列发送消息,所以肯定会涉及到阻塞时间
- 返回参数pdFAIL或pdTRUR,返回pdFAIL是说明定时器开启失败
xTimerStop()
- 函数功能:停止定时器
- 函数声明: xTimerStop(xTimer, xTicksToWait )
- 输入参数xTimer: 停止定时器句柄
- 输入参数xTicksToWait:定时器停止阻塞时间,因为停止定时器时本质是向队列发送消息,所以肯定会涉及到阻塞时间
- 返回参数pdFAIL或pdTRUR,返回pdFAIL是说明定时器停止失败
- 同样地,这些函数也有相应的中断级API,功能与上面任务级的类似,这里就不展开叙述了。下面给出一个用法示例。
用法示例
实验目的:通过按键控制软件定时器的开启与关闭,创建两个定时器,在回调函数中记录定时器调用次数
#define TIMER1_PERIOD 1000
#define TIMER1_ID 1
TimerHandle_t Timer1_Handler;
#define TIMER2_PERIOD 3000
#define TIMER2_ID 2
TimerHandle_t Timer2_Handler;
BaseType_t err;
Timer1_Handler = xTimerCreate((char *)"Timer1",
(TickType_t )TIMER1_PERIOD,
(UBaseType_t) pdTRUE,
(void *)TIMER1_ID,
(TimerCallbackFunction_t) Timer1_CallBack);
if (Timer1_Handler == NULL)
{
printf("Timer1 Creat Failed!!!\r\n");
}
else
{
err = xTimerStart(Timer1_Handler,10);
if (err != pdTRUE)
{
printf("Timer1 Start Failed!!!\r\n");
}
}
Timer2_Handler = xTimerCreate((char *)"Timer2",
(TickType_t )TIMER2_PERIOD,
(UBaseType_t)pdFALSE,
(void *) TIMER2_ID,
(TimerCallbackFunction_t) Timer2_CallBack);
if (Timer2_Handler == NULL)
{
printf("Timer2 Creat Failed!!!\r\n");
}
else
{
err = xTimerStart(Timer2_Handler,10);
if (err != pdTRUE)
{
printf("Timer1 Start Failed!!!\r\n");
}
}
u8 keyFlag=0;
void key_task(void* pvParameters)
{
BaseType_t err;
while(1)
{
if (Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON)
{
if ( (keyFlag&0x01) == 0x01)
keyFlag &= ~(0x01<<0);
else
keyFlag |= (0x01<<0);
if ((keyFlag&0x01) == 0x01)
{
err = xTimerStart(Timer1_Handler,0);
if (err != pdTRUE )
{
printf("Timer1 Start Failed!!!\r\n");
}
else
{
printf("Timer1 Start!!\r\n");
}
}
else
{
err = xTimerStop(Timer1_Handler,0);
if (err != pdTRUE )
{
printf("Timer1 Stop Failed!!!\r\n");
}
else
{
printf("Timer1 Stop!!\r\n");
}
}
}
if (Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON)
{
if ( (keyFlag&0x02) == 0x02)
keyFlag &= ~(0x01<<1);
else
keyFlag |= (0x01<<1);
if ( (keyFlag&0x02) == 0x02)
{
xTimerStart(Timer2_Handler,0);
printf("Timer2 Start!!\r\n");
}
else
{
xTimerReset(Timer2_Handler,0);
printf("Timer2 Reset!!\r\n");
}
}
vTaskDelay(10);
}
}
void Timer1_CallBack(TimerHandle_t xTimer)
{
static int count1 = 0;
count1++;
LED0 = ~LED0;
printf("timer1 Call Back %d\r\n",count1);
}
void Timer2_CallBack(TimerHandle_t xTimer)
{
static int count2 = 0;
count2++;
LED1 = ~LED1;
printf("timer2 Call Back %d\r\n",count2);
}
|