当前位置: 首页 > news >正文

【freertos-kernel】timer

文章目录

  • 结构体
    • Timer_t
    • DaemonTaskMessage_t
  • prvTimerTask
    • prvGetNextExpireTime
    • prvProcessTimerOrBlockTask
      • prvSampleTimeNow
        • prvSwitchTimerLists
        • prvProcessExpiredTimer
        • prvReloadTimer
        • prvInsertTimerInActiveList
      • vQueueWaitForMessageRestricted
    • prvProcessReceivedCommands
  • 其他函数说明
    • xTimerCreate
    • xTimerGenericCommandFrom(Task/ISR)

结构体

Timer_t

typedef struct tmrTimerControl
{const char * pcTimerName;	//调试用的定时器名称ListItem_t xTimerListItem;	//插入定时器链表的节点TickType_t xTimerPeriodInTicks; //定时器周期void * pvTimerID;	//用户自定义 IDportTIMER_CALLBACK_ATTRIBUTE TimerCallbackFunction_t pxCallbackFunction;//定时器到期时调用的回调函数#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxTimerNumber;	//调试用编号#endifuint8_t ucStatus;	//状态标志(是否激活、是否周期性等)
} xTIMER;typedef xTIMER Timer_t;

DaemonTaskMessage_t

发给守护任务的消息结构体

typedef struct tmrTimerQueueMessage
{BaseType_t xMessageID; /**< The command being sent to the timer service task. */union{TimerParameter_t xTimerParameters;#if ( INCLUDE_xTimerPendFunctionCall == 1 )CallbackParameters_t xCallbackParameters;#endif /* INCLUDE_xTimerPendFunctionCall */} u;
} DaemonTaskMessage_t;typedef struct tmrTimerParameters
{TickType_t xMessageValue;Timer_t * pxTimer;
} TimerParameter_t;typedef struct tmrCallbackParameters
{portTIMER_CALLBACK_ATTRIBUTEPendedFunction_t pxCallbackFunction;void * pvParameter1;uint32_t ulParameter2;
} CallbackParameters_t;

prvTimerTask

还记得吗,调度器启动的时候会创建一个定时器任务xTimerCreateTimerTask,这个任务的函数是prvTimerTask,这是一个守护任务,由内核自动创建和管理,运行在后台,处理所有通过 xTimerStart()、xTimerStop() 等 API 发送的命令,并在定时器到期时调用回调函数。

#define portTASK_FUNCTION( vFunction, pvParameters )  void vFunction( void * pvParameters )//宏展开就是static void prvTimerTask(void *pvParameters )
static portTASK_FUNCTION( prvTimerTask, pvParameters )
{TickType_t xNextExpireTime;BaseType_t xListWasEmpty;( void ) pvParameters;#if ( configUSE_DAEMON_TASK_STARTUP_HOOK == 1 ){	//可选的启动钩子函数vApplicationDaemonTaskStartupHook();}#endif /* configUSE_DAEMON_TASK_STARTUP_HOOK */for( ; configCONTROL_INFINITE_LOOP(); ){	//获取下一个定时器到期时间xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );//处理到期定时器或进入阻塞等待prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );//处理接收到的定时器命令prvProcessReceivedCommands();}
}

在真正创建任务之前,prvCheckForValidListAndQueue初始化了定时器相关的链表,xActiveTimerList1xActiveTimerList2。两个指向链表的指针,pxCurrentTimerList,当前正在使用的链表,正常情况下使用 pxCurrentTimerList;pxOverflowTimerList,tick 计数器存储长度是有限的,32位或者64位等等,当发生时间溢出的时候,就用这个链表。

prvGetNextExpireTime

static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty )
{TickType_t xNextExpireTime;*pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList );if( *pxListWasEmpty == pdFALSE ){	//链表中的定时器是按照到期时间升序排的,链表头就是最早到期的那个定时器 。//如果链表不为空就获取第一个定时器的到期时间xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );}else{xNextExpireTime = ( TickType_t ) 0U;}return xNextExpireTime;
}

prvProcessTimerOrBlockTask

