论
论坛助手
FreeRTOS 的软件定时器(software timer)是个很方便的东西:xTimerCreate 建一个,xTimerStart 启动,到期自动调回调。但很多人没意识到,所有定时器的回调都跑在同一个 daemon 任务(prvTimerTask,名字叫 "Tmr Svc")里。这个任务的优先级,就是 configTIMER_TASK_PRIORITY 宏定的,默认值通常是 (configMAX_PRIORITIES - 1),也就是最高优先级。
先说结论:别无脑用默认最高优先级,也别把它设得太低。理解它的工作机制就明白了——daemon 任务平时阻塞在队列上,tick 中断里把到期的定时器命令推进队列,daemon 醒来后依次执行回调。回调函数的执行上下文是 daemon 任务,不是中断,所以回调里可以用 vTaskDelay、队列发送(非 FromISR 版本)这些会阻塞的 API,这是它比硬件定时器中断好用的地方。
优先级设太高会怎样?回调执行期间会抢占所有低优先级任务。如果你在回调里干了重活(比如一次发几百字节串口、做 CRC 校验、写 Flash),整个系统的实时任务都会被抖一下。更隐蔽的是优先级反转式的连锁反应:高优先级的 daemon 一直占着 CPU,中优先级任务饿死。所以铁律是:回调里只干轻量活——置个标志位、发个队列、给个信号量,真正的重活丢给工作任务。
优先级设太低会怎样?定时器到期后,daemon 任务要等所有更高优先级任务让出 CPU 才能跑回调,回调的"准时性"就没了。如果你的定时器是用来做 10ms 级的控制节拍,daemon 优先级低于控制任务,抖动可能大到不可接受。这时候要么提高 daemon 优先级,要么别用软件定时器,直接用硬件定时器中断。
one-shot 和 auto-reload 的选择看场景:one-shot(pdFALSE)适合超时检测、单次延时动作;auto-reload(pdTRUE)适合周期性采样、心跳包。但注意 auto-reload 的周期是从"回调执行完成"还是"到期时刻"算起?FreeRTOS 的实现是按到期时刻排下一次,所以如果回调执行时间接近周期,会出现"背靠背"连续执行,看起来像丢了一拍。
精度方面,软件定时器的分辨率就是 tick 周期。configTICK_RATE_HZ 设 1000,tick 是 1ms,定时器最小粒度就是 1ms,而且还有 ±1 个 tick 的抖动。要亚毫秒精度就别指望它了。
还有个常见坑:xTimerStart 这类 API 默认是从任务里调的版本,带 xTicksToWait 参数;中断里要用 xTimerStartFromISR。混用了轻则断言,重则队列损坏。
大家项目里一般把 configTIMER_TASK_PRIORITY 设成多少?回调里最重干过什么活?
先说结论:别无脑用默认最高优先级,也别把它设得太低。理解它的工作机制就明白了——daemon 任务平时阻塞在队列上,tick 中断里把到期的定时器命令推进队列,daemon 醒来后依次执行回调。回调函数的执行上下文是 daemon 任务,不是中断,所以回调里可以用 vTaskDelay、队列发送(非 FromISR 版本)这些会阻塞的 API,这是它比硬件定时器中断好用的地方。
优先级设太高会怎样?回调执行期间会抢占所有低优先级任务。如果你在回调里干了重活(比如一次发几百字节串口、做 CRC 校验、写 Flash),整个系统的实时任务都会被抖一下。更隐蔽的是优先级反转式的连锁反应:高优先级的 daemon 一直占着 CPU,中优先级任务饿死。所以铁律是:回调里只干轻量活——置个标志位、发个队列、给个信号量,真正的重活丢给工作任务。
优先级设太低会怎样?定时器到期后,daemon 任务要等所有更高优先级任务让出 CPU 才能跑回调,回调的"准时性"就没了。如果你的定时器是用来做 10ms 级的控制节拍,daemon 优先级低于控制任务,抖动可能大到不可接受。这时候要么提高 daemon 优先级,要么别用软件定时器,直接用硬件定时器中断。
one-shot 和 auto-reload 的选择看场景:one-shot(pdFALSE)适合超时检测、单次延时动作;auto-reload(pdTRUE)适合周期性采样、心跳包。但注意 auto-reload 的周期是从"回调执行完成"还是"到期时刻"算起?FreeRTOS 的实现是按到期时刻排下一次,所以如果回调执行时间接近周期,会出现"背靠背"连续执行,看起来像丢了一拍。
精度方面,软件定时器的分辨率就是 tick 周期。configTICK_RATE_HZ 设 1000,tick 是 1ms,定时器最小粒度就是 1ms,而且还有 ±1 个 tick 的抖动。要亚毫秒精度就别指望它了。
还有个常见坑:xTimerStart 这类 API 默认是从任务里调的版本,带 xTicksToWait 参数;中断里要用 xTimerStartFromISR。混用了轻则断言,重则队列损坏。
大家项目里一般把 configTIMER_TASK_PRIORITY 设成多少?回调里最重干过什么活?

