@(嵌入式)
简述
考虑平台硬件定时器个数限制的, FreeRTOS 通过一个 Daemon 任务(启动调度器时自动创建)管理软定时器, 满足用户定时需求. Daemon 任务会在其执行期间检查用户启动的时间周期溢出的定时器,并调用其回调函数。
对于硬件定时器的中断服务程序, 我们知道不应该在里面执行复杂,可能导致阻塞的工作,相应的, 虽然软定时器实际是在定时Daemon 任务中执行,但是阻塞的话会导致其他定时器调用被延时, 所以实际使用也应该避免。
软定时器是通过一个任务来辅助实现,该功能时刻裁剪的 , 只有设置 FreeRTOSConfig.h
中configUSE_TIMERS == 1
将相关代码编译进来, 才能正常使用相关功能。
分析的源码版本是 v9.0.0
使用定时器
开始先介绍下如何在自己的工程中使用 FreeRTOS 的软件定时器。
配置定时器服务任务
程序中需要使用到软件定时器, 需要先在 FreeRTOSConfig.h
中正确配置如下宏 :
* configUSE_TIMERS
是否编译定时器相关代码, 如需要使用定时器, 设置为 1
* configTIMER_TASK_PRIORITY
设置定时器Daemon 任务优先级, 如果优先级太低, 可能导致定时器无法及时执行
* configTIMER_QUEUE_LENGTH
设置定时器Daemon 任务的命令队列深度, 设置定时器都是通过发送消息到该队列实现的。
* configTIMER_TASK_STACK_DEPTH
设置定时器Daemon 任务的栈大小
创建 启动 停止定时器
如下示例代码所示
TimerHandle_t xTimerUser; // 定义句柄
// 定时器回调函数格式
void vTimerCallback( TimerHandle_t xTimer )
{
// do something no block
// 获取溢出次数
static uin32_t ulCount = ( uint32_t ) pvTimerGetTimerID( xTimer );
// 累积溢出次数
++ulCount;
// 更新溢出次数
vTimerSetTimerID( xTimer, ( void * ) ulCount );
if (ulCount == 10) {
// 停止定时器
xTimerStop( xTimer, 0 );
}
}
void fun()
{
// 申请定时器, 配置
xTimerUser = xTimerCreate
/*调试用, 系统不用*/
("Timer's name",
/*定时溢出周期, 单位是任务节拍数*/
100,
/*是否自动重载, 此处设置周期性执行*/
pdTRUE,
/*记录定时器溢出次数, 初始化零, 用户自己设置*/
( void * ) 0,
/*回调函数*/
vTimerCallback);
if( xTimerUser != NULL ) {
// 启动定时器, 0 表示不阻塞
xTimerStart( xTimerUser, 0 );
}
}
如上所示, 调用函数 xTimerCreate
申请,配置定时器, 通过 xTimerStart
启动定时器, 当定时器计数溢出时, 系统回调注册的函数。
定时器可以设置为一次性 One-shot 或者自动重载 Auto-reload 两种, 第一种溢出后停止定时器, 第二种溢出后会再次启动定时器。
修改定时器
在申请定时器的时候设置的定时器周期, 可以通过函数 xTimerChangePeriod
修改, 如下示例 :
void vAFunction_2( TimerHandle_t xTimer )
{
// 判断定时器是否处于运行状态
if( xTimerIsTimerActive( xTimer ) != pdFALSE )
{
/* xTimer is active, do something. */
}
else
{
// 处于这个状态的定时器, 可能由于 :
// 1 定时器 create 后没有start
// 2 一次性定时器执行溢出后
// 修改定时器周期
if( xTimerChangePeriod( xTimer,
/*修改定时周期*/
500 / portTICK_PERIOD_MS,
/*允许阻塞最大时间 100 ticks*/
100 ) == pdPASS )
{
// update fail
// 阻塞 100 tick 仍然无法发送命令