将部分代码作为应用程序中的任务独立执行可以简化大型复杂问题的设计。 当有多个 CPU 时,任务支持还允许选定的功能并行运行。 本文将调查 Arduino 框架对 ESP32 系列设备的 FreeRTOS 任务支持。 除了少数例外,本材料适用于您可能遇到的其他使用 FreeRTOS 的硬件平台。
https://embed.notionlytics.com/wt/ZXlKd1lXZGxTV1FpT2lJMk5ERXhabUkyTXpBMU56azBOREptT1RjMk1EVmpNREZoTWpBelpXVmlNQ0lzSW5kdmNtdHpjR0ZqWlZSeVlXTnJaWEpKWkNJNklsZHNTR2hsVEZSUFdXeHpaVmRhUW1ZNU1YQmxJbjA9
在单核 MCU(微控制器单元)中,任何时刻只能执行一项任务。 正在执行的任务会一直运行,直到硬件计时器指示时间片已过期。 超时时,FreeRTOS 调度程序通过保存其寄存器来保存当前任务的状态。 据说当前任务已被计时器抢占。
然后调度程序选择另一个准备运行的任务。 准备好运行的最高优先级任务的状态在它停止的地方恢复和恢复。 时间片的持续时间足够小,MCU 每秒可以运行多个任务。 这称为并发处理。
硬件计时器对于并发处理至关重要,因为它可以防止一项任务独占 CPU。 陷入永无止境循环的任务不会阻止其他任务的执行。 最后,抢占式调度允许编写任务函数,就好像它是系统中唯一的程序一样。 程序员的意外之喜!
由于增加了 CPU,双核 ESP32 可以同时执行两个任务。 这可能完成两倍的工作。 然而,乐鑫芯片的命名约定可能会令人困惑。 ESP32 可以是单核或双核芯片。 还有ESP32-S,可能还是双核的。 最后,新的 ESP32-S2 是单核 CPU。 表 1 按标识符列出了一些常用的 ESP 芯片。
$$ \begin{array}{|l|l|l|}\hline \text { Identifier } & \text {Cores }& \text { Description } \\\hline \text { ESP32-D0WDQ6 } & 2 & \text { Initial production release chip of the ESP32 series. } \\\hline \text { ESP32-D0WD } & 2 & \text { Smaller physical package variation similar to ESP32-D0WDQ6. } \\\hline \text { ESP32D2WD } & 2 & 2 \text { MB (16 Mb) embedded flash memory variation. } \\\hline \text { ESP32S0WD } & 1 & \text { Single-core processor variation. } \\\hline \text { ESP32-S2 } & 1 & \text { The newest S2 variant. } \\\hline\end{array} $$
很多时候,双核程序可以适用于在单核处理器上运行,但看门狗定时器等复杂因素通常需要额外的措施。
在我们回顾任务创建和控制之前,让我们检查一下您在程序开始时继承的 Arduino 环境。 您的程序是否在任务中启动? 确实如此! 除了您的任务之外,其他 FreeRTOS 任务也在后台执行。 其中一些任务提供服务,如定时器、WiFi、TCP/IP,蓝牙等。
表 2 说明了调用函数 setup() 和 loop() 时运行的 FreeRTOS 任务。 名为 loopTask 的任务是主要的 Arduino 任务,它调用函数 setup() 和 loop()。
$$ \begin{array}{|l|l|l|l|l|}\hline \text { Task Name } & \text { Task\# } & \text { Priority } & \text { Stack } & \text { CPU } \\\hline \text { loopTask } & 12 & 1 & 5188 & 1 \\\hline \text { Tmr Svc } & 8 & 1 & 1468 & 0 \\\hline \text { IDLE1 } & 7 & 0 & 592 & 1 \\\hline \text { IDLE0 } & 6 & 0 & 396 & 0 \\\hline \text { ipc1 } & 3 & 24 & 480 & 1 \\\hline \text { ipc0 } & 2 & 24 & 604 & 0 \\\hline \text { esp\_timer } & 1 & 22 & 4180 & 0 \\\hline\end{array} $$
标记为 Stack 的列表示未使用的堆栈字节(每个任务都需要自己的堆栈)。 此表按任务编号按时间倒序排序。 loopTask 是最后一个创建的任务。 缺少任务编号意味着其他任务已创建并在其工作完成时结束。 优先级列说明了分配的任务优先级,零代表最低执行优先级。
最后,双核 ESP32 上的任务在 CPU 0 和 CPU 1 之间分配。乐鑫将支持任务放在 CPU 0 中,而应用程序任务在 CPU 1 上运行。这样可以保持 WiFi、TCP/IP 和蓝牙等服务运行 无需特别考虑您的应用程序。 尽管有这个约定,您仍然可以在任一 CPU 中创建任务。自然,当使用单 CPU 平台时,一切都在 CPU 0 中运行。
从这个例子中,我们可以注意到一些有趣的点: