一、结论先行
FreeRTOS 中的延时(无论是普通延时还是 vTaskDelay)都不是绝对精确的硬实时延时,它们都是“软实时”的,具有一定的不确定性。
其准确性从高到低排序一般为:硬件定时器中断 > vTaskDelayUntil > vTaskDelay >> 普通空循环延时。
下面我们将深入探讨为什么会这样,以及它们之间的区别。
二、详细对比与原理分析
为了更直观地理解不同延时方法的工作原理和影响其准确性的因素,下图展示了它们的执行流程与中断点:
普通空循环延时(极度不推荐)
单位:通常试图模拟微秒或毫秒,但极度不准确。
不准确的原因:
完全阻塞:此类延时完全独占CPU,期间无法处理任何其他任务、中断或事件,严重破坏系统实时性。
受中断影响:如果在此期间发生中断,CPU 会去执行中断服务程序,执行完再回来继续循环。这会导致实际延时时间远长于预期。
受编译器优化影响:循环可能会被编译器优化掉,或者不同优化等级导致循环执行次数不一致。
与 CPU 频率强相关:代码的执行速度严格依赖于主频。如果CPU频率改变(如为省电降频),延时时间会同比变化。
结论:在RTOS中绝对不要使用这种延时方法,它与多任务设计的初衷背道而驰。
vTaskDelay (相对延时)
单位:系统节拍。其物理时间长度由 configTICK_RATE_HZ 决定(例如 1000 Hz = 1 ms/tick)。
不准确的原因:
调度延迟(最主要原因):vTaskDelay 到期后,任务变为就绪态,但并非立刻运行。如果此时有更高优先级的任务正在运行,或者同优先级的任务正在其时间片内运行,当前任务必须等待,直到调度器选中它。这个等待时间是不确定的。
节拍粒度:延时的最小单位是一个节拍。如果你要延时 1.5 个节拍的时间,系统只能延时 1 或 2 个节拍,会产生“量化误差”。
中断处理:虽然任务调度不会被中断服务程序阻塞,但长时间关中断或高优先级中断处理也会轻微影响调度器响应的及时性。
关系:vTaskDelay 的精度与时间片大小间接相关。时间片越长,同等优先级任务一旦运行,它就可能阻塞你更久。时间片越短,任务切换越频繁,你的任务被及时调度的机会就越大。
本质:vTaskDelay(N) 的意思是“请至少延时 N 个节拍”,而不是“精确延时 N 个节拍”。
vTaskDelayUntil (绝对延时)
单位:同样是系统节拍。
为什么更准确:它用于实现固定的周期。它会记录一个上一次唤醒的时间变量,并在下次延时中补偿掉任务本身执行所花费的时间。
vTaskDelay: 执行时间 + 延时时间 = 总时间(不固定)
vTaskDelayUntil: 唤醒点 + 周期时间 = 下一个唤醒点(固定)
它准确吗? 它保证了周期的稳定性,消除了任务执行时间带来的累积误差。但它依然无法避免 调度延迟。任务周期性的间隔是准的,但任务每次实际运行的时刻可能会有微小的抖动(Jitter),原因同样是它到期后可能无法立刻被调度。
三、总结对比表格
四、如何提高延时准确性?
提高 configTICK_RATE_HZ:
提高节拍频率(如从 100Hz 提高到 1000Hz)可以减小节拍粒度,从而减少量化误差,也让调度器响应更及时。但代价是系统开销增大(中断更频繁)。
合理设计任务优先级:
对实时性要求高的任务,赋予更高的优先级。这样当它的延时结束后,它能立即抢占低优先级任务,最大限度地减少调度延迟。
使用 vTaskDelayUntil:
对于任何周期性任务,***首选 vTaskDelayUntil 而不是 vTaskDelay,以避免累积误差。
对于极端精确的延时(硬件级):
如果需要微秒级别的绝对精确延时(例如驱动一个非常精确的时序协议),FreeRTOS 的软件延时API都无法满足。
必须使用硬件定时器中断来实现。在硬件定时器中断中直接操作硬件或通知一个高优先级任务。这才是真正的“硬实时”保障。
最终结论:
FreeRTOS 提供的延时是 任务级的、协作式的软实时延时,其不确定性主要来源于基于优先级的任务调度机制本身。这种不准确性是牺牲绝对时间精度以换取多任务并发性和系统效率的必然结果。理解这一点,并根据你的实际需求选择正确的延时方法,是设计出可靠RTOS应用的关键。
————————————————
版权声明:本文为CSDN博主「Shylock_Mister」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Shylock_Mister/article/details/151067690
|
|