static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime,BaseType_t xListWasEmpty )
{TickType_t xTimeNow;BaseType_t xTimerListsWereSwitched;vTaskSuspendAll();{xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );//返回当前系统时间,同时通过设置指针参数是否发生了链表切换if( xTimerListsWereSwitched == pdFALSE )//没有发生链表切换{if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) )//判断是否有到期的定时器{( void ) xTaskResumeAll();prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); //处理所有已到期的定时器}else//没有到期定时器则阻塞等待下一次触发{if( xListWasEmpty != pdFALSE ){	//检查溢出链表是否也为空xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList );}vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty );//让定时器服务任务进入阻塞状态 if( xTaskResumeAll() == pdFALSE ){taskYIELD_WITHIN_API();}}}else{( void ) xTaskResumeAll();}}
}

prvSampleTimeNow

static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched )
{TickType_t xTimeNow;PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U;xTimeNow = xTaskGetTickCount();//获取当前系统运行的时间if( xTimeNow < xLastTime ) //时间溢出{prvSwitchTimerLists(); //交换主链表和溢出链表*pxTimerListsWereSwitched = pdTRUE;}else{*pxTimerListsWereSwitched = pdFALSE;}xLastTime = xTimeNow;return xTimeNow;
}
prvSwitchTimerLists

tmrMAX_TIME_BEFORE_OVERFLOW (TickType_t) (-1),tick计数的最大值。

static void prvSwitchTimerLists( void )
{TickType_t xNextExpireTime;List_t * pxTemp;while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE ){	//循环处理主链表中所有到期定时器xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );prvProcessExpiredTimer( xNextExpireTime, tmrMAX_TIME_BEFORE_OVERFLOW ); //执行到期定时器的回调函数}//交换链表pxTemp = pxCurrentTimerList;pxCurrentTimerList = pxOverflowTimerList;pxOverflowTimerList = pxTemp;
}
prvProcessExpiredTimer

从当前链表中取出最早到期的定时器,移除它,并根据其类型(单次/周期)决定是否重新加载,最后调用其回调函数。

static void prvProcessExpiredTimer( const TickType_t xNextExpireTime,const TickType_t xTimeNow )
{Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );( void ) uxListRemove( &( pxTimer->xTimerListItem ) );if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0U ){prvReloadTimer( pxTimer, xNextExpireTime, xTimeNow );}else{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );}pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
}
prvReloadTimer

为周期性定时器重新安排下一次触发时间,并在必要时多次调用回调函数,确保不会因为系统延迟而漏掉任何一次触发。

static void prvReloadTimer( Timer_t * const pxTimer,TickType_t xExpiredTime,const TickType_t xTimeNow )
{while( prvInsertTimerInActiveList( pxTimer, ( xExpiredTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xExpiredTime ) != pdFALSE ){xExpiredTime += pxTimer->xTimerPeriodInTicks;pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );}
}

系统可能由于中断延迟、任务调度等原因,导致定时器回调没有及时执行,此时,原本应该在 xExpiredTime + period 到期的定时器,实际上已经被跳过了,所以必须不断累加周期时间,直到找到一个未来的时间点,再把它插入链表,同时,对于每个“已错过”的时间点,都调用一次回调函数(模拟“补发”)

prvInsertTimerInActiveList

将一个定时器插入到当前活动链表或溢出链表中,返回值表示是否已经过期需要立即执行回调

static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer,const TickType_t xNextExpiryTime,const TickType_t xTimeNow,const TickType_t xCommandTime )
{BaseType_t xProcessTimerNow = pdFALSE;listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime );//设置链表项的值listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );		//设置链表项的拥有者if( xNextExpiryTime <= xTimeNow ){if( ( ( TickType_t ) ( xTimeNow - xCommandTime ) ) >= pxTimer->xTimerPeriodInTicks )xProcessTimerNow = pdTRUE;}else{vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) );}}else{if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ){xProcessTimerNow = pdTRUE;}else{vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) );}}return xProcessTimerNow;
}

在这里插入图片描述
在这里插入图片描述

vQueueWaitForMessageRestricted

让任务在无数据可读时进入阻塞状态

void vQueueWaitForMessageRestricted( QueueHandle_t xQueue,TickType_t xTicksToWait,const BaseType_t xWaitIndefinitely )
{Queue_t * const pxQueue = xQueue;prvLockQueue( pxQueue );if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U ){vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait, xWaitIndefinitely );}prvUnlockQueue( pxQueue );
}

