[PICO][Adv]任务调度器

Raspberry Pi Pico 2 任务调度器

在嵌入式编程中,任务调度器是一种用于管理多个任务执行的软件机制。它允许你在单个处理器核心上“同时”运行多个任务,而无需使用复杂的多线程技术。任务调度器通过将任务分解为小的时间片段,并在这些片段之间快速切换,从而实现“伪并行”执行,让多个任务看起来像是在同时运行。

本文将分别使用 Arduino 风格(基于 Arduino-Pico 核心)和 C/C++ 风格(基于官方 Pico SDK)来详细介绍任务调度器的设计、实现以及实际应用案例。


1. 什么是任务调度器?

任务调度器是一种软件机制,用于在有限的计算资源上管理多个任务的执行。它通过将 CPU 时间划分为多个时间片段,并按照一定的规则分配给不同的任务,使得多个任务可以共享处理器。

在 Raspberry Pi Pico 2 这样的单核微控制器上,任务调度器尤为重要,因为它可以帮助你:

  • 组织复杂逻辑:将系统功能拆分为独立的任务模块
  • 提高响应性:确保每个任务都能按时获得 CPU 时间
  • 简化代码结构:避免在主循环中编写冗长的顺序逻辑
  • 便于维护和扩展:新增任务不影响现有任务

1.1 任务调度器的类型

类型 描述 适用场景
时间片轮转 每个任务分配固定的时间片,轮流执行 任务执行时间相近的场景
优先级调度 高优先级任务优先执行 实时性要求高的场景
事件驱动调度 任务仅在特定事件发生时执行 低功耗、事件驱动的系统
协作式调度 任务主动让出 CPU 控制权 任务结构清晰、无抢占需求
抢占式调度 高优先级任务可打断低优先级任务 严格实时系统(需 RTOS)

本文将重点介绍协作式时间片调度,这是最适合 Pico 2 裸机编程的调度方式,无需引入复杂的 RTOS。


2. 简单的任务调度器实现

2.1 基于时间间隔的简单调度器(Arduino 风格)

最简单的调度器使用 millis() 函数检查每个任务的执行时间间隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#define TASK_COUNT 3

// 任务函数声明
void taskReadSensor();
void taskControlLED();
void taskSendSerial();

// 任务上次执行时间记录
unsigned long lastRunTime[TASK_COUNT] = {0};

// 任务执行间隔(毫秒)
const unsigned long taskIntervals[TASK_COUNT] = {
500, // 传感器读取:每 500ms 一次
1000, // LED 控制:每 1 秒一次
2000 // 串口发送:每 2 秒一次
};

void setup() {
Serial.begin(115200);
pinMode(25, OUTPUT); // 板载 LED
}

void loop() {
unsigned long currentTime = millis();

// 检查任务 0:读取传感器
if (currentTime - lastRunTime[0] >= taskIntervals[0]) {
taskReadSensor();
lastRunTime[0] = currentTime;
}

// 检查任务 1:控制 LED
if (currentTime - lastRunTime[1] >= taskIntervals[1]) {
taskControlLED();
lastRunTime[1] = currentTime;
}

// 检查任务 2:串口发送
if (currentTime - lastRunTime[2] >= taskIntervals[2]) {
taskSendSerial();
lastRunTime[2] = currentTime;
}
}

void taskReadSensor() {
// 模拟读取传感器(实际可读取 ADC)
int sensorValue = analogRead(26);
// 仅打印到串口,实际可存储到全局变量
Serial.print("Sensor: ");
Serial.println(sensorValue);
}

void taskControlLED() {
static bool ledState = false;
ledState = !ledState;
digitalWrite(25, ledState);
}

void taskSendSerial() {
Serial.println("Heartbeat: System running");
}

2.2 基于时间间隔的简单调度器(C/C++ 风格)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include "pico/stdlib.h"
#include "hardware/adc.h"
#include <stdio.h>

#define TASK_COUNT 3

// 任务函数类型定义
typedef void (*TaskFunc)(void);

// 任务结构体
typedef struct {
TaskFunc func;
uint32_t interval_ms;
uint32_t last_run_ms;
} Task;

// 任务实例
Task tasks[TASK_COUNT];

// 任务函数实现
void task_read_sensor(void) {
uint16_t raw = adc_read();
printf("Sensor: %u\n", raw);
}

