news 2026/9/7 19:05:19

RTOS的软件定时器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RTOS的软件定时器

软件定时器

在FreeRTOS里,我们可以设置无数个"软件定时器",它们都是基于系统滴答中断
(Tick Interrupt)。

定时器的使用

使用定时器需要指定以下内容:

  • 指定时间:启动定时器和运行回调函数,两者的间隔被称为定时器的周期(period)。
  • 指定类型:一次性和自动加载定时器
  • 指定回调函数:指定到时间后要做什么

软件定时器有两种状态:运行和冬眠

软件定时器的上下文

在FreeRTOS中,定时器函数需要在RTOS Damemon Task中执行,也被称为RTOS守护任务。

当FreeRTOS的配置项configUSE_TIMERS被设置为1时,在启动调度器时,会自动创建RTOS Damemon Task。

我们自己编写的任务函数要使用定时器时,是通过"定时器命令队列"(timer command queue)和守护任务交互,如下图所示:

守护任务的优 先级为: configTIMER_TASK_PRIORITY ;定时器命令队列的长度为configTIMER_QUEUE_LENGTH。

定时器的使用

定时器的创建,包含动态分配内存和静态分配内存两种方式:

/* 使用动态分配内存的方法创建定时器 * 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 );

回调函数:

void ATimerCallback( TimerHandle_t xTimer ); typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer );

删除:

/* 删除定时器 * xTimer: 要删除哪个定时器 * xTicksToWait: 超时时间 * 返回值: pdFAIL 表示"删除命令"在 xTicksToWait 个 Tick 内无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );

启动和停止定时器

/* 启动定时器 * xTimer: 哪个定时器 * xTicksToWait: 超时时间 * 返回值: pdFAIL 表示"启动命令"在 xTicksToWait 个 Tick 内无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); /* 启动定时器(ISR 版本) * xTimer: 哪个定时器 * pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒, * 如果守护任务的优先级比当前任务的高, * 则"*pxHigherPriorityTaskWoken = pdTRUE", * 表示需要进行任务调度 * 返回值: pdFAIL 表示"启动命令"无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken ); /* 停止定时器 * xTimer: 哪个定时器 * xTicksToWait: 超时时间 * 返回值: pdFAIL 表示"停止命令"在 xTicksToWait 个 Tick 内无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait ); /* 停止定时器(ISR 版本) * xTimer: 哪个定时器 * pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒, * 如果守护任务的优先级比当前任务的高, * 则"*pxHigherPriorityTaskWoken = pdTRUE", * 表示需要进行任务调度 * 返回值: pdFAIL 表示"停止命令"无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken );

复位

/* 复位定时器 * xTimer: 哪个定时器 * xTicksToWait: 超时时间 * 返回值: pdFAIL 表示"复位命令"在 xTicksToWait 个 Tick 内无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait ); /* 复位定时器(ISR 版本) * xTimer: 哪个定时器 * pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒, * 如果守护任务的优先级比当前任务的高, * 则"*pxHigherPriorityTaskWoken = pdTRUE", * 表示需要进行任务调度 * 返回值: pdFAIL 表示"停止命令"无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerResetFromISR( TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken );

修改定时器周期

/* 修改定时器的周期 * xTimer: 哪个定时器 * xNewPeriod: 新周期 * xTicksToWait: 超时时间, 命令写入队列的超时时间 * 返回值: pdFAIL 表示"修改周期命令"在 xTicksToWait 个 Tick 内无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, TickType_t xNewPeriod, TickType_t xTicksToWait ); /* 修改定时器的周期 * xTimer: 哪个定时器 * xNewPeriod: 新周期 * pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒, * 如果守护任务的优先级比当前任务的高, * 则"*pxHigherPriorityTaskWoken = pdTRUE", * 表示需要进行任务调度 * 返回值: pdFAIL 表示"修改周期命令"在 xTicksToWait 个 Tick 内无法写入队列 * pdPASS 表示成功 */ BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer, TickType_t xNewPeriod, BaseType_t *pxHigherPriorityTaskWoken );

资源管理

在想要独占式的访问临界资源时,有以下几种常用的手段:

  • 互斥量
  • 屏蔽中断
  • 禁止调度器

屏蔽中断:

  • 任务中使用:taskENTER_CRITICA()/taskEXIT_CRITICAL()
  • ISR 中使用:taskENTER_CRITICAL_FROM_ISR()/taskEXIT_CRITICAL_FROM_ISR()
