1、RTOS基础概念:什么是实时操作系统、前后台系统与RTOS对比、任务与调度器
大家好,欢迎来到这门课的第一讲。
说实话,每次带新人做飞控项目,我第一件事不是让他们看代码,而是先问一个问题:“你觉得你的单片机在跑什么?” 很多人会愣住,然后说“跑主循环啊”。嗯,这就是我们今天要聊的起点。
1.1 什么是实时操作系统?
实时操作系统,英文叫 RTOS(Real-Time Operating System)。
它和我们电脑上的 Windows、Linux 不太一样。Windows 你点一下鼠标,它过几毫秒响应,你感觉不到。但在飞控里,延迟 1 毫秒,飞机可能就翻了。
所以,实时操作系统的核心就两个字:确定。
什么叫确定?就是:这个任务必须在规定时间内完成,晚一丁点都不行。
实时 ≠ 快,实时 = 可预测
我见过很多新手把“实时”理解为“跑得快”,其实不对。一个系统哪怕跑得慢,但只要每次响应时间都在 1ms 以内,它就是实时的。反过来,一个系统平均 0.1ms 响应,但偶尔抽风变成 10ms,那它就不实时。
RTOS 通常分为两类:
- 硬实时:错过截止时间 = 系统崩溃。飞控就是典型的硬实时系统。
- 软实时:偶尔错过一下,还能接受。比如视频播放,卡一帧问题不大。
我在做某款四旋翼飞控时,遇到过一个问题:电机控制任务偶尔被中断卡住,导致一个桨叶响应慢了 3ms,飞机直接侧翻。那次之后,我对硬实时的敬畏心就刻进骨子里了。
1.2 前后台系统与 RTOS 对比
先说说什么是“前后台系统”。
说白了,就是很多单片机入门时写的那种代码:
void main(void)
{
while(1)
{
// 前台:处理传感器数据
read_sensor();
// 后台:处理控制算法
control_loop();
// 再处理通信
send_telemetry();
// 嗯,还有显示
update_led();
}
}
这就是典型的前后台系统。大循环是“后台”,中断服务函数是“前台”。
这种结构有什么问题?我举个例子你就明白了。
假设你的飞控要同时做三件事:
- 每 1ms 读取一次陀螺仪数据
- 每 10ms 执行一次姿态解算
- 每 100ms 发送一次遥测数据
在前后台系统里,如果姿态解算花了 8ms,那陀螺仪读取就被硬生生推迟了 8ms。这 8ms 里,飞机可能已经偏了 2 度。
而 RTOS 是怎么做的?
// 任务1:陀螺仪读取,每1ms执行一次
void task_gyro(void *arg)
{
while(1)
{
read_gyro();
vTaskDelay(pdMS_TO_TICKS(1));
}
}
// 任务2:姿态解算,每10ms执行一次
void task_attitude(void *arg)
{
while(1)
{
calculate_attitude();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
// 任务3:遥测发送,每100ms执行一次
void task_telemetry(void *arg)
{
while(1)
{
send_data();
vTaskDelay(pdMS_TO_TICKS(100));
}
}
你看,每个任务各跑各的,互不干扰。调度器会保证:该读陀螺仪的时候,一定让你读。
| 对比项 | 前后台系统 | RTOS |
|---|---|---|
| 任务响应 | 依赖主循环周期 | 可独立调度 |
| 代码复杂度 | 低,适合简单应用 | 稍高,但结构清晰 |
| 实时性 | 差,高优先级任务会被阻塞 | 好,可抢占 |
| 可维护性 | 差,改一处可能影响全局 | 好,任务独立 |
| 典型应用 | LED 闪烁、按键扫描 | 飞控、机器人、工业控制 |
我的建议:如果你只是做个玩具车,前后台完全够用。但如果你要做飞控,别犹豫,直接上 RTOS。我曾经见过一个团队用前后台做飞控,最后代码里塞满了各种标志位和状态机,维护起来简直噩梦。
1.3 任务与调度器
好,现在聊聊 RTOS 里最核心的两个概念:任务 和 调度器。
任务是什么?
任务,说白了就是一个无限循环的函数。每个任务有自己的栈空间、优先级和状态。
在 RTOS 里,任务有四种状态:
- 运行态:正在被 CPU 执行
- 就绪态:随时可以运行,但 CPU 正在忙别的
- 阻塞态:在等某个事件(比如延时、信号量)
- 挂起态:被暂停了,需要别人唤醒
你想想看,一个飞控里通常有多少个任务?
我做过的一个典型四旋翼飞控,大概有 8-10 个任务:
- 传感器读取任务(最高优先级)
- 姿态解算任务
- 位置控制任务
- 电机控制任务
- 遥控器接收任务
- 遥测发送任务
- 日志记录任务(最低优先级)
调度器又是什么?
调度器就是 RTOS 的大脑。它决定:下一个该谁跑?跑多久?
最常见的调度策略是基于优先级的抢占式调度。
什么意思?
就是:高优先级的任务随时可以打断低优先级的任务。比如传感器读取任务优先级最高,它一就绪,调度器立刻把 CPU 给它,不管其他任务跑没跑完。
注意:优先级不是越高越好。我曾经犯过一个错误:把日志任务优先级设得和传感器任务一样高。结果日志任务偶尔抢占了传感器任务,导致传感器数据丢失,飞机姿态解算直接飘了。那次试飞差点炸机。
所以,优先级设计要谨慎。一般来说,时间敏感的任务(传感器、控制)给高优先级,非关键任务(日志、显示)给低优先级。
调度器的工作流程大致是这样的:
1. 当前任务运行中
2. 发生中断或任务主动让出CPU
3. 调度器查看就绪队列
4. 选择优先级最高的任务
5. 切换上下文,开始执行新任务
6. 回到步骤1
这个上下文切换,就是 RTOS 的看家本领。它会把当前任务的寄存器、栈指针等信息保存起来,然后恢复下一个任务的信息。这个过程通常只需要几十微秒。
嗯,这里要注意:上下文切换的频率不是越高越好。切换太频繁,CPU 时间都花在切换上了,实际干活的时间就少了。一般飞控里,切换频率在 1kHz 左右比较合适。
小结
今天的内容就这些。我们来捋一捋:
- 实时操作系统:核心是可预测,不是快
- 前后台 vs RTOS:飞控必须用 RTOS,别犹豫
- 任务:就是一个个独立的功能模块
- 调度器:决定谁先跑,谁后跑
下一讲,我们会深入 FreeRTOS 的任务创建和管理。到时候我会带大家手写一个简单的任务调度示例,看看 RTOS 到底是怎么“抢”CPU 的。
好,今天就到这里。有什么问题,欢迎在评论区留言。