void task_control_led(void) {
static bool led_state = false;
led_state = !led_state;
gpio_put(25, led_state);
}

void task_send_serial(void) {
printf("Heartbeat: System running\n");
}

int main() {
stdio_init_all();

// 初始化 ADC(用于传感器读取)
adc_init();
adc_gpio_init(26);
adc_select_input(0);

// 初始化 LED
gpio_init(25);
gpio_set_dir(25, GPIO_OUT);

// 初始化任务表
tasks[0].func = task_read_sensor;
tasks[0].interval_ms = 500;
tasks[0].last_run_ms = 0;

tasks[1].func = task_control_led;
tasks[1].interval_ms = 1000;
tasks[1].last_run_ms = 0;

tasks[2].func = task_send_serial;
tasks[2].interval_ms = 2000;
tasks[2].last_run_ms = 0;

while (true) {
uint32_t now = to_ms_since_boot(get_absolute_time());

for (int i = 0; i < TASK_COUNT; i++) {
if (now - tasks[i].last_run_ms >= tasks[i].interval_ms) {
tasks[i].func();
tasks[i].last_run_ms = now;
}
}

// 避免空循环占用过多 CPU,可适当延时
sleep_ms(1);
}
return 0;
}

3. 更完善的协作式任务调度器

上述简单调度器存在一个局限:每个任务必须在规定的时间间隔内执行完毕,否则会影响其他任务的调度。更完善的调度器应支持:

  • 动态添加/移除任务
  • 单次执行任务(延时执行)
  • 任务执行时间监控
  • 任务主动让出 CPU

3.1 任务调度器类设计(Arduino 风格)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Task.h - 任务调度器头文件
#ifndef TASK_H
#define TASK_H

#include <Arduino.h>

// 任务类型:周期性执行或单次执行
enum TaskType {
PERIODIC,
ONE_SHOT
};

// 任务回调函数类型
typedef void (*TaskCallback)();

// 任务结构体
struct Task {
TaskCallback callback;
unsigned long interval; // 执行间隔(毫秒)
unsigned long lastRunTime; // 上次执行时间
unsigned long delay; // 延时启动时间
TaskType type;
bool enabled;
bool pendingRemove; // 标记待删除
};

class TaskScheduler {
private:
static const int MAX_TASKS = 10;
Task tasks[MAX_TASKS];
int taskCount;

public:
TaskScheduler() : taskCount(0) {}

// 添加周期性任务
int addPeriodicTask(TaskCallback callback, unsigned long interval, unsigned long delay = 0) {
if (taskCount >= MAX_TASKS) return -1;
tasks[taskCount].callback = callback;
tasks[taskCount].interval = interval;
tasks[taskCount].lastRunTime = millis();
tasks[taskCount].delay = delay;
tasks[taskCount].type = PERIODIC;
tasks[taskCount].enabled = true;
tasks[taskCount].pendingRemove = false;
return taskCount++;
}

// 添加单次任务(延时执行一次)
int addOneShotTask(TaskCallback callback, unsigned long delay) {
if (taskCount >= MAX_TASKS) return -1;
tasks[taskCount].callback = callback;
tasks[taskCount].interval = 0;
tasks[taskCount].lastRunTime = millis();
tasks[taskCount].delay = delay;
tasks[taskCount].type = ONE_SHOT;
tasks[taskCount].enabled = true;
tasks[taskCount].pendingRemove = false;
return taskCount++;
}

// 禁用任务
void disableTask(int id) {
if (id >= 0 && id < taskCount) {
tasks[id].enabled = false;
}
}

// 启用任务
void enableTask(int id) {
if (id >= 0 && id < taskCount) {
tasks[id].enabled = true;
}
}

// 标记任务待删除
void removeTask(int id) {
if (id >= 0 && id < taskCount) {
tasks[id].pendingRemove = true;
}
}

// 运行调度器(在主循环中调用)
void run() {
unsigned long now = millis();

for (int i = 0; i < taskCount; i++) {
if (!tasks[i].enabled) continue;

// 检查是否需要执行
bool shouldRun = false;

if (tasks[i].type == ONE_SHOT) {
// 单次任务:检查延时是否已到
if (tasks[i].delay > 0 && (now - tasks[i].lastRunTime) >= tasks[i].delay) {
shouldRun = true;
}
} else {
// 周期性任务:检查执行间隔
if ((now - tasks[i].lastRunTime) >= tasks[i].interval) {
shouldRun = true;
}
}

if (shouldRun) {
tasks[i].callback(); // 执行任务
tasks[i].lastRunTime = now; // 更新上次执行时间

// 单次任务执行后标记删除
if (tasks[i].type == ONE_SHOT) {
tasks[i].pendingRemove = true;
}
}
}

// 清理标记删除的任务(避免在遍历中直接删除)
for (int i = 0; i < taskCount; i++) {
if (tasks[i].pendingRemove) {
// 将最后一个任务移到当前位置,减少碎片
tasks[i] = tasks[taskCount - 1];
taskCount--;
i--; // 重新检查当前位置
}
}
}

// 获取任务数量
int getTaskCount() { return taskCount; }
};