/* 在任务中,当前时刻中断是使能的 * 执行这句代码后,屏蔽中断 */ taskENTER_CRITICAL(); /* 访问临界资源 */ /* 在任务中,当前时刻中断是使能的 * 执行这句代码后,屏蔽中断 */ taskENTER_CRITICAL(); /* 访问临界资源 */

暂停调度器

/* 暂停调度器 */ void vTaskSuspendAll( void ); /* 恢复调度器 * 返回值: pdTRUE 表示在暂定期间有更高优先级的任务就绪了 * 可以不理会这个返回值 */ BaseType_t xTaskResumeAll( void );

案例--DHT11的使用

DHT11使用单总线通信,所有信号都靠一根线传输,对时间精度要求很高。因此如果程序中有其他高频率的中断可能会打断DHT11的时序读取过程,导致数据错乱。

因此我们在使用定时器读取DHT11的数据的时候可以暂停调度器,然后读取完再恢复调度器即可。

创建定时器读取数据:

DHT11_Init(); //创建定时器 g_TimerDHT11 = xTimerCreate ("DHT11", 2000, pdTRUE, NULL, DHT11_TimerFunc);

对应的回调函数:

可以看到我们通过暂停调度器从而保证数据的正确性

static void DHT11_TimerFunc(TimerHandle_t xTimer) { int hum, temp; int err; //暂停调度器 vTaskSuspendAll(); err = DHT11_Read(&hum, &temp); xTaskResumeAll(); //恢复调度器 if(err == 0) { char buff[20]; sprintf(buff, "hum:%d temp:%d", hum, temp); LCD_ClearLine(0, 0); LCD_PrintString(0, 0, buff); } else { LCD_ClearLine(0, 0); LCD_PrintString(0, 0, "DHT11 error"); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 19:03:09

为什么元学习是通用智能AGI的关键?

当下人工智能的发展,正从“专项智能”的规模化落地,迈向“通用人工智能(AGI)”的终极探索。大语言模型、多模态模型的迭代升级,让AI在文本生成、图像识别、逻辑推理等单一领域逼近人类水平,但始终无法突破任…

作者头像 李华
网站建设 2026/9/3 22:28:33

985/211毕业生求职平台推荐 资质核验及选型指南

2026年高学历毕业生求职市场趋势 2026年985/211毕业生求职呈现「优先选择中大型企业、关注成长空间、倾向使用垂直中高端招聘平台」三大趋势,对平台资质的关注度提升40%。 高学历毕业生求职需求变化趋势 985/211毕业生求职需求已从「找工作」向「找好工作」转变&…

作者头像 李华
网站建设 2026/9/2 20:42:22

ASP.NET Core 从入门到精通:视频教程学习路径与核心模块拆解

这次我们来看一套 ASP.NET Core 从入门到精通的视频教程资源。对于想系统学习 .NET 现代 Web 开发框架的开发者来说,一套结构清晰、内容完整的视频教程是快速上手的捷径。这套教程的核心价值在于它覆盖了从零基础到进阶实战的全链路,重点不是让你死记概念…

作者头像 李华
网站建设 2026/9/4 7:54:19

Mkvtoolnix v74.0.0:无损视频处理利器,告别重新编码

如果你经常处理视频文件,尤其是从网上下载的各类资源,一定会遇到这样的困扰:一个视频文件,音轨、字幕、章节信息全都封装在一个MKV容器里,但你只想提取其中的中文字幕,或者替换掉质量不佳的音轨&#xff0c…

作者头像 李华
网站建设 2026/9/4 13:54:08

2026答辩自述稿工具红黑榜:选错代价真不小

毕业季一到,答辩自述稿就成了压在很多学生心头的一块石头。写不出来、写出来不像自己的话、导师改了三轮还摇头——这些场景每年都在上演。市面上标榜能写自述稿的AI工具越来越多,但真正经得起答辩现场追问的没几个。这篇红黑榜把话说透:红榜…

作者头像 李华
网站建设 2026/9/3 1:15:37

分治法—假币问题(附例题详解|呕心沥血|保姆级教学)

摘要 本文系统讲解天平称量假币问题的通用解法,区分硬币奇、偶情况。核心策略:偶数枚硬币,要分两堆;奇数堆硬币,要分三堆。文章还结合2017年上半年真题展示实战步骤,并附官方答案。逻辑清晰,方…

作者头像 李华