FreeRTOS学习笔记之软件定时器
一、简介
FreeRTOS中的定时器是(基于滴答定时器)由软件实现的,与STM32的定时器不同,STM32的硬件定时器可以实现输入捕获,输出比较且定时器数量数量有限,当STM32的定时器计时时间到后,可以进入中断函数,而FreeRTOS中的定时器计时时间到后会执行一段代码,这段代码叫做回调函数
FreeRTOS中的定时器在内存足够的情况下可以有无限个,使用起来简单且成本低;缺点就是精度没有硬件高,而且滴答定时器可以被系统中断打断
对于软件定时器容易被系统中断打断,对于需要高精度要求的场合,不建议使用软件定时器。
1.1 什么是软件定时器服务任务?
在调用函数 vTaskStartScheduler()开启任务调度器的时候,系统会自主创建软件定时器服务任务和空闲任务。
软件定时器服务任务它是一个任务,主要负责软件定时器超时的逻辑判断、调用超时软件定时器的回调函数以及处理软件定时器命令队列,我的理解是软件定时器服务任务它是一个任务,这个任务管理所有软件定时器。该任务服务于全部的软件定时器
注意:软件定时器超时回调函数是由软件定时器服务任务调用的,软件定时器超时回调函数不是任务,所以回调函数不会阻塞,也因此不可在回调函数中使用可能会导致任务阻塞的API函数,如果用了导致阻塞的API函数,那么会将软件定时器服务任务阻塞,阻塞后,软件定时器服务任务负责的其他软件定时器在软件定时器服务任务阻塞期间不可工作。
1.2 什么是软件定时器命令队列?
先看几个宏定义
#define configUSE_TIMERS 用于开启软件定时器功能,系统创建软件定时器服务任务和空闲任务
#define configTIMER_TASK_PRIORITY 用于配置软件定时器服务任务的任务优先级,建议配置最大优先级用于调用软件定时器超时回调函数
#define configTIMER_QUEUE_LENGTH 用于配置软件定时器命令队列的队列长度(须大于0)
#define configTIMER_TASK_STACK_DEPTH 用于配置软件定时器服务任务的栈大小
开启系统软件定时器功能使系统创建软件定时器服务任务、配置软件定时器命令队列的长度、设定软件定时器服务任务优先级、设定软件定时器服务任务的栈大小
对于软件定时器命令队列,其相当于介于应用程序和软件定时器服务任务的一个桥梁
从本篇的2.2 API函数章节可以看出,想要对软件按定时器进行操作,就需要往软件定时器命令队列中发送相应的指令(比如创建、开启定时器等),(如果队列项满了,还需要发送进行阻塞等待),软件定时器服务任务在从软件定时器命令队列中获取指令对定时器进行更改
1.3 软件定时器状态
<1>休眠态:休眠态软件定时器可以通过其句柄被引用,但是因为没有运行,所以其定时超时回调函数不会被执行。
<2>运行态:处于运行态或在上次定时超时后再次定时超时的软件定时器,会执行其定时超时回调函数
<1>单次定时器:定时器的一旦定时超时,只会执行一次其软件定时器超时回调函数,超时后可以被手动重新开启,但单次定时器不会自动重新开启定时
<2>周期定时器:定时器的一旦被开启,会在每次超时时,自动地重新启动定时器,从而周期地执行其软件定时器回调函数。
注意:新创建的软件定时器处于休眠状态,也就是未运行状态。可以通过发送命令队列使定时器从休眠态转变为运行态
二、结构体/API函数
2.1 结构体
typedef struct tmrTimerControl {const char * pcTimerName; //软件定时器名字ListItem_t xTimerListItem; //软件定时器列表项TickType_t xTimerPeriodInTicks; //软件定时器周期void * pvTimerID; //软件定时器ID,当两个定时器对应同一个超时回调函数时,通过ID分辨TimerCallbackFunction_t pxCallbackFunction; //软件定时器的回调函数#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxTimerNumber; //软件定时器编号,调试用#endifuint8_t ucStatus; //软件定时器状态,单次/周期} xTIMER;
2.2 API函数
函数 | 描述 |
xTimerCreate() | 动态方式创建软件定时器 |
xTimerCreateStatic() | 静态方式创建软件定时器 |
xTimerStart() | 开启软件定时器定时 |
xTimerStartFromISR() | 在中断中开启软件定时器定时 |
xTimerStop() | 停止软件定时器定时 |
xTimerStopFromISR() | 在中断中停止软件定时器定时 |
xTimerReset() | 复位软件定时器定时 |
xTimerResetFromISR() | 在中断中复位软件定时器定时 |
xTimerChangePeriod() | 更改软件定时器的定时超时时间 |
xTimerChangePeriodFromISR() | 在中断中更改软件定时器的定时超时时间 |
xTimerDelete() | 删除软件定时器 |
2.2.1 创建软件定时器API
TimerHandle_t xTimerCreate
(const char * const pcTimerName ,//@软件定时器名const TickType_t xTimerPeriodInTicks ,//@软件定时器定时超时时间,单位为系统时钟节拍const UBaseType_t uxAutoReload ,//@定时模式,pdTURE周期定时,pdFALSE单次定时void * const pvTimerID ,//@定时器ID,用于多个定时器公用一个回调函数TimerCallbackFunction_t pxCallbackFunction //@软件定时器超时回调函数
);
返回值:
NULL:定时器创建失败
其他值:软件定时创建成功,返回其句柄
2.2.2 开启软件定时器API
为什么每一个对软件定时器进行操作的API都有可以设定的等待时间呢?因为操作定时器的是定时器服务任务,而定时器服务任务需要从定时器命令队列中获取消息指令,也就是用于对定时器操作的API相当于向软件定时器队列中发送消息指令,但队列项的值是一定的,如果多个API操作函数往队列中发送消息,那么当队列项满的时候,则可以选择阻塞等待一段时间,看看队列项没有没有空的。
BaseType_t xTimerStart
(TimerHandle_t xTimer , //@待开启的软件定时器的句柄const TickType_t xTicksToWait //@发送命令到软件定时器命令队列的最大等待时间
);
返回值:
pdPASS:软件定时器开启成功
pdFAIL:软件定时器开启失败
2.2.3 关闭软件定时器API
BaseType_t xTimerStop
(TimerHandle_t xTimer , //@待关闭的软件定时器的句柄const TickType_t xTicksToWait //@发送命令到软件定时器命令队列的最大等待时间
);
返回值:
pdPASS:软件定时器关闭成功
pdFAIL:软件定时器关闭失败
2.2.4 复位软件定时器
该功能将使软件定时器的重新开启定时,例如定时器设定超时时间为10ms,当定时器计时到5ms时复位该定时器,则定时器会重新计时10ms.
BaseType_t xTimerReset
(TimerHandle_t xTimer ,//@待复位的软件定时器的句柄const TickType_t xTicksToWait //@发送命令到软件定时器命令队列的最大等待时间
);
返回值:
pdPASS:软件定时器复位成功
pdFAIL:软件定时器复位失败
2.2.5 更改软件定时器的定时超时时间
BaseType_t xTimerChangePeriod
(TimerHandle_t xTimer ,//@待更改定时超时时间的软件定时器的句柄const TickType_t xNewPeriod ,//@新的定时超时时间,单位:系统时钟节拍const TickType_t xTicksToWait //@发送命令到软件定时器命令队列的最大等待时间
);
返回值:
pdPASS:软件定时器定时超时时间更改成功
pdFAIL: 软件定时器定时超时时间更改失败