【第20期】延时的艺术:HAL_Delay vs vTaskDelay

裸机与RTOS核心差异 :裸机的延时是**"死等"(烧电、霸占 CPU);RTOS 的延时是"挂起"**(让权、省电)。

1. 裸机延时:焦虑的失眠者 (HAL_Delay)

我们在学习 STM32 的第一天就用过 HAL_Delay()。来看看它的底层逻辑(简化版):

void HAL_Delay(uint32_t Delay) {

uint32_t tickstart = HAL_GetTick(); // 记录开始时间

// 死循环检查

while ((HAL_GetTick() - tickstart) < Delay) {

// 空转!CPU 在这里疯狂跑圈,什么有意义的事都没做。

// 就像一个人盯着手表看,每一秒都数着过。

}

}

后果

  1. 霸道 :当 CPU 执行 HAL_Delay(1000) 时,这 1 秒钟内,主循环里排在后面的所有任务(按键扫描、屏幕刷新)全部被迫暂停。整个系统处于"假死"状态。

  2. 烧电:虽然 CPU 没干正事,但它处于全速运行状态(Run Mode),电流消耗是最大的(例如 20mA)。

2. RTOS 延时:定闹钟睡觉 (vTaskDelay)

RTOS 的 vTaskDelay (FreeRTOS) 或 OS_Delay (uCOS) 完全不同。

当你在任务 A 中调用 vTaskDelay(1000) 时,操作系统内核会做一系列复杂的动作:

  1. 移出:调度器把任务 A 从**【就绪列表 (Ready List)】**中拿走。这意味着调度器下次挑选"谁来运行"时,根本不会看任务 A 一眼。

  2. 记录:调度器把任务 A 放入**【延时列表 (Delayed List)】**,并给它贴个条子:"在系统时间到达 X+1000 时叫醒我"。

  3. 让权 (Yield) :任务 A 此时交出 CPU 使用权。调度器立刻去【就绪列表】找优先级最高的任务 B。

  4. 切换:CPU 保存 A 的现场,恢复 B 的现场,开始运行 B。

直观感受: 任务 A 说:"我要睡 1 秒。" 然后它就真的"消失"了。CPU 转头去干别的事。直到 1 秒后,系统滴答中断(SysTick)发现时间到了,才会把任务 A 从"小黑屋"里放出来,重新回到【就绪列表】排队。


3. 神奇的空闲任务 (Idle Task)

你可能会问:"如果系统里只有任务 A,它延时了,CPU 把权交出来,交给谁呢?"

这时候,RTOS 会自动创建一个最低优先级的保底任务------空闲任务 (Idle Task)

当所有业务任务都处于"阻塞"或"延时"状态时,CPU 就会运行空闲任务。 更厉害的是,我们可以在空闲任务里植入低功耗代码(Hook 函数):

void vApplicationIdleHook(void) {

// 汇编指令 WFI (Wait For Interrupt)

// 它的作用是:CPU 暂停运行,停止取指,时钟停振,进入休眠。

// 直到下一个中断(比如 SysTick 或 串口中断)来临,CPU 才会瞬间醒来。

__WFI();

}

巨大的功耗差异

  • 裸机 Delay:CPU 100% 时间全速跑,电流 ~20mA。

  • RTOS Delay :如果任务大部分时间在 Delay,CPU 大部分时间在 Idle Task 里执行 WFI 睡觉。电流可能降到 ~5mA 甚至更低。


4. 这里的"坑":相对延时 vs 绝对延时

RTOS 通常提供两种延时 API,新手容易混淆。

  • vTaskDelay (相对延时)

    • 含义:从调用这一行代码的时刻开始,延时 N 个节拍。

    • 场景:简单的 Sleep

    • 缺点:如果有高优先级中断打断,或者任务执行本身耗时,会导致周期累计误差。比如你想每 1000ms 闪一次灯,结果变成了 1005ms, 1010ms...

  • vTaskDelayUntil (绝对延时)

    • 含义:从上一次唤醒的时刻算起,让整个任务周期严格等于 N 个节拍。

    • 场景:需要高精度周期的采样任务(如每 2ms 读取一次 ADC)。它会自动扣除任务执行本身消耗的时间,多退少补。


总结陈词

  1. HAL_Delay忙等待 (Busy Wait),既阻碍别人运行,又浪费电能,是 RTOS 编程的大忌。

  2. vTaskDelay阻塞 (Blocking),它是 RTOS 实现多任务并发调度的基础------只有你让出了 CPU,别人才能运行。

  3. Idle Task + WFI 是 RTOS 天然低功耗的秘诀。

相关推荐
Aaron15882 小时前
AD9084和Versal RF系列具体应用案例对比分析
嵌入式硬件·算法·fpga开发·硬件架构·硬件工程·信号处理·基带工程
就不掉头发3 小时前
I/O复用
运维·服务器·c语言·开发语言
LCG米4 小时前
低功耗设计艺术:基于STM32U3系列MCU实现人类存在检测(仅6%占空比)
stm32·单片机·嵌入式硬件
zd8451015004 小时前
[LWIP] 如何启LWIP用调试信息输出
单片机
v先v关v住v获v取5 小时前
桌面级五轴机械臂cad1张总图+三维图+设计说明书
科技·单片机·51单片机
电子工程师-C515 小时前
基于51单片机的智能调温淋浴器
单片机·嵌入式硬件·51单片机
ComputerInBook5 小时前
函数调用栈帧分析(Windows平台)
c语言·windows·编译原理·汇编语言·c++语言
番茄灭世神6 小时前
32位ARM单片机视频教程第一篇
arm开发·单片机·嵌入式·gd32·pn学堂
SystickInt7 小时前
C语言 UTC时间转化为北京时间
c语言·开发语言