#endif

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "Task.h"

TaskScheduler scheduler;

// 定义任务函数
void blinkLED() {
static bool state = false;
state = !state;
digitalWrite(25, state);
Serial.println("LED toggled");
}

void readSensor() {
int val = analogRead(26);
Serial.print("Sensor: ");
Serial.println(val);
}

void oneTimeTask() {
Serial.println("One-time task executed!");
}

void setup() {
Serial.begin(115200);
pinMode(25, OUTPUT);

// 添加周期性任务:每 500ms 闪烁 LED
scheduler.addPeriodicTask(blinkLED, 500);

// 添加周期性任务:每 2 秒读取传感器
scheduler.addPeriodicTask(readSensor, 2000);

// 添加单次任务:5 秒后执行一次
scheduler.addOneShotTask(oneTimeTask, 5000);
}

void loop() {
scheduler.run();
// 主循环可以做其他低优先级工作
}

3.2 C/C++ 风格的任务调度器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "pico/stdlib.h"
#include <stdio.h>
#include <stdlib.h>

#define MAX_TASKS 10

// 任务类型
typedef enum {
TASK_PERIODIC,
TASK_ONE_SHOT
} TaskType;

// 任务回调函数类型
typedef void (*TaskCallback)(void);

// 任务结构体
typedef struct {
TaskCallback callback;
uint32_t interval_ms;
uint32_t last_run_ms;
uint32_t delay_ms;
TaskType type;
bool enabled;
bool pending_remove;
} Task;

// 任务调度器
typedef struct {
Task tasks[MAX_TASKS];
int task_count;
} TaskScheduler;

// 初始化调度器
void scheduler_init(TaskScheduler* scheduler) {
scheduler->task_count = 0;
for (int i = 0; i < MAX_TASKS; i++) {
scheduler->tasks[i].callback = NULL;
scheduler->tasks[i].enabled = false;
}
}

// 添加周期性任务
int scheduler_add_periodic(TaskScheduler* scheduler, TaskCallback callback,
uint32_t interval_ms, uint32_t delay_ms) {
if (scheduler->task_count >= MAX_TASKS) return -1;

Task* task = &scheduler->tasks[scheduler->task_count];
task->callback = callback;
task->interval_ms = interval_ms;
task->last_run_ms = to_ms_since_boot(get_absolute_time());
task->delay_ms = delay_ms;
task->type = TASK_PERIODIC;
task->enabled = true;
task->pending_remove = false;

return scheduler->task_count++;
}

// 添加单次任务
int scheduler_add_oneshot(TaskScheduler* scheduler, TaskCallback callback,
uint32_t delay_ms) {
if (scheduler->task_count >= MAX_TASKS) return -1;

Task* task = &scheduler->tasks[scheduler->task_count];
task->callback = callback;
task->interval_ms = 0;
task->last_run_ms = to_ms_since_boot(get_absolute_time());
task->delay_ms = delay_ms;
task->type = TASK_ONE_SHOT;
task->enabled = true;
task->pending_remove = false;

return scheduler->task_count++;
}

// 启用/禁用任务
void scheduler_enable_task(TaskScheduler* scheduler, int id, bool enable) {
if (id >= 0 && id < scheduler->task_count) {
scheduler->tasks[id].enabled = enable;
}
}

