嵌入式调度器开发学习笔记
本笔记涵盖裸机开发、协作式调度器、抢占式调度器三种开发模式的对比与选型
目录
- 三种开发模式总览
- 裸机开发
- 协作式调度器
- 抢占式调度器
- 核心对比分析
- 选型指南
- 实战代码模板
1. 三种开发模式总览
| 维度 |
裸机开发 |
协作式调度器 |
抢占式调度器 |
| 执行模型 |
单一主循环 |
时间片轮询 |
优先级抢占 |
| 任务切换 |
顺序执行 |
任务主动让出 |
调度器强制切换 |
| 实时性 |
差 |
软实时 |
硬实时 |
| 内存开销 |
最小 |
小 (~100B) |
大 (每任务独立栈) |
| 复杂度 |
简单 |
中等 |
复杂 |
| 典型代表 |
while(1) 轮询 |
时间片调度器 |
FreeRTOS, RT-Thread |
2. 裸机开发
2.1 基本架构
int main(void) {
System_Init();
while(1) {
Task_LED(); // 任务1
Task_Key(); // 任务2
Task_UART(); // 任务3
Task_Sensor(); // 任务4
}
}
2.2 时间管理方式
// 每个任务内部自己管理执行时间
void Task_LED(void) {
static uint32_t last_tick = 0;
if (HAL_GetTick() - last_tick >= 500) {
last_tick = HAL_GetTick();
HAL_GPIO_TogglePin(LED_GPIO, LED_PIN);
}
}
void Task_Key(void) {
static uint32_t last_tick = 0;
if (HAL_GetTick() - last_tick >= 10) {
last_tick = HAL_GetTick();
Key_Scan();
}
}
2.3 特点分析
| 优点 |
缺点 |
简单直观 |
任务耦合度高 |
资源占用极低 |
增删任务需改主循环 |
无额外内存开销 |
时间管理代码重复 |
无并发安全问题 |
一个阻塞全部卡死 |
2.4 执行时序
时间轴 →
┌────┬────┬────┬────┬────┬────┬────┬────┐
│LED │Key │UART│Sens│LED │Key │UART│Sens│
│判断│判断│判断│判断│判断│判断│判断│判断│
└────┴────┴────┴────┴────┴────┴────┴────┘
↑ 每次循环遍历所有任务
响应延迟 = T1 + T2 + T3 + T4 (所有任务执行时间之和)
3. 协作式调度器
3.1 基本架构
// 任务结构体定义
typedef struct {
void (*func)(void); // 任务函数指针
uint32_t period; // 执行周期 (ms)
uint32_t last_run; // 上次执行时间
uint8_t enable; // 使能标志
} Task_t;
// 任务表:集中管理
Task_t tasks[] = {
{ Task_LED, 500, 0, 1 },
{ Task_Key, 10, 0, 1 },
{ Task_UART, 1, 0, 1 },
{ Task_Sensor, 100, 0, 1 },
};
int main(void) {
System_Init();
Scheduler_Init(tasks, 4);
while(1) {
Scheduler_Run();
}
}
3.2 调度器实现
static Task_t *task_list;
static uint8_t task_count;
void Scheduler_Init(Task_t *tasks, uint8_t count) {
task_list = tasks;
task_count = count;
}
void Scheduler_Run(void) {
uint32_t now = HAL_GetTick();
for (uint8_t i = 0; i < task_count; i++) {
if (!task_list[i].enable) continue;
if (now - task_list[i].last_run >= task_list[i].period) {
task_list[i].last_run = now;
task_list[i].func();
}
}
}
// 动态控制接口
void Scheduler_Enable(uint8_t id, uint8_t en) {
task_list[id].enable = en;
}
void Scheduler_SetPeriod(uint8_t id, uint32_t period) {
task_list[id].period = period;
}
3.3 任务函数规范
// ✅ 正确:专注业务逻辑,快速返回
void Task_LED(void) {
LED_Toggle();
}
void Task_Motor(void) {
Motor_PID_Step(); // 执行一步就返回
}
// ❌ 错误:阻塞式代码
void Task_Wrong(void) {
while(1) { // 死循环,系统卡死
// ...
}
HAL_Delay(1000); // 阻塞延时,其他任务无法执行
}
3.4 与裸机的核心区别
| 维度 |
裸机 |
协作式调度器 |
| 时间管理 |
每个任务自己管理 |
调度器统一管理 |
| 任务增删 |
修改主循环 |
仅修改任务表 |
| 动态控制 |
不支持 |
支持启停/改周期 |
| 代码复用 |
差 |
好 |
3.5 执行时序
调度器检查任务表,只执行到期的任务
t=0 t=10 t=20 t=100 t=500
UART Key Key Sensor LED
UART UART UART Key Key
UART UART
4. 抢占式调度器
4.1 基本架构 (FreeRTOS)
int main(void) {
// 创建任务,分配独立栈空间
xTaskCreate(Task_LED, "LED", 128, NULL, 1, NULL); // 低优先级
xTaskCreate(Task_Motor, "Motor", 256, NULL, 3, NULL); // 高优先级
xTaskCreate(Task_HMI, "HMI", 512, NULL, 2, NULL); // 中优先级
vTaskStartScheduler(); // 启动调度器
}
// 任务函数:可以有死循环和阻塞
void Task_Motor(void *param) {
while(1) {
Motor_PID_Control();
vTaskDelay(pdMS_TO_TICKS(1)); // 非阻塞等待
}
}
void Task_LED(void *param) {
while(1) {
LED_Toggle();
vTaskDelay(pdMS_TO_TICKS(500));
}
}
4.2 抢占机制
高优先级任务可随时打断低优先级任务
时间轴 →
┌────┬──┬────┬──┬────┬──────────┐
│ T1 │T3│ T1 │T3│ T2 │ T1 │
│低 │高│低 │高│中 │ 低 │
└────┴──┴────┴──┴────┴──────────┘
↑ ↑
T3抢占 T3抢占
最坏响应时间 ≈ 上下文切换时间 (微秒级)
4.3 内存模型
┌─────────────────────────┐
│ Task1 栈 (256B) │
├─────────────────────────┤
│ Task2 栈 (512B) │
├─────────────────────────┤
│ Task3 栈 (256B) │
├─────────────────────────┤
│ 系统栈 + TCB (~64B) │
├─────────────────────────┤
│ 全局变量 │
└─────────────────────────┘
典型开销:内核 6-10KB + 每任务 128-1024B
4.4 并发安全
// 抢占式下必须保护共享资源
uint32_t g_counter = 0;
SemaphoreHandle_t mutex;
void Task_A(void *param) {
while(1) {
xSemaphoreTake(mutex, portMAX_DELAY); // 加锁
g_counter++;
Process(g_counter);
xSemaphoreGive(mutex); // 解锁
vTaskDelay(10);
}
}
void Task_B(void *param) {
while(1) {
xSemaphoreTake(mutex, portMAX_DELAY);
g_counter += 10;
xSemaphoreGive(mutex);
vTaskDelay(20);
}
}
5. 核心对比分析
5.1 任务响应时间
场景:紧急任务需要 1ms 响应
- Task_Slow: 执行 50ms
- Task_Normal: 执行 10ms
- Task_Urgent: 执行 1ms (紧急)
【协作式】
┌──────────────────────┬────────────┬─┐
│ Task_Slow │Task_Normal │U│
│ (50ms) │ (10ms) │ │
└──────────────────────┴────────────┴─┘
最坏响应:60ms ❌
【抢占式】
┌────┬─┬────┬─┬────┬─┬──────────────┐
│Slow│U│Slow│U│Slow│U│ Slow继续 │
└────┴─┴────┴─┴────┴─┴──────────────┘
最坏响应:~μs ✅
5.2 内存占用对比
| 配置 |
协作式 |
抢占式 (FreeRTOS) |
| 内核代码 |
~500B |
~6-10KB |
| 5任务总 RAM |
~200B |
~3-7KB |
| 最小可运行 |
1KB RAM |
4KB RAM |
5.3 特性对比
| 特性 |
协作式 |
抢占式 |
| 任务切换开销 |
几乎为零 |
上下文保存/恢复 |
| 代码可重入性 |
不要求 |
必须可重入 |
| 共享资源保护 |
通常不需要 |
需要互斥锁 |
| 任务间通信 |
全局变量 |
队列/信号量 |
| 死锁风险 |
无 |
有 |
| 调试难度 |
简单 |
复杂 |
6. 选型指南
6.1 决策流程图
┌─────────────────┐
│ 有硬实时要求? │
└────────┬────────┘
│
┌──────────────┴──────────────┐
│ 是 │ 否
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 抢占式 RTOS │ │ RAM < 4KB ? │
└─────────────────┘ └────────┬────────┘
│
┌───────────┴───────────┐
│ 是 │ 否
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 协作式/裸机 │ │ 任务 > 5 个? │
└─────────────────┘ └────────┬────────┘
│
┌───────────┴───────────┐
│ 是 │ 否
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 协作式调度器 │ │ 裸机开发 │
└─────────────────┘ └─────────────────┘
6.2 场景推荐
| 场景 |
推荐方案 |
| 简单控制 (LED、按键、少量传感器) |
裸机 |
| 中等复杂度 + 软实时 |
协作式调度器 |
| 多任务 + 硬实时要求 |
RTOS |
| 资源极度受限 (< 4KB RAM) |
裸机或状态机 |
| 网络协议栈、文件系统 |
RTOS |
| 电机控制、工业控制 |
RTOS |
7. 实战代码模板
7.1 完整协作式调度器
/* ============== scheduler.h ============== */
#ifndef __SCHEDULER_H
#define __SCHEDULER_H
#include <stdint.h>
typedef struct {
void (*func)(void);
uint32_t period;
uint32_t last_run;
uint8_t enable;
} Task_t;
void Scheduler_Init(Task_t *tasks, uint8_t count);
void Scheduler_Run(void);
void Scheduler_Enable(uint8_t id, uint8_t en);
void Scheduler_SetPeriod(uint8_t id, uint32_t period);
#endif
/* ============== scheduler.c ============== */
#include "scheduler.h"
static Task_t *task_list;
static uint8_t task_count;
extern uint32_t HAL_GetTick(void);
void Scheduler_Init(Task_t *tasks, uint8_t count) {
task_list = tasks;
task_count = count;
for (uint8_t i = 0; i < count; i++) {
task_list[i].last_run = 0;
task_list[i].enable = 1;
}
}
void Scheduler_Run(void) {
uint32_t now = HAL_GetTick();
for (uint8_t i = 0; i < task_count; i++) {
if (!task_list[i].enable) continue;
if (now - task_list[i].last_run >= task_list[i].period) {
task_list[i].last_run = now;
task_list[i].func();
}
}
}
void Scheduler_Enable(uint8_t id, uint8_t en) {
if (id < task_count) {
task_list[id].enable = en;
}
}
void Scheduler_SetPeriod(uint8_t id, uint32_t period) {
if (id < task_count) {
task_list[id].period = period;
}
}
/* ============== main.c ============== */
#include "scheduler.h"
// 任务函数声明
void Task_LED(void);
void Task_Key(void);
void Task_UART(void);
// 任务表
Task_t tasks[] = {
{ Task_LED, 500, 0, 1 },
{ Task_Key, 10, 0, 1 },
{ Task_UART, 1, 0, 1 },
};
#define TASK_COUNT (sizeof(tasks) / sizeof(tasks[0]))
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init();
Scheduler_Init(tasks, TASK_COUNT);
while(1) {
Scheduler_Run();
}
}
void Task_LED(void) {
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
void Task_Key(void) {
// 按键扫描逻辑
}
void Task_UART(void) {
// 串口处理逻辑
}
7.2 混合架构 (中断 + 调度器)
// 中断处理紧急事件 + 主循环调度后台任务
void SysTick_Handler(void) {
HAL_IncTick();
Motor_PID_Control(); // 1ms 硬实时任务
}
int main(void) {
System_Init();
Scheduler_Init(tasks, TASK_COUNT);
while(1) {
Scheduler_Run();
__WFI(); // 低功耗等待
}
}
8. 总结
8.1 一句话概括
| 模式 |
核心特点 |
| 裸机 |
顺序执行,每个任务自己管时间 |
| 协作式 |
调度器管时间,任务必须主动让出 CPU |
| 抢占式 |
调度器强制切换,高优先级可打断低优先级 |
8.2 本质区别
- 裸机 vs 协作式:时间管理从"分散"到"集中"
- 协作式 vs 抢占式:任务切换从"自觉"到"强制"
8.3 记忆口诀
裸机简单资源省,任务耦合维护难;
协作调度表驱动,主动让出是关键;
抢占实时响应快,互斥死锁要防范。
笔记整理时间:2026-02-21