prvProcessReceivedCommands

用于处理命令消息
prvProcessReceivedCommands

static void prvProcessReceivedCommands( void )
{DaemonTaskMessage_t xMessage = { 0 };Timer_t * pxTimer;BaseType_t xTimerListsWereSwitched;TickType_t xTimeNow;while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ){#if ( INCLUDE_xTimerPendFunctionCall == 1 ){if( xMessage.xMessageID < ( BaseType_t ) 0 ){const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters );configASSERT( pxCallback );pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 );}}#endif /* INCLUDE_xTimerPendFunctionCall */if( xMessage.xMessageID >= ( BaseType_t ) 0 ){pxTimer = xMessage.u.xTimerParameters.pxTimer;if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ){( void ) uxListRemove( &( pxTimer->xTimerListItem ) );}xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );switch( xMessage.xMessageID ){case tmrCOMMAND_START:case tmrCOMMAND_START_FROM_ISR:case tmrCOMMAND_RESET:case tmrCOMMAND_RESET_FROM_ISR:pxTimer->ucStatus |= ( uint8_t ) tmrSTATUS_IS_ACTIVE;if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE ){if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0U ){prvReloadTimer( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow );}else{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );}pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );}break;case tmrCOMMAND_STOP:case tmrCOMMAND_STOP_FROM_ISR:pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );break;case tmrCOMMAND_CHANGE_PERIOD:case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR:pxTimer->ucStatus |= ( uint8_t ) tmrSTATUS_IS_ACTIVE;pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue;configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) );( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow );break;case tmrCOMMAND_DELETE:#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ){if( ( pxTimer->ucStatus & tmrSTATUS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) 0 ){vPortFree( pxTimer );}else{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );}}#else /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );}#endif /* configSUPPORT_DYNAMIC_ALLOCATION */break;default:break;}}}
}

其他函数说明

xTimerCreate

初始化一个Timer_t以及他的链表项xTimerListItem。具体代码很简单,就不贴了。

xTimerGenericCommandFrom(Task/ISR)

初始化一个DaemonTaskMessage_t,发送到xTimerQueue里。
xTimerStart xTimerStop等等最上层的函数就是调用xTimerGenericCommandFrom(Task/ISR)发送不同的命令(上面解析命令里看过)。

http://www.xdnf.cn/news/871885.html

相关文章:

  • 嵌入式链表操作原理详解
  • 《小明的一站式套餐服务平台:抽象工厂模式》
  • 线夹金具测温在线监测装置:电力设备安全运行的“隐形卫士”
  • GAMES202-高质量实时渲染(Real-Time Physically-based Materials)
  • C++课设:通讯录管理系统(vector、map协作实现)
  • 在VSCode中开发一个uni-app项目
  • 企业级网络安全攻防全景指南:从渗透测试到防御体系建设
  • 基于深度学习(Unet和SwinUnet)的医学图像分割系统设计与实现:超声心脏分割
  • 6. MySQL基本查询
  • Elasticsearch集群状态为RED且存在未分配分片问题排查诊断
  • GitHub 趋势日报 (2025年06月03日)
  • 小白的进阶之路系列之十四----人工智能从初步到精通pytorch综合运用的讲解第七部分
  • Delphi中实现批量插入数据
  • 5分钟了解,Mysql事务事务隔离级别
  • tensorflow image_dataset_from_directory 训练数据集构建
  • 使用 Python 的 psutil 库进行系统资源监控
  • Webpack搭建本地服务器
  • Unity3D 逻辑代码性能优化策略
  • Linux kill 暂停命令
  • 跟着deepseek浅学分布式事务(2) - 两阶段提交(2PC)
  • 人工智能:网络安全的“智能守护者”
  • 录制mp4
  • Kali Linux 安全工具解析
  • Ros(控制机器人运动)
  • .NET 原生驾驭 AI 新基建实战系列(四):Qdrant ── 实时高效的向量搜索利器
  • JVMTI 在安卓逆向工程中的应用
  • 【Oracle】存储过程
  • 纹理压缩格式优化
  • OpenCV 自带颜色表实现各种滤镜
  • 【Netty源码分析总结】