公司动态
RTOS-F429-HAL-绝对延时和相对延时(2026/7/31)
目录一vTaskDelayUntil() — 绝对延时1函数原型2怎么用3跟 vTaskDelay 的语法对比二rtos\9\ 改动清单1freertos_demo.c2main.c3时间轴推演task2(prio5, 绝对延时) task1(prio3, 相对延时)task2(prio3, 绝对延时) task1(prio5, 相对延时)三生活化解释1vTaskDelay — 从现在开始算睡多久2vTaskDelayUntil — 定一个固定的闹钟时间3一张图对比4什么时候用哪个一vTaskDelayUntil()— 绝对延时1函数原型void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement );形参含义pxPreviousWakeTime上一次醒来的绝对时间。第一次调用前必须用xTaskGetTickCount()初始化。函数内部每次调用自动更新这个值。xTimeIncrement周期时长单位tick。每次醒来后间隔多久再醒。返值无。需要宏INCLUDE_vTaskDelayUntil 1✅ 我们已开2怎么用TickType_t xLastWakeTime xTaskGetTickCount(); // 记下现在是几点 while (1) { // 干活... vTaskDelayUntil(xLastWakeTime, 500); // 下次醒 xLastWakeTime 500 // ↑ ↑ // 它帮你 500 更新 周期 500 个 tick }第一次xLastWakeTime 100假设当前时间→ 睡到100500600第二次xLastWakeTime 600→ 睡到6005001100第三次xLastWakeTime 1100→ 睡到11005001600内部自动帮你做了xLastWakeTime 500你不用手动加。3跟vTaskDelay的语法对比// 相对延时就一个参数睡多久 vTaskDelay(500); // 绝对延时多一个上次几点醒的变量来锚定节奏 TickType_t wake xTaskGetTickCount(); vTaskDelayUntil(wake, 500); // wake 必须传地址函数内部会更新它二rtos\9\ 改动清单1freertos_demo.c行号内容37-38task1(prio3)、task2(prio5)——高优先级抢占低优先级88-98task1vTaskDelay(500)相对延时周期会漂108-119task2vTaskDelayUntil(xLastWakeTime, 500)绝对延时不漂2main.c行号内容5-7去掉 Key.h/TIM6.h本实验不需要按键和运行时间统计18-20去掉 Key_Init()/ConfigureTimeForRunTimeStats()27printf 标题 →vTaskDelay vs vTaskDelayUntil Test!代码很简洁。用示波器看 PH10(红灯, task1) 和 PH11(绿灯, task2)task2 绿灯精确 500ms 一个周期不漂task1 红灯~500ms 但有抖动因为会被 task2 抢或者起步晚了没示波器的话看着灯闪也行肉眼分不出重点是用逻辑分析仪/示波器看差异。3时间轴推演task2(prio5, 绝对延时) task1(prio3, 相对延时) t0: task2(prio5) 跑 LED1翻 → delay_ms(20) → vTaskDelayUntil → 睡到 t500 t20: task1(prio3) 拿到 CPUtask2 在睡 LED0翻 → delay_ms(20) → vTaskDelay(500) → 睡到 t20500540 t500: task2 准时醒 LED1翻 → delay_ms(20) → t520 vTaskDelayUntil → 睡到 t1000 t540: task1 醒task2 在睡没人抢 LED0翻 → delay_ms(20) → t560 vTaskDelay(500) → 睡到 t1060 task1 周期第一次翻灯 t20第二次 t540 → 520ms task2 周期第一次翻灯 t0第二次 t500 → 500ms 差的那 20ms 不是被抢了 CPU——task2 醒的时候 task1 本来就在睡。差的是开头 task2 先占了 20mstask1 天生起步晚了 20ms。之后两家的唤醒时间永远错开谁也不抢谁各过各的。 那之前说的抢占导致波形拉长是上一节的场景——task1 优先级更高的时候才会发生task1 在跑 delay_ms 期间task2 的绝对时间到了但 task2 优先级低抢不进去只能等 task1 腾出手所以 task2 的 500ms 才变成 ~520ms。 这一节的结论更简单task2 优先级高就先跑占了前面 20mstask1 起步晚周期自然长。task2(prio3, 绝对延时) task1(prio5, 相对延时)两个任务都做同一件事翻转 LED → delay_ms(20) 忙等 → 延时。区别只在延时方式。 t0 调度器启动 task1(prio5更高) 先跑 ├── LED0 翻转瞬间 ├── delay_ms(20) → 忙等 20msCPU 不释放 └── vTaskDelay(500) → 阻塞记录500ms 后叫醒我将于t520ms时醒来。 t≈20 task2(prio3更低) 终于等到 CPU ├── xLastWakeTime xTaskGetTickCount() → 记下现在是 20 ├── LED1 翻转 ├── delay_ms(20) → 忙等 20mst≈40 └── vTaskDelayUntil(wake, 500) → 计算下次醒 20 500 t520 → 现在才 t40睡 480ms t520 task1 的 vTaskDelay(500) 到期醒了 → task1 优先级 5 task2 优先级 3 //task1 先跑把task2抢占了两人同时从t520ms醒来 ├── LED0 翻转 └── delay_ms(20) → t≈540 t540 task2 的 vTaskDelayUntil 也到期了应该 t520 醒的被 task1 拖到 540 → 已经错过了 20ms立刻跑 ├── LED1 翻转 ├── delay_ms(20) → t≈560 └── vTaskDelayUntilxLastWakeTime 500 → 下次醒 20500500 t1020 实际睡多久1020 - 560 460ms ← ★ 比 500 短 t≈1040 task1 又醒了5205001020 delay_ms 20 t≈1040 → 又抢占 为什么示波器上看到的是 ~496ms 而不是 500ms 看上图——task2 每次都被 task1 拖后腿但 vTaskDelayUntil 死守着绝对时间点。你迟到了它就缩短下一段睡眠来追回来所以示波器上 task2 的 LED 周期会围绕 500ms 上下抖动有时候是 496ms在追、有时候是 504ms被拖。 这就是绝对延时的意义长期来看平均周期精确是 500ms不漂移。反观 task1 的 vTaskDelay每次都在漂100 次以后可能漂出几十毫秒。 一句话 vTaskDelay 倒计时闹钟晚起了就整体漂移。vTaskDelayUntil 固定时刻闹钟晚起了就自己缩短下一次睡眠来追回去长期节奏永远不变。你示波器看到的 496ms 就是因为 task2 在追被 task1 拖后腿的进度。三生活化解释1vTaskDelay — 从现在开始算睡多久vTaskDelay(500); // 从现在起睡 500ms生活例子午休闹钟你说从现开始睡 30 分钟 → 设了个倒计时 30 分钟 BUT你在床上刷了 10 分钟手机才真的闭眼 → 实际醒来是 40 分钟后刷手机也算在了睡之前的时间里 → 你的午休节奏漂移了代码里的后果一个任务每 100ms 打印一次 第 1 次t0 → 打印 → vTaskDelay(100) → t100ms 醒来 第 2 次t100 → 打印 → 但打印花了 5ms → vTaskDelay(100) → t205ms 醒来 第 3 次t205 → 打印(5ms) → vTaskDelay(100) → t310ms 醒来 第 4 次t310 → ... 每次都在漂移100 次以后可能差了半秒2vTaskDelayUntil — 定一个固定的闹钟时间TickType_t last_wake xTaskGetTickCount(); // 记下起床时间 while (1) { // 干活... vTaskDelayUntil(last_wake, 100); // 不管刚才干了多久下次必须是 last_wake100ms }生活例子学校上课铃上课铃每隔 45 分钟响一次——不管你是准时回到座位还是磨蹭了 3 分钟 第一节9:00 响铃 → 上课 45 分钟 第二节9:45 响铃 ← 固定不是从第一节下课开始 45 第三节10:30 响铃 ← 固定 即使你课间拖堂了 5 分钟下一节的**响铃时间不变**。代码里的效果第 1 次t0 → 打印(5ms) → vTaskDelayUntil(wake, 100) → t100 准时醒 第 2 次t100 → 打印(5ms) → vTaskDelayUntil(wake, 100) → t200 准时醒 第 3 次t200 → 打印(5ms) → vTaskDelayUntil(wake, 100) → t300 准时醒 不管每次打印花了 5ms 还是 50ms下一次醒来的**绝对时间点不变**3一张图对比vTaskDelay相对 干活→睡100ms→干活→睡100ms→干活→睡100ms→... ↑ ↑ 漂了5ms ↑ 漂了10ms 节奏越来越偏 vTaskDelayUntil绝对 t0 t100 t200 t300 t400 ← 固定的绝对时间点 │ │ │ │ │ 干活 干活 干活 干活 干活 ↑ ↑ ↑ ↑ ↑ 每次都在同一个节拍上不漂移4什么时候用哪个场景用哪个无所谓精确周期随便睡一下vTaskDelay(100)周期性采集数据ADC 采样vTaskDelayUntil()周期性发送心跳包vTaskDelayUntil()按键扫描、LED 闪烁vTaskDelay(10)够用了一句话vTaskDelay 从刚才睡醒开始倒计时每次会漂移。vTaskDelayUntil 定好下一次醒来的绝对时间不管中间折腾了多久节奏永远不乱。