【嵌入式——FreeRTOS】软件定时器
定时器简介
定时器:从指定的时刻开始,经过一个指定时间,然后触发一个超时事件,用户可自定义定时器的周期。
硬件定时器:芯片本身自带的定时器模块,硬件定时器的精度一般很高,每次在定时时间到达之后就会触发一个中断,用户在中断服务函数中处理信息。
软件定时器:是指具有定时功能的软件,可设置定时周期,当指定时间到达之后要调用回调函数,用户在回调函数中处理信息。
软件定时器优缺点
优点
- 理论是只需要足够内存,就可以创建多个
- 使用简单、成本低
缺点
- 精度没那么高
软件定时器特点
- 可裁剪:软件定时器是可裁剪可配置的功能,如果要使能软件定时器,需将configUSE_TIMERS宏配置为1
- 单次和周期:软件定时器支持设置成单次定时器或周期定时器
软件定时器的超时回调函数是由软件定时器服务任务调用的,软件定时器的超时回调函数本身不是任务,因此不能在该回调函数中使用可能会导致任务阻塞的API函数。
软件定时器服务任务:在调用函数vTaskStartScheduler()开启任务调度器的时候,会创建一个用于管理软件定时器的任务,这个任务就叫做软件定时器服务任务。
软件定时器服务任务作用:
- 负责软件定时器超时的逻辑判断
- 调用超时软件定时器的超时回调函数
- 处理软件定时器的命令队列
软件定时器的命令队列
FreeRTOS提供了许多软件定时器相关的API函数,这些API函数大多都是往定时器的队列中写入消息,这个队列叫做软件定时器命令队列,是提供给FreeRTOS中的软件定时器使用,用户是不能直接访问的。
软件定时器相关配置
当FreeRTOS的配置项configUSE_TIMERS设置为1,在启动任务调度器时,会自动创建软件定时器的服务/守护任务prvTimerTask()。
软件定时器服务任务的优先级为configTIMER_TASK_PRIORITY(31)。
定时器的命令队列长度为 configTIMER_QUEUE_LENGTH(5)。
软件定时器的超时回调函数是在软件定时器服务任务中被调用的,服务任务不是专为某个定时器服务的,它还要处理其他定时器。
回调函数要尽快执行,不能进入阻塞状态,即不能调用那些会让任务阻塞的API函数,如vTaskDelay()。
访问队列或者信号量的非零阻塞时间的API函数也不能调用。
软件定时器的状态
- 休眠态:软件定时器可以通过其句柄被引用,但因为没有运行,所以其定时超时回调函数不能被执行。
- 运行态:运行态的定时器,当指定时间到达之后,它的超时回调函数会被调用。
休眠态转变为运行态:发送命令队列。
工作模式
单次定时器
单次定时器的一旦定时超时,只会执行一次其软件定时器超时回调函数,不会自动重新开启定时,不过可以被手动重新开启。
周期定时器
周期定时器的一旦启动以后就会执行完回调函数以后自动的重新启动,从而周期的执行器软件定时器回调函数。
软件定时器结构体成员介绍
typedef struct tmrTimerControl
{
const char * pcTimerName; //软件定时器名字
ListItem_t xTimerListItem; //软件定时器列表项
TickType_t xTimerPeriodInTicks; //软件定时器的周期
void * pvTimerID; //软件定时器ID
TimerCallbackFunction_t pxCallbackFunction; //软件定时器回调函数
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTimerNumber; //软件定时器编号,调试用
#endif
uint8_t ucStatus; //软件定时器状态
} xTIMER;
相关API
函数 | 描述 |
---|---|
xTimerCreate() | 动态方式创建软件定时器 |
xTimerCreateStatic() | 静态方式创建软件定时器 |
xTimerStart() | 开启软件定时器定时器 |
xTimerStartFromISR() | 在中断中开启软件定时器定时器 |
xTimerStop() | 停止软件定时器定时 |
xTimerStopFromISR() | 在中断中停止软件定时器定时 |
xTimerReset() | 复位软件定时器定时 |
xTimerResetFromISR() | 在中断中复位软件定时器定时 |
xTimerChangePeriod() | 更改软件定时器的定时超时时间 |
xTimerChangePeriodFromISR() | 在中断中更改软件定时器的定时超时时间 |
//返回值 NULL 创建失败 其他值 创建成功 返回定时器句柄
TimerHandle_t xTimerCreate(
const char * const pcTimerName, //软件定时器名
const TickType_t xTimerPeriodInTicks, //定时超时时间
const BaseType_t xAutoReload, //定时器模式 pdTRUE:周期定时器,pdFALSE:单次定时器
void * const pvTimerID, //定时器ID
TimerCallbackFunction_t pxCallbackFunction //回调函数
)
//xTimer 待开启的软件定时器句柄
//xTicksToWait 发送命令到软件定时器命令队列的最大等待时间
//返回值 pdPASS开启成功 pdFAIL 开启失败
#define xTimerStart( xTimer, xTicksToWait ) \
xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
//xTimer 待停止的软件定时器句柄
//xTicksToWait 发送命令到软件定时器命令队列的最大等待时间
//返回值 pdPASS停止成功 pdFAIL 停止失败
#define xTimerStop( xTimer, xTicksToWait ) \
xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) )
//xTimer 待复位的软件定时器句柄
//xTicksToWait 发送命令到软件定时器命令队列的最大等待时间
//返回值 pdPASS复位成功 pdFAIL 复位失败
#define xTimerReset( xTimer, xTicksToWait ) \
xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
//xTimer 待更新的软件定时器句柄
//xNewPeriod 新的定时超时时间 单位1ms
//xTicksToWait 发送命令到软件定时器命令队列的最大等待时间
//返回值 pdPASS更改成功 pdFAIL 更改失败
#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) \
xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U )
代码示例
#include "queue.h"
#inculde "sempht.h"
TimerHandle_t timer1_handle = 0; //单次定时器句柄
TimerHandle_t timer2_handle = 0; //周期定时器句柄
static TaskHandle_t start_tid;
void timer1Callback( TimerHandle_t pxTimer );
void timer2Callback( TimerHandle_t pxTimer );
void init(){
//创建
xTaskCreate((TaskFunction_t)task_start,
(const char *)"Initialize all task",
(uint16_t)START_THREAD_STKSZ,
(void *)NULL,
(UBaseType_t)(START_THREAD_PRIO),
(TaskHandle_t *)&start_tid);
//这个方法会创建两个定时器任务
vTaskStartScheduler();
}
//发送任务通知值
void task_start(){
timer1_handle = xTimerCreate("time1",1000,pdFALSE,(void *)1,timer1Callback);
timer2_handle = xTimerCreate("time1",1000,pdTRUE,(void *)1,timer2Callback);
xTaskCreate((TaskFunction_t)task1,
(const char *)"Initialize all task",
(uint16_t)START_THREAD_STKSZ,
(void *)NULL,
(UBaseType_t)(START_THREAD_PRIO),
(TaskHandle_t *)&start_tid);
}
//timer1超时回调函数
void timer1Callback( TimerHandle_t pxTimer )
{
static uint32_t timer = 0;
printf("timer1执行次数\n",++timer);
}
//timer2超时回调函数
void timer2Callback( TimerHandle_t pxTimer )
{
static uint32_t timer = 0;
printf("timer2执行次数\n",++timer);
}
void task1(void * pvParameters){
uint8_t key=0;
while(1){
key = key_scan();
if(key == KEY0_PRES){
//开启定时器
xTimerStart(timer1_handle , portMAX_DELAY);
xTimerStart(timer2_handle , portMAX_DELAY);
} else if(key == KEY1_PRES){
//停止定时器
xTimerStop(timer1_handle , portMAX_DELAY);
xTimerStop(timer2_handle , portMAX_DELAY);
}
}
}