软件定时器允许设置一段时间,当设置的时间到达之后就执行指定的功能函数,被定时器调用的这个功能函数叫做定时器的回调函数。回调函数的两次执行间隔叫做定时器的定时周期,简而言之,当定时器的定时周期到了以后就会执行回调函数。
软件定时器的回调函数是在定时器服务任务中执行的,所以一定不能在回调函数中调用任何会阻塞任务的API函数。比如,定时器回调函数中不能调佣vTaskDelay()、vTaskDelayUnti(),还有一些访问队列或者信号量的非零阻塞时间的API函数。
软件定时器时一个可选项,不属于FreeRTOS内核的功能,它是由定时器服务(或Daemon)任务来提供的。FreeRTOS中所使用的定时器相关API函数,大部分是使用FreeRTOS的队列发送命令给定时器服务任务的。这个队列叫做定时器命令队列。定时器命令队列是提供给FreeRTOS的软件定时器使用的,用户不能直接使用。
软件定时器的相关宏定义配置在FreeRTOSConfig.h文件中。
#define configUSE_TIMERS 1 //软件定时器开启
#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) //优先级
#define configTIMER_QUEUE_LENGTH 10 //定时器命令队列长度
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) //定时器服务任务的任务堆栈大小。
软件定时器分为两种:单次定时器和周期定时器,单次定时器的话定时器回调函数就执行一次,然后定时器就会停止。单次定时器可以再次手动重新启动,但是不能自动重启。周期定时器一旦启动后,当定时器超时执行完回调函数以后会自动重新启动,这样回调函数就会周期性的执行。
有时候需要在定时器运行的时候重新复位软件定时器,复位软件定时器的话会清零计数,并且重新计算定时周期到达的时间点。
1.xTimerCreate() 创建软件定时器(动态内存)
此函数用于创建一个软件定时器,所需要的内存通过动态内存管理方法分配。新创建的软件定时器处于休眠状态,也就是未运行的状态。
函数原型:
TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
参数:
pcTimerName:软件定时器名字,名字是一串字符串,用于调试使用
xTimerPeriodInTicks:软件定时器的定时器周期,单位是时钟节拍数,可以通过设置(周期ms/portTICK_PERIOD_MS)转换为需要的时间周期,单位ms。
uxAutoReload:设置定时器模式。pdTRUE 为周期定时器,pdFALSE 为单次定时器。
pvTimerID:定时器ID号,一般情况下每个定时器都有一个回调函数,FreeRTOS支持多个定时器共用一个回调函数,在回调函数中根据定时器ID号来处理不同的定时器。
pxCallbackFunction:定时器回调函数。
返回值:
NULL:创建失败
其他值:创建成功,返回定时器句柄。
实例:
TimerHandle_t xTimer_test;
xTimer_test = xTimerCreate("Test timer", /* Just a text name, not used by the kernel. */
(1 * 50 / portTICK_PERIOD_MS), /* The timer period in ticks. */
pdFALSE, /* The timers will auto-reload themselves when they expire. */
NULL, /* Assign each timer a unique id equal to its array index. */
test_timer_callback /* Each timer calls the same callback when it expires. */
);
2.xTimerCreateStatic()创建软件定时器(静态内存)
此函数用于创建一个软件定时器,所需要的内存需要用户自行分配。新创建的软件定时器处于休眠状态。
函数原型:
TimerHandle_t xTimerCreateStatic( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t *pxTimerBuffer )
参数:
pcTimerName:软件定时器名字,名字是一串字符串,用于调试使用
xTimerPeriodInTicks:软件定时器的定时器周期,单位是时钟节拍数,可以通过设置(周期ms/portTICK_PERIOD_MS)转换为需要的时间周期,单位ms。
uxAutoReload:设置定时器模式。pdTRUE 为周期定时器,pdFALSE 为单次定时器。
pvTimerID:定时器ID号,一般情况下每个定时器都有一个回调函数,FreeRTOS支持多个定时器共用一个回调函数,在回调函数中根据定时器ID号来处理不同的定时器。
pxCallbackFunction:定时器回调函数。
pxTimerBuffer :指向一个StaticTimer_t类型的变量,用来保存定时器结构体。
返回值:
NULL:创建失败
其他值:创建成功,返回定时器句柄。
实例:
static StaticTimer_t xTimerBuffer;
TimerHandle_t xTimer ;
xTimer = xTimerCreateStatic( "T1", // Text name for the task. Helps debugging only. Not used by FreeRTOS.
xTimerPeriod, // The period of the timer in ticks.
pdTRUE, // This is an auto-reload timer.
( void * ) &uxVariableToIncrement, // A variable incremented by the software timer's callback function
prvTimerCallback, // The function to execute when the timer expires.
&xTimerBuffer ); // The buffer that will hold the software timer structure.
3.xTimerStart() 启动软件定时器
启动软件定时器。软件定时器在创建好之后会处于等待状态,调用该函数之后会将定时器转为运行状态。如果软件定时器正在运行的话,调用函数xTimerStart()的结果和xTimerReset()是一样的。
函数原型:
#define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
参数:
xTimer:要开启的软件定时器的句柄
xTicksToWait :设置阻塞时间,调用该函数开启软件定时器其实就是向定时器命令队列发送一条tmrCOMMAND_START命令,既然是向队列发送消息,那肯定会涉及到入队阻塞时间的设置。
返回值:
pdPASS:启动成功
pdFALL:启动失败。
实例:
if( xTimerStart( xTimers[ x ], 0 ) != pdPASS )
{
// The timer could not be set into the Active state.
}
4.xTimerStartFromISR()在中断中启动软件定时器
函数原型:
#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )
参数:
xTimer:要开启的软件定时器的句柄
pxHigherPriorityTaskWoken:标记退出此函数后是否进行任务切换。当设置为pdTRUE时,退出中断服务函数之前一定会进行一次任务切换。
返回值:
pdPASS:启动成功
pdFALL:启动失败。
实例:
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if( xTimerStartFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS )
{
// The start command was not executed successfully. Take appropriate
// action here.
}
5.xTimerStop()停止软件定时器
此函数用于停止一个软件定时器,此函数用于任务中。
函数原型:
#define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) )
参数:
xTimer:要停止的软件定时器的句柄
xTicksToWait :设置阻塞时间,调用该函数停止软件定时器其实就是向定时器命令队列发送一条tmrCOMMAND_STOP命令,既然是向队列发送消息,那肯定会涉及到入队阻塞时间的设置。
返回值:
pdPASS:停止成功
pdFALL:停止失败。
实例:
xTimerStop( pxTimer, 0 );
6.xTimerStopFromISR()中断中停止软件定时器
函数原型:
#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U )
参数:
xTimer:要停止的软件定时器的句柄
pxHigherPriorityTaskWoken:标记退出此函数后是否进行任务切换。当设置为pdTRUE时,退出中断服务函数之前一定会进行一次任务切换。
返回值:
pdPASS:停止成功
pdFALL:停止失败。
实例:
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if( xTimerStopFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS )
{
// The stop command was not executed successfully. Take appropriate
// action here.
}
7.xTimerReset() 复位软件定时器
复位一个软件定时器,此函数只能用于任务中,不能用于中断中。
函数原型:
#define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
参数:
xTimer:要复位的软件定时器的句柄
xTicksToWait :设置阻塞时间,调用该函数停止软件定时器其实就是向定时器命令队列发送一条tmrCOMMAND_RESET命令,既然是向队列发送消息,那肯定会涉及到入队阻塞时间的设置。
返回值:
pdPASS:复位成功
pdFALL:复位失败。
实例:
if( xTimerReset( xBacklightTimer, 100 ) != pdPASS )
{
// The reset command was not executed successfully. Take appropriate
// action here.
}
8.xTimerResetFromISR()中断中复位软件定时器
函数原型:
#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )
参数:
xTimer:要复位的软件定时器的句柄
pxHigherPriorityTaskWoken:标记退出此函数后是否进行任务切换。当设置为pdTRUE时,退出中断服务函数之前一定会进行一次任务切换。
返回值:
pdPASS:复位成功
pdFALL:复位失败。
实例:
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if( xTimerResetFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS )
{
// The reset command was not executed successfully. Take appropriate
// action here.
}
代码验证:
创建一个任务,在任务中开启一个1秒周期的定时器,定时器定时打印。
static TimerHandle_t xTimer_test;
static const uint32_t timer_id = 2;
任务1
static void vTestTask1_H(void *pvParameters)
{
xTimer_test = xTimerCreate("Test timer", /* Just a text name, not used by the kernel. */
(1000 / portTICK_PERIOD_MS), /* The timer period in ticks. */
pdTRUE, /* The timers will auto-reload themselves when they expire. */
(void *)timer_id, /* Assign each timer a unique id equal to its array index. */
vTimerCallback /* Each timer calls the same callback when it expires. */
);
if(xTimer_test == NULL)
{
LOG_I(common,"[TASK1]:Timer create fail"); //失败
}
else
{
LOG_I(common,"[TASK1]:Timer create sucess"); //成功
if( xTimerStart( xTimer_test, 0 ) != pdPASS )
{
LOG_I(common,"[TASK1]:Timer start fail"); //失败
}
}
vTaskDelete(NULL);
}
定时器回调函数:
void vTimerCallback( TimerHandle_t pxTimer )
{
configASSERT( pxTimer );
static uint32_t time_cnt = 0;
time_cnt++;
int32_t lArrayIndex = 0;
lArrayIndex = ( int32_t ) pvTimerGetTimerID( pxTimer );
LOG_I(common,"[DEBUG]:timer name:%stimer id:%d",pcTimerGetName(pxTimer),lArrayIndex);
if(lArrayIndex = timer_id)
{
LOG_I(common,"[DEBUG]:time cnt:%d",time_cnt);
}
}
结果:
在创建定时器的时候,设置了timer_id为2。在回调函数中,通过pvTimerGetTimerID()函数来获取timer_id,通过pcTimerGetName()函数来获取定时器名字,并打印出来。结果可以看到都是相互对应的。并且定时器时个周期定时器,间隔1秒钟会打印一次。