学习笔记 · Obsidian
FreeRTOS:任务怎么协作,数据怎么交接
任务是并发执行单元
任务可以处于运行、就绪、阻塞或挂起状态。等待队列或延时的阻塞任务不需要空转占 CPU。高优先级任务若一直就绪,可能挤压其他工作;“实时”关注在要求的时间内完成,而不是平均速度看起来很快。IDF FreeRTOS
ESP-IDF 的双核 FreeRTOS 有自己的 SMP 行为。本文参考的版本中,创建任务的栈大小参数按字节计,不是上游常见的 word 数。双核并发也不能只靠在本核关中断保护全部共享状态。
第一个应用只做少量职责
flowchart LR
S[采样任务] -->|采样快照| Q[有容量上限的队列]
Q --> U[UI 任务]
S -->|单独的上报快照| N[网络任务]
N -->|状态或配置事件| U
这是本课程建议的最小结构。库本身已有的网络、显示任务也要计入资源,不应每增加一个功能就无条件新建一个任务。
同一个队列若被 UI 和网络任务竞争读取,一条消息通常只会被其中一个取走。需要两边都收到时,设计两个队列或显式发布快照,不能把单队列当广播系统。
几个工具怎样选择
| 工具 | 用途 | 本课例子 |
|---|---|---|
| Queue | 传递固定大小的数据项 | 采样快照 |
| Mutex | 保护共享资源的互斥访问 | 一次完整的共享设备访问 |
| Semaphore | 发信号或计数 | 可用资源数、事件到达 |
| Event Group | 表示多个事件条件的位 | IP 就绪等启动条件 |
| Task Notification | 通知某个任务,或按模式计数/置位 | 唤醒采样任务 |
| Software Timer | 到期调度回调 | 延迟重试触发器 |
概念入口:FreeRTOS 官方文档;落到 ESP32-S3 上,优先核对 IDF API。
Queue 的“复制”是什么意思
假设队列 item size 是 sizeof(sample_summary_t),发送时复制这个大小的内容;队列不会推断结构体内的指针该做深拷贝。IDF Queue API
本项目第一轮快照只放整数、布尔值和固定大小字段,以简化生命周期。如果队列只传缓冲区指针,就必须约定发送失败谁释放、接收后谁释放、取消任务时如何回收。
队列满是必须设计的正常情况
假设每秒生产 10 条,消费者每秒只能处理 2 条,有界队列迟早会满;把容量调大只是推迟问题。
本项目建议:UI 的“当前读数”可以保留最新值并统计丢弃量;命令和报警不能静默丢弃,需要明确拒绝、重试或持久化策略。该区分来自业务语义,不是 Queue 自动提供的保证。
ISR 与任务的交接
中断中只做允许的短操作,使用合适的 ...FromISR API;是否请求切换到刚唤醒的高优先级任务,按对应 port 的要求处理。普通回调不一定在 ISR 中执行,先看调用上下文再选 API。IDF FreeRTOS API
软件定时器回调也不应承担长时间网络调用。让回调通知任务,任务再做可阻塞的工作。
三个验收实验
- 网络任务模拟延迟 5 秒,UI 计时与触摸应继续响应。
- 故意让生产快于消费,确认队列满时返回值、丢弃计数和界面行为符合设计。
- 连续运行一段时间,记录任务栈余量、空闲内存最低值和采样间隔;看趋势而不是只截一张正常数值。
过关标准:能解释谁拥有每份数据、任务在哪里阻塞,以及重启或停止任务前如何清理资源。当前未执行板卡并发实验。