# 引言 在嵌入式开发中,多任务处理是常见需求。然而,对于 Arduino 这类资源有限的平台,完整 RTOS(如 FreeRTOS)可能占用过多 RAM/Flash,且引入额外复杂度。协作式调度器(Cooperative Scheduler)提供了一种轻量级替代方案:任务主动让出 CPU,由调度器按策略切换。结合状态机(State Machine),我们可以实现非阻塞的任务切换,让系统看起来“同时”运行多个任务。 # 原理讲解 ## 协作式调度 vs 抢占式调度 - **抢占式调度**:由定时器中断强制切换任务,需要保存/恢复上下文,对硬件要求高。 - **协作式调度**:任务自行决定何时让出 CPU(如调用 `yield()`),无需硬件上下文切换,实现简单,适合事件驱动或周期性任务。 ## 状态机任务模型 每个任务被建模为一个有限状态机(FSM)。任务在每次被调度时执行一个“步骤”,根据当前状态执行相应操作,然后转移到下一个状态。通过状态转移,任务可以分阶段完成长操作,避免阻塞。 ## 调度策略 最简单的协作式调度器采用**轮询(Round-Robin)**:按顺序依次调用每个任务的 `update()` 函数。任务内部通过状态机控制执行节奏,若某任务需等待外部事件,则直接返回,不占用 CPU。 # 实现步骤 ## 1. 定义任务结构 我们定义一个 `Task` 结构体,包含状态变量、周期、上次执行时间等。 ```c typedef struct { void (*update)(void); // 任务函数指针 uint32_t period; // 执行周期(ms) uint32_t last_run; // 上次执行时间 uint8_t state; // 当前状态(用户自定义) } Task; ``` ## 2. 实现调度器核心 调度器维护一个任务数组,并在 `loop()` 中遍历调用。 ```c #define MAX_TASKS 5 Task tasks[MAX_TASKS]; uint8_t task_count = 0; void scheduler_init() { task_count = 0; } bool add_task(void (*update)(void), uint32_t period) { if (task_count >= MAX_TASKS) return false; tasks[task_count].update = update; tasks[task_count].period = period; tasks[task_count].last_run = 0; tasks[task_count].state = 0; task_count++; return true; } void scheduler_run() { uint32_t now = millis(); for (uint8_t i = 0; i < task_count; i++) { if (now - tasks[i].last_run >= tasks[i].period) { tasks[i].last_run = now; tasks[i].update(); // 调用任务函数 } } } ``` ## 3. 编写状态机任务示例 以 LED 闪烁和串口打印为例,展示状态机写法。 ```c // 任务1:LED 闪烁(状态机) #define LED_PIN 13 #define STATE_LED_ON 0 #define STATE_LED_OFF 1 void task_led_update() { switch (tasks[0].state) { // 假设任务索引为0 case STATE_LED_ON: digitalWrite(LED_PIN, HIGH); tasks[0].state = STATE_LED_OFF; break; case STATE_LED_OFF: digitalWrite(LED_PIN, LOW); tasks[0].state = STATE_LED_ON; break; } } // 任务2:串口打印计数(状态机) #define STATE_PRINT 0 void task_print_update() { static int count = 0; switch (tasks[1].state) { case STATE_PRINT: Serial.print("Count: "); Serial.println(count++); tasks[1].state = STATE_PRINT; // 保持状态 break; } } ``` ## 4. 在 Arduino 中集成 ```c void setup() { Serial.begin(9600); pinMode(LED_PIN, OUTPUT); scheduler_init(); add_task(task_led_update, 500); // 每500ms切换LED add_task(task_print_update, 1000); // 每1000ms打印 } void loop() { scheduler_run(); } ``` # 注意事项 - **任务函数必须非阻塞**:避免使用 `delay()`,改用状态机或记录时间戳。 - **状态管理**:每个任务的状态变量应独立,建议使用 `task[i].state` 而非全局变量,以便调度器管理。 - **周期精度**:`millis()` 基于定时器,若任务执行时间过长,可能影响其他任务,需确保每个任务执行时间远小于周期。 - **任务数量限制**:`MAX_TASKS` 根据 RAM 调整,避免浪费。 - **可扩展性**:可增加优先级或时间片,但保持简单是协作式调度的优势。 # 总结 通过状态机和协作式调度,我们在 Arduino 上实现了轻量级多任务,无需 RTOS 即可满足大部分场景。这种方法不仅节省资源,还让代码结构清晰,易于调试。掌握这一技巧,你将能更高效地开发复杂的嵌入式应用。