FreeRTOS 学习笔记:软件定时器,为什么它不是又一个任务
上一篇把任务、队列、信号量分清之后,这周轮到软件定时器(Software Timer)。我的软体机器人项目里有一堆「不紧急但必须周期性发生」的小事:状态指示灯闪烁、给上位机发心跳包、采样节拍提示、气阀动作后的延时确认。最初我全用任务加 vTaskDelay 凑,后来才发现这类活更适合交给软件定时器。
1. 软件定时器是什么:一个专门管闹钟的服务任务
把 configUSE_TIMERS 打开后,FreeRTOS 会创建一个定时器服务任务(也叫守护任务)。我创建的每一个软件定时器到期时,它的回调函数都在这个服务任务的上下文里执行。好处很直接:哪怕有十几个定时器,也只共用这一个任务,不用为每件周期小事单独开任务,省栈内存。代价也要记住:所有回调排队在同一个任务里跑,一个回调卡住,后面的定时器全部迟到。
2. 两种定时器:单次和周期
xTimerCreate 创建时由 uxAutoReload 参数区分:pdTRUE 是周期型(自动重装,到期回调后自己接着计时),pdFALSE 是单次型(响一次就停)。我的习惯用法:心跳包、指示灯闪烁用周期型;「气阀打开 200ms 后检查一次压力」这类一次性延时用单次型。常用 API 就几个:xTimerStart 启动、xTimerStop 停止、xTimerReset 重新计时、xTimerChangePeriod 改周期。注意在中断里调用要换成带 FromISR 后缀的版本,和信号量那篇讲的是同一条规矩。
3. 最重要的坑:回调函数里不许阻塞
这是我最开始最容易犯的错。回调跑在定时器服务任务里,所以回调中绝不能调用 vTaskDelay,不能阻塞式地等队列或信号量,更不能直接干重活。正确姿势是:回调只做「发通知」这一件事,把消息丢给队列或任务通知,真正的处理放回普通任务里做。回调写得越短越好,写长了等于亲手把所有定时器一起拖慢。
4. 精度边界:它只适合「软」定时
软件定时器的计时基准是系统节拍(Tick),精度受 configTICK_RATE_HZ 限制,而且回调还可能被高优先级任务推迟执行。所以它适合秒级、百毫秒级的松散节拍;要微秒级精度或硬实时截止期,还是老老实实使用硬件定时器中断。我的分工是:硬件定时器管 PWM、采样触发这类硬活,软件定时器管心跳、闪烁、超时提醒这类软活,两边互不打扰。
5. 一个实际用法:通信超时看门狗
串口接收上位机指令时,我用单次定时器做超时保护:每收到一帧完整数据就 xTimerReset 重新计时;如果超过 3 秒没有新数据,回调发通知给监控任务,判定通信链路中断,让执行器进入安全状态。比专门开一个任务去轮询省事得多,代码也干净。
小结:软件定时器就是「一个服务任务管一堆闹钟」;周期还是单次看 uxAutoReload;回调只发通知、不干活不阻塞;精度只到节拍级,硬实时仍归硬件定时器。下一篇准备写任务通知——它是这几个机制里最轻量的一个,正好和队列、信号量做个对比。
- 点赞
- 收藏
- 关注作者
评论(0)