软件定时器
所谓定时器,也就可以类比生活中人们常用的闹钟,可以单次响铃提醒,也可以间隔固定时间响铃提醒;与FreeRTOS定时器不同的是周期不同,FreeRTOS的周期更加短,一般使用毫秒(ms)、秒(s)。
软件定时器,是指定时器的触发方式,软件定时器一旦到达定时时间就会触发回调函数。
回调函数,遵循快进快出原则,因此,其中一定不能存在任何的阻塞,如vTaskDelay()、while(1)或者是其他能够产生阻塞的情况。
FreeRTOS的软件定时器在功能上支持:
- 软件定时器单次与周期执行;
- 裁剪:能通过宏关闭软件定时器功能;
- 软件定时器创建;
- 软件定时器启动;
- 软件定时器停止;
- 软件定时器复位;
- 软件定时器删除;
定时器周期性工作与单次工作时间轴简图
在freeRTOS的配置上,如果要是用定时器就需要配置下面几个宏定义:
//打开定时器
#define configUSE_TIMERS 1
//定时器的优先级
#define configTIMER_TASK_PRIORITY 50
//定时器栈大小
#define configTIMER_TASK_STACK_DEPTH 50
//定时器队列大小
#define configTIMER_QUEUE_LENGTH 50
FreeRTOS定时器的控制块
typedef struct tmrTimerControl /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
const char * pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */
TickType_t xTimerPeriodInTicks; /*<< How quickly and often the timer expires. */
void * pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */
TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */
#endif
uint8_t ucStatus; /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */
} xTIMER;
定时器控制块中一共含有7个变量,其作用解析如下:
- const char * pcTimerName:记录定时器名字
- ListItem_t xTimerListItem:定时器的列表项,用于插入定时器列表;
- TickType_t xTimerPeriodInTicks:定时器的周期,单位为系统节拍周期,即tick;
- void * pvTimerID:定时器的ID,整数形式。该ID是当一个回调函数分配给一个或多个定时器时,可以根据IP不同处理回调函数中不同程序;
- TimerCallbackFunction_t pxCallbackFunction:定时器回调函数;
- UBaseType_t uxTimerNumber:跟踪工具分配的ID,如FreeRTOS+Trace;
- uint8_t ucStatus:保存计时器是否静态分配,以及它是否处于活动状态;
在定义完成控制块后,代码中会使用typedef xTIMER Timer_t
重定义控制块的变量别名,在后续代码及开发中可以使用别名完成程序开发。
相关函数解析
在FreeRTOS的定时器相关函数中,相关的函数有很多,如:发送指定到定时器任务队列函数xTimerGenericCommand
、更新定时器周期函数xTimerChangePeriod
、重启定时器函数xTimerReset
、创建定时器函数xTimerCreate
、启动定时器函数xTimerStart
等等,其中最为重要且常用的函数当属创建定时器xTimerCreate
、启动定时器xTimerStart
。
动态创建定时器函数
函数原型:
TimerHandle_t xTimerCreate( const char * const pcTimerName,
TickType_t xTimerPeriodInTicks,
BaseType_t xAutoReload,
void * pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
函数说明:
- const char * const pcTimerName:设置定时器名字;
- TickType_t xTimerPeriodInTicks:设置定时器执行周期;
- BaseType_t xAutoReload:设置定时器是单次或周期执行;
- void * pvTimerID:设置定时器ID;
- TimerCallbackFunction_t pxCallbackFunction:设置定时器回调函数;
- 返回值:pxNewTimer ,数据类型为TimerHandle_t。一旦创建成功,则返回值不为NULL;否则,就为NULL;
函数解析:
通过该函数可以创建一个定时器,但是该函数仅仅只是申请了一块内存空间,真正进行定时器初始化的函数为prvInitialiseNewTimer()
。但是需要注意的是创建完成后处于就绪状态,需要启动后才能正常使用该定时器。
静态创建定时器函数
函数原型:
TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,
TickType_t xTimerPeriodInTicks,
BaseType_t xAutoReload,
void * pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t *pxTimerBuffer );
函数参数说明:
- const char * const pcTimerName:设置定时器名字;
- TickType_t xTimerPeriodInTicks:设置定时器执行周期;
- BaseType_t xAutoReload:设置定时器是单次或周期执行;
- void * pvTimerID:设置定时器ID;
- TimerCallbackFunction_t pxCallbackFunction:设置定时器回调函数;
- StaticTimer_t *pxTimerBuffer:指向StaticTimer_t类型的变量,用于保存计时器的状态,就不需要动态分配内存了;
- 返回值:pxNewTimer ,数据类型为TimerHandle_t。一旦创建成功,则返回值不为NULL;否则,就为NULL;
函数解析:
定时器创建函数,与上面的定时器创建函数功能上一样,参数与返回值大部分相同,唯一不同的是定时器内存的分配方式,该函数无需动态分配内存,因为其已经传递一个指针用于保存定时器的相关内容。
启动定时器函数
函数原型:
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
函数参数说明:
- TimerHandle_t xTimer:创建定时器成功后返回的控制权柄,也就是其地址
- TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。在系统开始调用
vTaskStartScheduler()
前调用该函数,那么函数形参无作用。在定时器开始指令成功送达定时器命令队列前,定时器还是处于阻塞状态的。- 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
返回 pdFAILE,成功发送则返回pdPASS。
函数解析:
该函数的作用为启动定时器,一旦创建定时器后,就必须使用该函数启动,否则定时器还是处于就绪状态,没有任何实际用处。
启动定时器的函数实际上还是调用xTimerGenericCommand()
函数,该函数含有五个参数、一个返回值,功能是发送命令到定时器命令队列。
在中断中启动定时器函数
函数原型:
BaseType_t xTimerStartFromISR( TimerHandle_t xTimer,
BaseType_t * pxHigherPriorityTaskWoken );
函数参数说明:
- TimerHandle_t xTimer:定时器控制句柄。
- BaseType_t * pxHigherPriorityTaskWoken:定时器守护任务的大部分时间都在阻塞态等
待定时器命令队列的命令。调用函数 xTimerStartFromISR()将会往定时器的命令队列发送
一个启动命令,这很有可能会将定时器任务从阻塞态 移 除 。如果调用函数
xTimerStartFromISR()让定时器任务脱离阻塞态,且定时器守护任务的优先级大于或者等
于当前被中断的任务的优先级,那么 pxHigherPriorityTaskWoken 的值会在函数
xTimerStartFromISR()内部设置为 pdTRUE,然后在中断退出之前执行一次上下文切换。- 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
返回 pdFAILE,成功发送则返回pdPASS。
函数解析:
xTimerStartFromISR()
是函数 xTimerStart()
的中断版本,用于在中断中启动一个先前由函数xTimerCreate()
或xTimerCreateStatic()
创建的软件定时器。
终止定时器函数
函数原型:
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );
函数参数说明:
- TimerHandle_t xTimer:创建定时器成功后返回的控制权柄,也就是其地址;
- TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。在系统开始调用
vTaskStartScheduler()
前调用该函数,那么函数形参无作用。在定时器开始指令成功送达定时器命令队列前,定时器还是处于阻塞状态的。- 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
返回 pdFAILE,成功发送则返回pdPASS。
函数解析:
终止定时器函数,实际上调用xTimerGenericCommand()
函数,该函数含有五个参数、一个返回值,功能是发送命令到定时器命令队列。
终止定时器后,定时器会回归就绪态,等待下一次唤醒。
在中断中停止定时器函数
函数原型:
BaseType_t xTimerStopFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
函数参数说明:
- TimerHandle_t xTimer:定时器控制句柄。
- BaseType_t * pxHigherPriorityTaskWoken:定时器守护任务的大部分时间都在阻塞态等
待定时器命令队列的命令。调用函数 xTimerStartFromISR()将会往定时器的命令队列发送
一个启动命令,这很有可能会将定时器任务从阻塞态 移 除 。如果调用函数
xTimerStartFromISR()让定时器任务脱离阻塞态,且定时器守护任务的优先级大于或者等
于当前被中断的任务的优先级,那么 pxHigherPriorityTaskWoken 的值会在函数
xTimerStartFromISR()内部设置为 pdTRUE,然后在中断退出之前执行一次上下文切换。- 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
返回 pdFAILE,成功发送则返回pdPASS。
函数解析:
该函数用于在中断停止定时器,使其进入就绪态。使用条件与xTimerStartFromISR()
函数类似。
设置定时器执行周期函数
函数原型:
void vTimerSetReloadMode( TimerHandle_t xTimer,
const BaseType_t xAutoReload )
函数参数说明:
- TimerHandle_t xTimer:定时器的控制权柄
- const BaseType_t xAutoReload:定时器新的执行周期。若为pdFALSE则单次执行;否则为pdTRUE,就周期执行。
函数解析:
使用该函数能够重新设置定时器的执行周期。
重启定时器函数
函数原型:
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
函数参数说明:
- TimerHandle_t xTimer:定时器的控制权柄;
- TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。
函数解析:
重新启动一个定时器。
删除定时器函数
函数原型:
BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );
函数参数说明:
- TimerHandle_t xTimer:定时器控制权柄;
- TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。
函数解析:
删除定时器函数,调用该函数能够删除对应定时器工作。
示例
创建两个定时器任务
通过创建链各个定时器任务,完成定时器任务1实现——LED1间隔循环1s闪烁与定时器任务2实现——LED3单次闪烁。
void timeCallBackTask(void);
void timeCallBackTask2(void);
int main(void)
{
TimerHandle_t xTimeHandle[2];
xTimeHandle[0] = xTimerCreate(
(const char *)"task1",// 定时器名字
(TickType_t)1000,// 定时器的周期
pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
(void*)1,// 给定时器分配的唯一ID
(TimerCallbackFunction_t)timeCallBackTask// 定时器的回调函数
);
xTimeHandle[1] = xTimerCreate(
(const char *)"task2",// 定时器名字
(TickType_t)2000,// 定时器的周期
pdFALSE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
(void*)2,// 给定时器分配的唯一ID
(TimerCallbackFunction_t)timeCallBackTask2// 定时器的回调函数
);
if(xTimeHandle[0] && xTimeHandle[1])
{
xTimerStart(xTimeHandle[0],0); //开启定时器
xTimerStart(xTimeHandle[1],0); //开启定时器
}
else
//定时器任务创建失败 打开LED8
changeLedStateByLocation(LED8,ON);
//打开任务调度器
vTaskStartScheduler();
}
/*****************************************
* 函数功能:定时器任务
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask(void)
{
//闪烁LED1
rollbackLedByLocation(LED1);
}
/*****************************************
* 函数功能:定时器任务2
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask2(void)
{
//闪烁LED3
rollbackLedByLocation(LED3);
}
创建一个系统任务来管理两个定时器任务
创建一个系统任务来开启与关闭定时器任务。由于创建完成定时器任务后,任务仍然处于就绪状态,需要开启函数xTimerStart
才能够完成启动,而碰到关闭函数 xTimerStop
定时器任务又会处于就绪状态。
通过不断地开启与关闭,可以实现定时器任务timeCallBackTask与timeCallBackTask2不断地切换,实际上也实现就是LED1与LED2轮流闪烁。
void timeTask1(void);
void timeCallBackTask(void);
void timeCallBackTask2(void);
//任务控制权柄
TaskHandle_t xHandleTsak[4];
// 定时器控制权柄
TimerHandle_t xTimeHandle[2];
int main(void)
{
BaseType_t xReturn[2];
xTimeHandle[0] = xTimerCreate(
(const char *)"task1",// 定时器名字
(TickType_t)1000,// 定时器的周期
pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
(void*)1,// 给定时器分配的唯一ID
(TimerCallbackFunction_t)timeCallBackTask// 定时器的回调函数
);
xTimeHandle[1] = xTimerCreate(
(const char *)"task2",// 定时器名字
(TickType_t)2000,// 定时器的周期
pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
(void*)2,// 给定时器分配的唯一ID
(TimerCallbackFunction_t)timeCallBackTask2// 定时器的回调函数
);
xReturn[0] = xTaskCreate(
(TaskFunction_t )timeTask1,//任务入口函数
(const char *)"timeTask1",//任务名字
(uint16_t)512,//任务栈大小
(void*)NULL,//任务入口参数
1,//任务优先级 优先级越高,任务优先选越高
&xHandleTsak[2]//任务控制块
);
if(xReturn[0] == pdFALSE)
changeLedStateByLocation(LED7,ON);
//打开任务调度器
vTaskStartScheduler();
}
/*****************************************
* 函数功能:定时器管理任务1
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeTask1(void)
{
uint32_t count = 0;
const volatile TickType_t xDelay500ms = pdMS_TO_TICKS( 500UL );
while(1)
{
//LED3闪烁
if(++count % 2)
rollbackLedByLocation(LED3);
//开启定时器任务1 关闭定时器任务2
if(count % 50 == 0)
{
xTimerStart(xTimeHandle[0],0);
xTimerStop(xTimeHandle[1],0);
}
//开启定时器任务2 关闭定时器任务1
if(count % 50 == 25)
{
xTimerStart(xTimeHandle[1],0);
xTimerStop(xTimeHandle[0],0);
}
//非阻塞延时1s
vTaskDelay( xDelay500ms );
}
}
/*****************************************
* 函数功能:定时器任务
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask(void)
{
rollbackLedByLocation(LED1);
}
/*****************************************
* 函数功能:定时器任务2
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask2(void)
{
rollbackLedByLocation(LED2);
}
本文是基于Cortex-M4核心板实现的,若需要移植FreeRTOS到Cortex-M4上可以参考文章【FreeRTOS】在Cortex-M4开发板上移植FreeRTOS并且实现LED灯闪烁(保姆级教程)
小编这里还有一篇关于定时器的文章,也欢迎各位点击观看😉😉😉【FreeRTOS】详细讲解FreeRTOS中任务管理并通过示例讲述其用法
最后 ,也欢迎大家留言或私信交流,大家共同进步!😁😁😁