FreeRTOS的软件定时器的源码分析

一、软件定时器的特性

  1. 定时器的关键成员:周期、回调函数
  2. 定时器的指定类型:一次性、可自动重载定时器
  3. 定时器的状态:运行、休眠
  4. FreeRTOS的软件定时器基于Tick中断实现,但是由于实时系统不允许在内核、中断中执行不确定时间的代码,因此定时器函数被放到了RTOS守护任务中实现

二、RTOS守护任务

  1. 优先级:configTIMER_TASK_PRIORITY,默认是2,高于IDLE任务
  2. 消息队列的长度:configTIMER_QUEUE_LENGTH,默认是10,40字节
  3. 用户调用定时器函数,通过专用的定时器消息队列,发送给RTOS守护任务,在RTOS守护任务中读消息队列,并执行相应的定时器处理函数
  4. prvTimerTask在调用vTaskStartScheduler时被创建

三、prvTimerTask任务

static void prvTimerTask( void *pvParameters )
{
  TickType_t xNextExpireTime;
  BaseType_t xListWasEmpty;
  for( ;; )
  {
    xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );        // 从pxCurrentTimerList获得下一个定时器到期时间,升序排列,头表示第一个到期的定时器
    prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );    // 定时器到期执行prvProcessExpiredTimer,没到期则有时间限制的等待xTimerQueue(最多等xNextExpireTime - xTimeNow)  
    prvProcessReceivedCommands();                                    // 处理接收命令,非阻塞循环读取队列xTimerQueue,处理message,包括启动定时器、停止定时器。修改定时器、删除定时器
  }
}
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow )
{
  // 定时器到期了,将定时器从 pxTimer->xTimerListItem队列中移除
  // 如果自动重载,则重新计算超时时间,再把timer加入队列,并通过xTimerGenericCommand函数发送tmrCOMMAND_START_DONT_TRACE命令
  // 最后执行定时器的回调函数
}

四、定时器相关函数

TimerHandle_t xTimerCreate(	const char * const pcTimerName,			    // 定时器名称
								const TickType_t xTimerPeriodInTicks,   // 定时器周期
								const UBaseType_t uxAutoReload,         // 是否自动重载
								void * const pvTimerID,                 // ID
								TimerCallbackFunction_t pxCallbackFunction )  // 回调函数
{
  Timer_t *pxNewTimer;
  pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) );          // 分配空间
  prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer );  // 初始化
  return pxNewTimer;
}
static void prvInitialiseNewTimer(	const char * const pcTimerName,
									const TickType_t xTimerPeriodInTicks,
									const UBaseType_t uxAutoReload,
									void * const pvTimerID,
									TimerCallbackFunction_t pxCallbackFunction,
									Timer_t *pxNewTimer )
{
  prvCheckForValidListAndQueue();
  pxNewTimer->pcTimerName = pcTimerName;
  pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;
  pxNewTimer->uxAutoReload = uxAutoReload;
  pxNewTimer->pvTimerID = pvTimerID;
  pxNewTimer->pxCallbackFunction = pxCallbackFunction;
  vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );    // 初始化链表节点
}
// 定时器删除函数
#define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) )
// 定时器复位函数
#define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
// 定时器启动函数
#define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
// 定时器停止函数
#define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) )
// 定时器更改定时周期函数
#define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) )

五、守护任务中对不同定时器函数命令的处理

  1. tmrCOMMAND_START、tmrCOMMAND_RESET、tmrCOMMAND_START_FROM_ISR、tmrCOMMAND_RESET_FROM_ISR、tmrCOMMAND_START_DONT_TRACE(重载),从list移除定时器,调用prvInsertTimerInActiveList将定时器加入pxCurrentTimerList中
  2. tmrCOMMAND_STOP、tmrCOMMAND_STOP_FROM_ISR,从list中移除定时器
  3. tmrCOMMAND_CHANGE_PERIOD、tmrCOMMAND_CHANGE_PERIOD_FROM_ISR,从list移除定时器,更新时间通过prvInsertTimerInActiveList加入list
  4. tmrCOMMAND_DELETE,,从list移除定时器,在vPortFree( pxTimer )释放空间
posted @ 2025-07-14 21:46  gramming  阅读(114)  评论(0)    收藏  举报