FreeRTOS源码分析:软件定时器-Timer

FreeRTOS 软件定时器(Timer)源码分析

FreeRTOS 的软件定时器是一种基于系统节拍(tick)的机制,允许用户创建和管理定时任务。以下是其核心实现逻辑和关键源码解析:


软件定时器的工作原理

FreeRTOS 的软件定时器由两个核心组件构成:

  1. 定时器服务任务(Daemon Task):一个独立的 FreeRTOS 任务,负责处理定时器的到期回调。
  2. 定时器命令队列:用于接收其他任务或中断发送的定时器操作命令(如启动、停止、重置等)。

定时器管理通过 xTimerCreate() 创建,通过 xTimerStart() 等 API 触发操作,实际执行由服务任务完成。


关键数据结构

typedef struct tmrTimerControl
{
    const char *pcTimerName;           // 定时器名称(调试用)
    ListItem_t xTimerListItem;         // 链表节点,用于插入延迟队列
    TickType_t xTimerPeriodInTicks;    // 定时周期(tick数)
    UBaseType_t uxAutoReload;          // 是否为自动重载定时器
    void *pvTimerID;                   // 用户标识符(ID)
    TimerCallbackFunction_t pxCallbackFunction; // 回调函数
} xTIMER;

定时器通过链表组织,服务任务按到期时间排序管理。


定时器相关API源码分析:

函数

描述

xTimerCreate()

动态方式创建软件定时器

xTimerStart()

开启软件定时器定时

xTimerStartFromISR()

在中断中开启软件定时器定时

xTimerStop()

停止软件定时器定时

xTimerStopFromISR()

在中断中停止软件定时器定时

主要分析以上函数.

一.xTimerCreate()
/************************
参数: 
        1.定时器名字(自己随便取)
        2.时间间隔/ms
        3.是否重载(单次还是周期性)
        4.回调函数地址
************************/
TimerHandle_t xTimerCreate( const char * const pcTimerName, 
                            const TickType_t xTimerPeriodInTicks,
                            const BaseType_t xAutoReload,
                            void * const pvTimerID,
                            TimerCallbackFunction_t pxCallbackFunction )
{
      /****************************分配timer结构体地址********************/  
      Timer_t * pxNewTimer;                         
      pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); 
      /******************************************************************/ 
      
      /***************************如果分配成功***************************/
      if( pxNewTimer != NULL )
      {
          /**********设置当前定时器状态*************/      
          pxNewTimer->ucStatus = 0x00;
          /************timer结构体配置,创建定时器任务的消息队列**********/
          prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, xAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer );
      }

      return pxNewTimer;
}
二.xTimerStart()/xTimerStop()/xTimerStartFromISR()/xTimerStopFromISR()
#define xTimerStart( xTimer, xTicksToWait ) \
    xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )

#define xTimerStop( xTimer, xTicksToWait ) \
    xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) )

#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) \
    xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )

#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) \
    xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U )



BaseType_t xTimerGenericCommand( TimerHandle_t xTimer,                    //定时器任务句柄     
                                 const BaseType_t xCommandID,             //开启还是关闭?
                                 const TickType_t xOptionalValue,         //
                                 BaseType_t * const pxHigherPriorityTaskWoken,//是否切换上下文
                                 const TickType_t xTicksToWait )          //阻塞时间
    {
        BaseType_t xReturn = pdFAIL;
        DaemonTaskMessage_t xMessage;                //定时器任务(守护进程)用的消息队列

        configASSERT( xTimer );                      //断言,要创建成功才能使用

       
        if( xTimerQueue != NULL )                    //xTimerQueue在TimerCreate中分配
        {
            /***************配置消息结构体,发送给xTimerQueue******************/
            xMessage.xMessageID = xCommandID;
            xMessage.u.xTimerParameters.xMessageValue = xOptionalValue;
            xMessage.u.xTimerParameters.pxTimer = xTimer;
            
            /****************依据xCommandID 决定消息发送方式******************/
            if( xCommandID < tmrFIRST_FROM_ISR_COMMAND )
            {
                if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
                {
                    xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait );
                }
                else
                {
                    xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY );
                }
            }
            else
            {
                xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );
            }
            /************************消息添加到队列尾************************/
            traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn );
        }
        else                                       //没有消息队列
        {
            mtCOVERAGE_TEST_MARKER();
        }

        return xReturn;
    }
  1. 无论是否是中断服务程序,Tmer的start和stop都是由xTimerGenericCommand()函数完成,而此函数主要通过调用QueueSend函数完成对定时器服务任务传递指令
  2. 服务任务的主逻辑在 prvTimerTask() 函数中(位于 timers.c),核心流程如下:
  • 检查定时器队列:从 xTimerQueue 接收命令(如启动、停止)。
  •  处理到期定时器:遍历链表,检查当前时间是否达到定时器的 xTimerListItem.xItemValue(到期时间)。
  •  执行回调:调用 pxCallbackFunction,若为自动重载定时器则重新插入链表。

    回调函数在服务任务上下文中运行,需注意:避免阻塞操作(可能影响其他定时器)。不可调用 vTaskDelete(NULL) 终止服务任务。

void prvTimerTask(void *pvParameters)
{
    TickType_t xNextExpireTime;
    BaseType_t xListWasEmpty;
    for (;;)
    {
        xQueueReceive(xTimerQueue, &xMessage, portMAX_DELAY);
        // 处理命令(如 xTimerStart() 发送的命令)
        prvProcessReceivedCommands();
        // 处理到期定时器
        prvProcessExpiredTimers(xNextExpireTime);
    }
}


    配置选项

    关键配置宏(在 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. 定时精度:依赖系统节拍,最小间隔为 1 tick。
    2. 上下文限制xTimerStart() 等 API 不可在中断中直接调用,需使用 xTimerStartFromISR()
    3. 资源消耗:每个定时器占用约 40 字节 RAM(依赖架构),服务任务增加调度开销。

    通过分析源码可优化定时器使用场景,例如高精度需求需结合硬件定时器。

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值