裸机任务调度框架通常指的是在没有操作系统的情况下,通过某种机制实现多任务的调度。常见的调度方法包括前后台系统、时间片轮询、状态机、协程等。
时间片轮询是比较常见的,通过定时器中断切换任务;状态机适合处理不同状态的任务;协程可以实现协作式多任务;事件驱动则依赖事件触发任务执行。可能还有基于优先级的调度,但裸机下实现复杂。
但是对于各个框架都是各自的优缺点的,时间片轮询实现简单,但实时性不高;状态机适合流程控制,但任务多了管理复杂;协程需要栈管理,可能占用较多内存;事件驱动响应快,但需要事件触发机制。
这个时候我觉得时间片轮询和事件驱动结合可能是个不错的选择,既有时间管理,又能响应外部事件。或者协程方式,但需要处理栈空间。
我的代码设计思路:
- 需要选择一个调度框架,比如时间片轮询,用SysTick定时器作为时基,任务函数在循环中按时间片执行
- 或者使用状态机,每个任务分成多个状态,每次执行一个状态。
- 或者用协作式调度,任务主动让出CPU
- 还要考虑到中断处理,使用SysTick中断来更新时间基准,但裸机下可能直接在循环中读取时间戳,减少中断依赖
一、常见裸机任务调度框架
1.1 前后台系统(超级循环)
-
原理:主循环(后台)轮询任务,中断处理(前台)处理紧急事件。
-
优点:
-
实现简单,无需复杂调度逻辑。
-
资源占用极低。
-
-
缺点:
-
实时性差,任务可能因阻塞导致响应延迟。
-
难以处理复杂多任务场景。
-
1.2 时间片轮询调度
-
原理:通过定时器中断划分时间片,按固定周期轮询任务。
-
优点:
-
任务执行时间可控。
-
支持优先级划分(需手动管理)。
-
-
缺点:
-
高优先级任务可能阻塞低优先级任务。
-
时间片划分不合理会导致任务饥饿。
-
1.3 状态机调度
-
原理:每个任务分解为多个状态,通过状态迁移控制执行流程。
-
优点:
-
逻辑清晰,适合流程化任务(如通信协议)。
-
非阻塞设计,节省CPU资源。
-
-
缺点:
-
状态迁移逻辑复杂,代码维护成本高。
-
难以实现任务抢占。
-
1.4 协程(Coroutine)调度
-
原理:通过手动切换任务上下文(保存/恢复寄存器),实现协作式多任务。
-
优点:
-
任务切换开销低。
-
代码结构类似多线程,易于理解。
-
-
缺点:
-
需要手动管理栈空间,易出现栈溢出。
-
协作式调度需任务主动让出CPU。
-
1.5 事件驱动调度
-
原理:任务由事件触发(如按键、定时器到期),通过事件队列管理任务执行。
-
优点:
-
响应速度快,适合异步事件处理。
-
资源利用率高。
-
-
缺点:
-
事件队列管理复杂。
-
需处理事件优先级和冲突。
-
二、框架对比和选型
框架类型 | 实时性 | 资源占用 | 复杂度 | 适用场景 |
---|---|---|---|---|
前后台系统 | 低 | 极低 | 低 | 简单控制逻辑 |
时间片轮询 | 中 | 低 | 中 | 周期性任务(如数据采集) |
状态机 | 中 | 低 | 高 | 流程化任务(如协议解析) |
协程 | 中 | 中 | 高 | 协作式多任务 |
事件驱动 | 高 | 中 | 高 | 异步事件处理(如GUI) |
最佳选择:时间片轮询 + 事件驱动混合调度
-
理由:
-
平衡性:时间片轮询确保周期性任务按时执行,事件驱动处理异步请求。
-
扩展性:支持优先级和动态任务添加。
-
三、代码实现(重头戏)
3.1 调度器核心数据结构
#include "stm32f10x.h"
#include <stdint.h>
// 任务状态
typedef enum {
TASK_READY, // 任务就绪
TASK_SUSPENDED // 任务挂起
} TaskState;
// 任务结构体
typedef struct {
void (*task_func)(void); // 任务函数
uint32_t interval; // 执行间隔(ms)
uint32_t last_run; // 上次执行时间
TaskState state; // 任务状态
uint8_t priority; // 任务优先级(0为最高)
} Task;
#define MAX_TASKS 10 // 最大任务数
static Task task_list[MAX_TASKS];
static uint8_t task_count = 0;
// 系统时间戳(通过SysTick更新)
volatile uint32_t system_ticks = 0;
3.2 调度器初始化
// SysTick初始化(1ms中断)
void SysTick_Init(void) {
SysTick_Config(SystemCoreClock / 1000); // 72MHz / 1000 = 72kHz
}
// SysTick中断服务函数
void SysTick_Handler(void) {
system_ticks++;
}
// 添加任务
uint8_t Scheduler_AddTask(
void (*task_func)(void),
uint32_t interval,
uint8_t priority
) {
if (task_count >= MAX_TASKS) return 0;
task_list[task_count] = (Task){
.task_func = task_func,
.interval = interval,
.last_run = system_ticks,
.state = TASK_READY,
.priority = priority
};
task_count++;
return 1;
}
3.3 任务调度逻辑
// 按优先级排序任务(冒泡排序)
static void SortTasks(void) {
for (int i = 0; i < task_count - 1; i++) {
for (int j = 0; j < task_count - i - 1; j++) {
if (task_list[j].priority > task_list[j + 1].priority) {
Task temp = task_list[j];
task_list[j] = task_list[j + 1];
task_list[j + 1] = temp;
}
}
}
}
// 任务调度器主循环
void Scheduler_Run(void) {
while (1) {
SortTasks(); // 按优先级排序任务
for (uint8_t i = 0; i < task_count; i++) {
Task *task = &task_list[i];
if (task->state != TASK_READY) continue;
// 检查是否到达执行时间
if ((system_ticks - task->last_run) >= task->interval) {
task->task_func(); // 执行任务
task->last_run = system_ticks;
}
}
}
}
3.4 举个例子
// LED闪烁任务(优先级1)
void Task_LED_Blink(void) {
static uint8_t led_state = 0;
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (led_state ^= 1) ? Bit_SET : Bit_RESET);
}
// 串口数据发送任务(优先级0)
void Task_UART_Send(void) {
static uint8_t counter = 0;
printf("Counter: %d\n", counter++);
}
int main(void) {
// 硬件初始化
HAL_Init();
SysTick_Init();
// 初始化GPIO和UART...
// 添加任务
Scheduler_AddTask(Task_LED_Blink, 500, 1); // 500ms间隔,优先级1
Scheduler_AddTask(Task_UART_Send, 1000, 0); // 1000ms间隔,优先级0
// 启动调度器
Scheduler_Run();
return 0;
}
四、框架的优化与扩展
4.1 动态任务管理
// 挂起任务
void Scheduler_SuspendTask(void (*task_func)(void)) {
for (uint8_t i = 0; i < task_count; i++) {
if (task_list[i].task_func == task_func) {
task_list[i].state = TASK_SUSPENDED;
break;
}
}
}
// 恢复任务
void Scheduler_ResumeTask(void (*task_func)(void)) {
for (uint8_t i = 0; i < task_count; i++) {
if (task_list[i].task_func == task_func) {
task_list[i].state = TASK_READY;
break;
}
}
}
4.2 事件驱动扩展
// 事件队列
#define MAX_EVENTS 10
typedef struct {
void (*handler)(void); // 事件处理函数
uint32_t timestamp;
} Event;
static Event event_queue[MAX_EVENTS];
static uint8_t event_head = 0, event_tail = 0;
// 事件入队
void Event_Post(void (*handler)(void)) {
if ((event_tail + 1) % MAX_EVENTS != event_head) {
event_queue[event_tail] = (Event){
.handler = handler,
.timestamp = system_ticks
};
event_tail = (event_tail + 1) % MAX_EVENTS;
}
}
// 在调度器中处理事件
void Scheduler_Run(void) {
while (1) {
// 处理事件队列
while (event_head != event_tail) {
Event *event = &event_queue[event_head];
event->handler();
event_head = (event_head + 1) % MAX_EVENTS;
}
// 原有任务调度逻辑...
}
}
五、总结
框架还不够完美,仍需优化,继续改进,期待下一篇哟。
混合调度(时间片轮询 + 事件驱动):
时间片轮询:确保周期性任务(如传感器采集)按时执行。
事件驱动:快速响应外部中断(如按键、通信)。
优先级控制:通过排序实现简单优先级调度。