// 标记任务待删除
void scheduler_remove_task(TaskScheduler* scheduler, int id) {
if (id >= 0 && id < scheduler->task_count) {
scheduler->tasks[id].pending_remove = true;
}
}

// 运行调度器
void scheduler_run(TaskScheduler* scheduler) {
uint32_t now = to_ms_since_boot(get_absolute_time());

for (int i = 0; i < scheduler->task_count; i++) {
Task* task = &scheduler->tasks[i];
if (!task->enabled) continue;

bool should_run = false;

if (task->type == TASK_ONE_SHOT) {
if (task->delay_ms > 0 && (now - task->last_run_ms) >= task->delay_ms) {
should_run = true;
}
} else {
if ((now - task->last_run_ms) >= task->interval_ms) {
should_run = true;
}
}

if (should_run) {
task->callback();
task->last_run_ms = now;

if (task->type == TASK_ONE_SHOT) {
task->pending_remove = true;
}
}
}

// 清理待删除任务
for (int i = 0; i < scheduler->task_count; i++) {
if (scheduler->tasks[i].pending_remove) {
scheduler->tasks[i] = scheduler->tasks[scheduler->task_count - 1];
scheduler->task_count--;
i--; // 重新检查当前位置
}
}
}

// ========== 使用示例 ==========

TaskScheduler scheduler;

void blink_led(void) {
static bool state = false;
state = !state;
gpio_put(25, state);
printf("LED toggled\n");
}

void read_sensor(void) {
printf("Reading sensor...\n");
}

void one_time_task(void) {
printf("One-time task executed!\n");
}

int main() {
stdio_init_all();
gpio_init(25);
gpio_set_dir(25, GPIO_OUT);

scheduler_init(&scheduler);
scheduler_add_periodic(&scheduler, blink_led, 500, 0);
scheduler_add_periodic(&scheduler, read_sensor, 2000, 0);
scheduler_add_oneshot(&scheduler, one_time_task, 5000);

while (true) {
scheduler_run(&scheduler);
// 可在此添加其他低优先级工作,或进入低功耗模式
sleep_ms(1);
}
return 0;
}

4. 高级调度器功能

4.1 异步任务与延时执行

在实际应用中,有时需要在某个任务中安排另一个任务在未来执行。这可以通过在任务回调中调用 addOneShotTask 实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 示例:读取传感器后延时发送数据
void sendData() {
Serial.println("Data sent after delay");
}

void readAndSchedule() {
int val = analogRead(26);
Serial.print("Sensor value: ");
Serial.println(val);

// 2 秒后发送数据
scheduler.addOneShotTask(sendData, 2000);
}

4.2 任务执行时间监控

为了防止某个任务占用过长时间,可以在调度器中添加执行时间监控。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void scheduler_run() {
unsigned long now = millis();

for (int i = 0; i < taskCount; i++) {
if (shouldRun(i)) {
unsigned long start = micros(); // 记录开始时间
tasks[i].callback();
unsigned long duration = micros() - start; // 计算耗时

if (duration > MAX_TASK_DURATION) {
Serial.print("Warning: Task ");
Serial.print(i);
Serial.print(" took ");
Serial.print(duration);
Serial.println(" us");
}
}
}
}

4.3 空闲任务与低功耗

当没有任务需要执行时,可以让 Pico 2 进入低功耗模式以节省电能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void scheduler_run() {
bool anyTaskExecuted = false;

for (int i = 0; i < taskCount; i++) {
if (shouldRun(i)) {
tasks[i].callback();
anyTaskExecuted = true;
}
}

// 如果没有任务执行,进入轻度休眠
if (!anyTaskExecuted) {
__wfi(); // Wait For Interrupt(ARM 指令)
}
}

5. 实际应用案例

5.1 智能环境监测站

设计一个环境监测系统,同时采集温度、湿度、光照,并定时上报数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "Task.h"

TaskScheduler scheduler;

// 模拟传感器读取(实际可连接 DHT22、光敏电阻等)
float temperature = 25.0;
float humidity = 60.0;
int lightLevel = 300;

void readTemperature() {
// 模拟读取温度(实际使用 ADC 或数字传感器)
temperature = 25.0 + (random(-10, 10) / 10.0);
Serial.print("Temp: ");
Serial.println(temperature);
}

void readHumidity() {
humidity = 60.0 + random(-5, 5);
Serial.print("Humidity: ");
Serial.println(humidity);
}

