2026/5/13 12:50:55
网站建设
项目流程
ui设计的网站,企业网站推广在哪里办,免费医疗网站模板,做网站的心得体会一、软件定时器的特性软件定时器是由FreeRTOS内核提供的纯软件功能#xff0c;通过守护任务#xff08;prvTimerTask#xff09;统一管理所有定时器#xff0c;不依赖于硬件定时器外设。软件定时器可以让某个任务只执行一次#xff08;一次性#xff1a;启动后#xff0…一、软件定时器的特性软件定时器是由FreeRTOS内核提供的纯软件功能通过守护任务prvTimerTask统一管理所有定时器不依赖于硬件定时器外设。软件定时器可以让某个任务只执行一次一次性启动后回调函数只会被调用一次也可以让任务周期性执行自动加载定时器启动后时间到之后会自动启动它使得回调函数被周期性得调用。当FreeRTOS的配置项configUSE_TIMERS被设置为1时在启动调度器时会自动创建守护任务RTOS Damemon Task在传统定时器设计中每次发生硬件节拍中断时中断处理函数会立即检查是否有定时器超时。如果发现超时会直接在中断上下文中调用对应的定时器函数。但这种方式存在明显问题若定时器任务函数执行时间过长会阻塞整个中断系统影响系统的实时响应。FreeRTOS采用了更高效的机制当定时器超时中断函数通过写队列来唤醒守护任务。守护任务中一直不断的在读队列一旦读取到超时消息的队列便从队列中读取并处理然后在守护任务的上下文中执行定时器任务函数。这样定时器任务函数的执行不再受中断环境限制增强了系统的稳定性和响应性。我们自己编写的任务函数要使用定时器时是通过定时器命令队列(timer command queue)与守护任务交互的。守护任务的优先级为configTIMER_TASK_PRIORITY二、软件定时器的相关函数源码研究说明这里只研究常见的、常用的函数源码2.1定时器控制块结构体tmrTimerControl/* 该结构体用于管理软件定时器的所有属性 */ typedef struct tmrTimerControl { const char *pcTimerName; /* 定时器名称字符串 */ /* 用于将定时器插入内核管理的链表中如就绪链表或阻塞链表 */ ListItem_t xTimerListItem; /* 定时器周期启动定时器和运行回调函数两者的的间隔以 Tick 为单位 */ TickType_t xTimerPeriodInTicks; /* 自动重载标志设置为 pdTRUE 时为周期性定时器。 设置为 pdFALSE 时为一次性定时器 */ UBaseType_t uxAutoReload; void *pvTimerID; /* 定时器标识 ID */ /* 回调函数指针指向用户定义的函数。定时器超时时内核将调用此函数处理超时事件 */ TimerCallbackFunction_t pxCallbackFunction; /* 当启用“跟踪功能”时分配定时器编号用于内核调试识别定时器 */ #if( configUSE_TRACE_FACILITY 1 ) UBaseType_t uxTimerNumber; #endif /* 仅在同时启用静态和动态分配时存在 */ /* 静态分配标志设置为 pdTRUE 表示定时器内存由用户静态分配删除定时器时不尝试释放内存否则为动态分配删除时需要释放内存 */ #if( ( configSUPPORT_STATIC_ALLOCATION 1 ) ( configSUPPORT_DYNAMIC_ALLOCATION 1 ) ) uint8_t ucStaticallyAllocated; #endif } xTIMER; typedef xTIMER Timer_t;2.2定时器创建函数与队列一样创建定时器有动态分配内存 和 静态分配内存。原型如下/* 使用动态分配内存的方法创建定时器 * pcTimerName:定时器名字, 用处不大, 尽在调试时用到 * xTimerPeriodInTicks: 周期, 以Tick为单位 * uxAutoReload: 类型, pdTRUE表示自动加载, pdFALSE表示一次性 * pvTimerID: 回调函数可以使用此参数, 比如分辨是哪个定时器 * pxCallbackFunction: 回调函数 * 返回值: 成功则返回TimerHandle_t, 否则返回NULL */ TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ); /* 使用静态分配内存的方法创建定时器 * pcTimerName:定时器名字, 用处不大, 尽在调试时用到 * xTimerPeriodInTicks: 周期, 以Tick为单位 * uxAutoReload: 类型, pdTRUE表示自动加载, pdFALSE表示一次性 * pvTimerID: 回调函数可以使用此参数, 比如分辨是哪个定时器 * pxCallbackFunction: 回调函数 * pxTimerBuffer: 传入一个StaticTimer_t结构体, 将在上面构造定时器 * 返回值: 成功则返回TimerHandle_t, 否则返回NULL */ TimerHandle_t xTimerCreateStatic(const char * const pcTimerName, TickType_t xTimerPeriodInTicks, UBaseType_t uxAutoReload, void * pvTimerID, TimerCallbackFunction_t pxCallbackFunction, StaticTimer_t *pxTimerBuffer );这里只分析动态创建的源码TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) { Timer_t *pxNewTimer; //指向新定时器控制块的指针 /* 动态分配定时器结构体内存大小为Timer_t结构体的大小 * 如果内存分配失败pvPortMalloc返回NULL */ pxNewTimer ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); /* 检查内存分配是否成功 */ if( pxNewTimer ! NULL ) { /* 调用初始化函数 */ prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); #if( configSUPPORT_STATIC_ALLOCATION 1 ) { /* 标记为动态分配以便删除定时器时正确释放内存 */ pxNewTimer-ucStaticallyAllocated pdFALSE; } #endif } 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 ) { /* 检查定时器指针是否有效防止对空指针进行操作 */ if( pxNewTimer ! NULL ) { /* 确保定时器服务任务所需的内部基础设施已创建和初始化 */ prvCheckForValidListAndQueue(); /* 使用函数参数初始化定时器结构体成员 */ pxNewTimer-pcTimerName pcTimerName; //定时器名称 pxNewTimer-xTimerPeriodInTicks xTimerPeriodInTicks;//定时器周期 pxNewTimer-uxAutoReload uxAutoReload; //自动重载标志 pxNewTimer-pvTimerID pvTimerID; //定时器ID标识 pxNewTimer-pxCallbackFunction pxCallbackFunction; //超时回调函数指针 /* 初始化定时器结构体中的链表节点设置为一个独立的、未连接的状态 为后续插入定时器管理链表做准备 */ vListInitialiseItem( ( pxNewTimer-xTimerListItem ) ); traceTIMER_CREATE( pxNewTimer ); }FreeRTOS用两个有序链表管理活跃的定时器。static List_t xActiveTimerList1 {0};static List_t xActiveTimerList2 {0};链表按超时时间升序排列最近超时的在前面定时器何时加入链表当调用xTimerStart、xTimerReset等启动函数时下面会提到2.3定时器删除函数#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 ) ) #define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) ) #define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), 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 ) #define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )通过“timers.h”可知以上函数都是通过调用统一的xTimerGenericCommand函数实现所有定时器操作的这样可以简化架构设计提高代码复用性。接下来详细研究这个函数。定时器通用命令函数用于向定时器守护任务发送各种操作命令参数TimerHandle_t xTimer目标定时器句柄指向要操作的定时器控制块const BaseType_t xCommandID命令标识符指定要执行的操作类型const TickType_t xOptionalValue参数值不同命令有不同的含义BaseType_t * const pxHigherPriorityTaskWoken用于ISR版本中传递任务唤醒状态const TickType_t xTicksToWait任务上下文发送命令时的最大阻塞时间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 ) { /* 填充命令消息结构体的字段 */ /* 设置消息ID即要执行的命令类型 */ xMessage.xMessageID xCommandID; /* 设置可选参数值不同类型命令使用此字段传递额外信息 */ xMessage.u.xTimerParameters.xMessageValue xOptionalValue; /* 设置目标定时器指针使守护任务知道操作哪个定时器 */ xMessage.u.xTimerParameters.pxTimer ( Timer_t * ) xTimer; /* 判断命令类型是否是中断服务程序的请求 命令ID小于tmrFIRST_FROM_ISR_COMMAND表示是任务上下文调用 */ if( xCommandID tmrFIRST_FROM_ISR_COMMAND ) { /* 如果调度器正在运行按正常方式发送 */ if( xTaskGetSchedulerState() taskSCHEDULER_RUNNING ) { /* 将命令消息发送到定时器队列尾部使用指定的阻塞时间 */ xReturn xQueueSendToBack( xTimerQueue, xMessage, xTicksToWait ); } else { /* 调度器未运行可能在初始化阶段以无延迟方式发送 */ xReturn xQueueSendToBack( xTimerQueue, xMessage, tmrNO_DELAY ); } } /* 中断服务程序ISR上下文发送命令分支 */ else { xReturn xQueueSendToBackFromISR( xTimerQueue, xMessage, pxHigherPriorityTaskWoken ); } traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn ); } else { mtCOVERAGE_TEST_MARKER(); } return xReturn; }