news 2026/8/16 16:58:56

FreeRTOS源码深入研究:6.软件定时器(software timer)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FreeRTOS源码深入研究:6.软件定时器(software timer)

一、软件定时器的特性

软件定时器是由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; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/16 9:44:47

最全词典整合收录:打造专业英语学习利器

最全词典整合收录&#xff1a;打造专业英语学习利器 【免费下载链接】最全词典整合收录词典刺客 本仓库提供了一个名为“最全词典整合收录(词典刺客)”的资源文件下载。该资源文件包含了以下词典的整合收录&#xff1a;- 柯林斯双解&#xff08;mddmdx&#xff09;- 朗文双解&a…

作者头像 李华
网站建设 2026/8/16 9:01:46

用户目录能不能放到其他盘?

用户目录能不能放到其他盘&#xff1f;是的, 你可以把用户文件夹移动到另一个磁盘, 但你应该小心操作. 许多人想要腾出系统盘空间或把个人文件放在单独的磁盘上. 移动用户文件夹可以缓解空间限制并简化备份, 但如果方法不当也可能引发问题. 本文解释了安全的选项, 需要遵循的步…

作者头像 李华
网站建设 2026/8/17 1:26:18

Captura音频质量优化终极指南:告别爆音与杂音困扰

Captura音频质量优化终极指南&#xff1a;告别爆音与杂音困扰 【免费下载链接】Captura Capture Screen, Audio, Cursor, Mouse Clicks and Keystrokes 项目地址: https://gitcode.com/gh_mirrors/ca/Captura Captura是一款功能强大的开源屏幕录制工具&#xff0c;支持屏…

作者头像 李华
网站建设 2026/8/16 8:48:26

终于交出焚诀了,运营新思路:短视频动漫化

作为开发者、内容创作者或职场人&#xff0c;日常总会遇到图片处理需求 —— 老照片修复、商品图抠图、低清图高清化&#xff0c;要么靠复杂 PS 耗时费力&#xff0c;要么付费工具成本太高。其实用 AIEnhancer.ai 按 “选场景→验效果→优流程” 的思路&#xff0c;就能零门槛实…

作者头像 李华
网站建设 2026/8/16 9:30:38

AI Vibe Coding 工具全景图:2025年最全指南

什么是 Vibe Coding&#xff1f; Vibe Coding 是一种新兴的编程范式&#xff0c;开发者通过自然语言与 AI 进行对话式交互来创建软件&#xff0c;而非直接编写代码。这种模式强调"意图驱动"&#xff0c;将开发者的角色从代码编写者转变为 AI 协作者和代码策展人。 工…

作者头像 李华
网站建设 2026/8/15 11:23:06

Ursa.Avalonia样式系统深度解析:模块化设计与主题切换完整指南

Ursa.Avalonia样式系统深度解析&#xff1a;模块化设计与主题切换完整指南 【免费下载链接】Ursa.Avalonia Ursa是一个用于开发Avalonia程序的控件库 项目地址: https://gitcode.com/IRIHI_Technology/Ursa.Avalonia Ursa.Avalonia作为企业级UI控件库&#xff0c;其样式…

作者头像 李华