Day 3:优先级与抢占
3.1 实验目标
理解 FreeRTOS 的抢占式调度和同优先级时间片轮转。通过修改任务优先级和故意去掉 osDelay 观察"饿死"现象。
3.2 代码修改
在 Day 2 双任务基础上,新增 Task_C 控制 LED2(PA7),并调整优先级:
freertos.c Init 区域:
| |
Task_C 实现:
| |
3.3 观察到的关键现象
现象一:正常情况(都有 osDelay)
- Task_A(Normal):每 500ms 串口打印
- Task_B(Normal):每 1000ms LED1 闪烁
- Task_C(AboveNormal):每 200ms LED2 闪烁
- 三者互不干扰,同时运行
现象二:Task_C 同优先级 + 去掉 osDelay(忙等)
临时去掉 Task_C 的 osDelay(200):
| |
观察结果:
- LED2 看起来"常亮"(实际翻转频率太高,肉眼无法分辨)
- 但 Task_A 和 Task_B 仍然正常运行!
原因:FreeRTOS 默认开启 Time Slicing(时间片轮转)。同优先级(或即使 Task_C 是 AboveNormal,但实验发现如果把它改回 Normal)任务之间,调度器每个 tick(1ms)会强制切换,即使任务没有主动阻塞。所以 Task_C 每 1ms 被踢下去,Task_A/B 能分到 CPU。
现象三:Task_C 高优先级(AboveNormal)+ 去掉 osDelay
观察结果:
- LED2 常亮
- LED1 停止闪烁,串口停止输出
原因:Task_C 优先级 AboveNormal > Task_A/B 的 Normal。Task_C 没有 osDelay,永远处于就绪/运行态。调度器每次检查就绪任务,它都是优先级最高的,独占 CPU。低优先级任务永远轮不到——这就是饿死(Starvation)。
3.4 核心概念总结
| 场景 | 结果 | 原因 |
|---|---|---|
| 同优先级 + 有 osDelay | 正常轮流执行 | 主动阻塞释放 CPU |
| 同优先级 + 无 osDelay | 不会饿死,但翻转极快 | 时间片轮转强制每 tick 切换 |
| 高优先级 + 有 osDelay | 正常执行,不阻塞低优先级 | 阻塞期间 CPU 释放给低优先级 |
| 高优先级 + 无 osDelay | 饿死低优先级 | 调度器永远选最高优先级就绪任务 |
重要结论:osDelay() 是任务"让出 CPU"的灵魂。没有它,高优先级任务会饿死低优先级任务;同优先级下虽然有时间片轮转保护,但也会浪费 CPU 做无意义的空转。
Day 4:任务挂起与恢复
4.1 实验目标
通过串口命令控制任务的暂停和恢复。新增 Task_CMD 作为命令解析器。
4.2 代码实现
freertos.c Init 区域:
| |
Task_CMD 实现:
| |
4.3 踩坑记录
坑 1:HAL_UART_Receive 超时后判断未接收到的数据
错误写法:
| |
正确写法:
| |
HAL_UART_Receive 的第四个参数是超时时间(ms)。如果 100ms 内没收到数据,返回 HAL_TIMEOUT,此时 rx_byte 内容不会被修改(但初始值随机的话就会误判)。
4.4 验证现象
- 串口发送
's'→ LED1 停止闪烁(被挂起) - 串口发送
'r'→ LED1 恢复闪烁(被恢复) - Task_A 串口打印和 Task_C LED2 闪烁不受影响
Day 5:空闲任务钩子与独立看门狗(IWDG)
5.1 实验目标
利用 FreeRTOS 的空闲任务钩子(Idle Hook)喂独立看门狗(IWDG)。如果任何用户任务进入死循环(忙等),空闲任务无法执行,看门狗超时复位系统。
5.2 CubeMX 配置
- IWDG → 勾选 Activated
- Prescaler:/32
- Reload Value:1000
- 计算超时:LSI 约 32kHz,32k/32 = 1kHz,1000/1k = 约 1 秒
重新 Generate Code。
5.3 代码实现
FreeRTOSConfig.h:
| |
freertos.c Application 区域:
| |
5.4 踩坑记录
坑 1:死循环测试时"看不出复位"
把 Task_A 改成死循环:
| |
观察现象:
- 串口疯狂输出
TaskA: Running - LED1 和 LED2 看起来常亮或微亮
- 系统似乎"没有复位"
原因:系统确实在反复复位(约每秒一次),但复位后 Task_A 立刻又开始疯狂输出,肉眼看起来像是"一直在跑"。
验证方法:在 main.c 的 HAL_Init() 之后添加复位标志检测:
| |
添加后观察到:
- 串口每隔约 1 秒打印
>>> IWDG RESET DETECTED! <<< - LED2 每 1 秒闪烁 3 次
- 证实了系统在看门狗控制下反复复位
5.5 核心概念
| 概念 | 说明 |
|---|---|
| 空闲任务 | FreeRTOS 自动创建的最低优先级任务(优先级 0)。当所有用户任务都阻塞时,CPU 执行它。 |
| 空闲任务钩子 | 在空闲任务每次执行时调用的用户函数。不能阻塞,不能调用会引起任务切换的 API。 |
| IWDG 独立看门狗 | 使用独立低速时钟(LSI),不依赖主时钟。即使主程序跑飞,只要 LSI 还在,看门狗就会超时复位。 |
| 为什么把喂狗放在空闲任务? | 只要有任何用户任务在忙等(死循环),空闲任务永远轮不到,看门狗就会超时。这是检测"系统是否还能正常调度"的最简单方法。 |
Week 1 总结
| 天数 | 内容 | 学习目的 |
|---|---|---|
| Day 1 | CubeMX 环境搭建、时钟配置、代码生成 | 基本理解 FreeRTOS 工程的创建 |
| Day 2 | 双任务创建、串口+LED同时运行 | 理解任务创建和 osDelay 阻塞 |
| Day 3 | 优先级抢占、时间片轮转、饿死实验 | 理解抢占式调度核心机制 |
| Day 4 | 任务挂起/恢复、串口命令控制 | 掌握挂起和恢复机制 |
| Day 5 | 空闲任务钩子、IWDG看门狗、复位检测 | 理解看门狗与调度器的关系 |