学习笔记 · Obsidian

FreeRTOS:任务怎么协作,数据怎么交接

ESP-IDFC / C++

任务是并发执行单元

任务可以处于运行、就绪、阻塞或挂起状态。等待队列或延时的阻塞任务不需要空转占 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

软件定时器回调也不应承担长时间网络调用。让回调通知任务,任务再做可阻塞的工作。

三个验收实验

  1. 网络任务模拟延迟 5 秒,UI 计时与触摸应继续响应。
  2. 故意让生产快于消费,确认队列满时返回值、丢弃计数和界面行为符合设计。
  3. 连续运行一段时间,记录任务栈余量、空闲内存最低值和采样间隔;看趋势而不是只截一张正常数值。

过关标准:能解释谁拥有每份数据、任务在哪里阻塞,以及重启或停止任务前如何清理资源。当前未执行板卡并发实验。


返回学习入口 · 来源与版本