void readLight() {
lightLevel = analogRead(26);
Serial.print("Light: ");
Serial.println(lightLevel);
}

void reportData() {
Serial.println("=== System Report ===");
Serial.print("Temperature: "); Serial.println(temperature);
Serial.print("Humidity: "); Serial.println(humidity);
Serial.print("Light: "); Serial.println(lightLevel);
Serial.println("====================");
}

void blinkLED() {
static bool state = false;
state = !state;
digitalWrite(25, state);
}

void setup() {
Serial.begin(115200);
pinMode(25, OUTPUT);

// 配置不同频率的采样任务
scheduler.addPeriodicTask(readTemperature, 2000); // 2 秒
scheduler.addPeriodicTask(readHumidity, 3000); // 3 秒
scheduler.addPeriodicTask(readLight, 1000); // 1 秒
scheduler.addPeriodicTask(reportData, 10000); // 10 秒上报
scheduler.addPeriodicTask(blinkLED, 500); // 0.5 秒心跳
}

void loop() {
scheduler.run();
}

5.2 多路 PWM 呼吸灯效果

使用调度器同时控制多个 LED 以不同的频率呼吸。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "Task.h"

TaskScheduler scheduler;

const int ledPins[] = {0, 1, 2, 3}; // 4 个 LED 引脚
int brightness[4] = {0, 0, 0, 0};
int fadeAmount[4] = {5, 3, 7, 4}; // 每个 LED 不同的渐变速度

void setup() {
for (int i = 0; i < 4; i++) {
pinMode(ledPins[i], OUTPUT);
// 添加每个 LED 的独立任务
scheduler.addPeriodicTask([i]() {
analogWrite(ledPins[i], brightness[i]);
brightness[i] += fadeAmount[i];
if (brightness[i] <= 0 || brightness[i] >= 255) {
fadeAmount[i] = -fadeAmount[i];
}
}, 30); // 每 30ms 更新一次
}
}

void loop() {
scheduler.run();
}

注意:Arduino 风格中不支持 lambda 表达式在普通 C++ 环境下?部分编译器支持。若不行,可将每个 LED 控制写成独立函数。


6. 任务调度器与 RTOS 的对比

特性 协作式任务调度器 RTOS(如 FreeRTOS)
资源占用 极小(仅需定时器和少量 RAM) 较大(需要任务栈、内核对象)
任务抢占 不支持,任务主动让出 CPU 支持,高优先级可抢占
实时性 中等,取决于任务执行时间 高,可保证硬实时
学习曲线 简单 较陡峭
适用场景 简单到中等复杂度项目 复杂、多优先级、强实时项目
Pico 2 支持 原生裸机实现 官方 SDK 支持 FreeRTOS 移植

对于大多数 Pico 2 项目,协作式调度器已经足够;当项目复杂度增加(如需要多优先级、信号量、消息队列等),可以考虑迁移到 FreeRTOS。


7. 总结

任务调度器是管理嵌入式系统多任务的强大工具。通过本文,你应该已经掌握了:

  • 任务调度器的基本概念和实现原理
  • 两种编程风格(Arduino 风格和 C/C++ 风格)的简单和进阶调度器实现
  • 高级功能:动态任务管理、单次任务、执行时间监控
  • 实际应用案例:环境监测站、多路 PWM 控制
调度器类型 适用场景
简单时间片调度 少量周期性任务,执行时间稳定
协作式任务调度器 多任务、不同周期、需动态管理
抢占式 RTOS 复杂系统、强实时性要求

8. 练习与拓展

  • 练习 1:为调度器添加“任务优先级”功能,高优先级任务获得更多 CPU 时间。
  • 练习 2:实现一个“看门狗”任务,监控其他任务是否按时执行,若超时则报警。
  • 练习 3:结合之前的事件驱动编程,让任务可以由外部事件触发执行。
  • 练习 4:将调度器移植到 FreeRTOS,体验抢占式调度的区别。

通过掌握任务调度器,你将能够编写出结构清晰、响应及时的 Pico 2 应用程序,轻松应对多任务并发场景。


[PICO][Adv]任务调度器
https://ka5fxt.cn/2026/03/30/PICO-Adv-任务调度器/
发布于
2026年3月30日
许可协议