1、课程导论与目标:为什么从裸机迁移到RTOS?

各位工程师朋友,大家好。我是你们这门课的老朋友。

先问大家一个问题:你手头的STM32项目,是不是已经变得有点「臃肿」了?

我刚入行那会儿,做的第一个产品是个智能家居网关。功能简单:读几个传感器,控制几个继电器,再通过串口跟Wi-Fi模块聊聊天。那时候我用的就是裸机——一个大循环,轮询所有外设。项目顺利交付,我也觉得「嗯,嵌入式嘛,不就这点事」。

但后来项目越来越复杂。客户要求加个LCD菜单,要能翻页、能设置参数;还要支持OTA升级,得在后台偷偷下载固件;再后来,要同时处理多个传感器的数据融合……那个大循环开始变得力不从心。按键响应慢了,串口丢包了,LCD刷新的时候传感器数据采集被卡住了。

说白了,裸机就像一个人同时干五份活——你想想看,能不出错吗?

裸机开发的「天花板」在哪里?

裸机开发,本质上是一个无限循环。你在这个循环里依次处理各个任务:

int main(void) {
    // 初始化
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_I2C1_Init();

    while (1) {
        Read_Sensor();      // 读传感器
        Update_Display();   // 刷新屏幕
        Check_Button();     // 检测按键
        Process_Serial();   // 处理串口数据
        // ... 更多任务
    }
}

这段代码看起来挺清晰,对吧?但实际跑起来,问题就来了:

  • 实时性差:如果Update_Display()执行时间太长,按键检测就会被延迟。用户按了键,可能要等几百毫秒才有反应。
  • 耦合度高:所有任务挤在一个循环里,改一个函数可能影响全局。我在项目中遇到过,为了给LCD驱动加个动画效果,结果串口接收缓冲区溢出了。
  • 扩展性差:想加个新功能?你得在循环里找个合适的位置插进去,还得保证不破坏原有的时序。
  • 功耗难控:裸机要么全速跑,要么进休眠。想做到「没事就睡,有事才醒」?你得自己写状态机,而且写出来往往漏洞百出。

我曾经踩过的坑:有个项目需要同时处理两个UART的数据,一个波特率115200,一个9600。裸机下我用了中断+标志位的方式,结果高波特率的UART频繁中断,导致低波特率的接收数据被覆盖。查了两天才发现是中断优先级没配好——但更根本的问题是,裸机下任务调度全靠程序员手动管理,出错的概率太高了。

RTOS能带来什么?

RTOS,全称是Real-Time Operating System,实时操作系统。它不是一个「大而全」的操作系统,而是一个轻量级的任务调度器。

它的核心思想很简单:把一个大循环拆成多个独立的小任务,让系统来决定什么时候该执行哪个任务。

我习惯用FreeRTOS,因为它免费、轻量、资料多。咱们这门课也以FreeRTOS为例。

同样的功能,用RTOS写出来是这样的:

// 任务1:读取传感器
void Task_Sensor(void *pvParameters) {
    while (1) {
        Read_Sensor();
        vTaskDelay(pdMS_TO_TICKS(100));  // 每100ms执行一次
    }
}

// 任务2:刷新显示
void Task_Display(void *pvParameters) {
    while (1) {
        Update_Display();
        vTaskDelay(pdMS_TO_TICKS(50));   // 每50ms刷新一次
    }
}

// 任务3:检测按键
void Task_Button(void *pvParameters) {
    while (1) {
        Check_Button();
        vTaskDelay(pdMS_TO_TICKS(20));   // 每20ms检测一次
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    // ... 外设初始化

    xTaskCreate(Task_Sensor, "Sensor", 128, NULL, 1, NULL);
    xTaskCreate(Task_Display, "Display", 256, NULL, 2, NULL);
    xTaskCreate(Task_Button, "Button", 128, NULL, 3, NULL);

    vTaskStartScheduler();  // 启动调度器
    while (1);  // 不会执行到这里
}

你看,每个任务都是独立的。按键检测不会被显示刷新阻塞,传感器读取有固定的周期。系统会自动切换任务,哪个任务该执行了,就切换到哪个。

这就是RTOS最大的价值:把「谁先跑、跑多久」的问题交给系统,你只需要关心「每个任务该做什么」。

裸机 vs RTOS:一张表说清楚

对比维度 裸机开发 RTOS开发
任务调度 手动轮询或中断 基于优先级的抢占式调度
实时性 依赖代码结构,难以保证 可配置任务优先级,响应时间可预测
代码耦合 高,所有代码在一个循环里 低,每个任务独立
扩展性 差,加功能容易破坏原有逻辑 好,直接添加新任务即可
资源开销 极低,几乎无额外开销 需要RAM/ROM存放内核和任务栈
调试难度 低,单线程逻辑简单 中等,需处理任务同步、优先级反转等问题
适用场景 功能简单、任务少的项目 多任务、实时性要求高的项目

我的建议:如果你的项目只有两三个任务,而且对实时性要求不高,裸机完全够用。但一旦任务超过5个,或者有严格的时序要求,我建议你直接上RTOS。别等到代码写到一半再改,那代价太大了。

课程目标:从零到一,完整移植

这门课的目标很明确:带你亲手把FreeRTOS移植到STM32上,从裸机一步步过渡到RTOS。

具体来说,学完这门课,你能做到:

  • 理解RTOS的核心概念:任务、调度、同步、通信
  • 掌握FreeRTOS的移植步骤:从源码下载到工程配置
  • 学会创建和管理多个任务
  • 会用信号量、队列、事件组等机制解决任务间通信问题
  • 能分析并解决RTOS下的常见问题:优先级反转、死锁、栈溢出

我不会给你讲一堆空洞的理论。每个知识点我都会结合我实际项目中的案例来讲。比如:

  • 「这个bug我调了三天,最后发现是任务栈开小了」
  • 「当时客户要求响应时间小于10ms,我用了中断+信号量的方式才搞定」

这些经验,才是这门课最值钱的部分。

硬件平台:为什么选STM32?

咱们这门课以STM32F103(也就是大家常说的「蓝板子」或「C8T6」)为主讲平台。原因有三:

  1. 普及率高:STM32是国内嵌入式工程师用得最多的MCU之一,资料多、社区活跃。你遇到问题,大概率有人已经踩过坑了。
  2. 资源适中:F103有20KB的RAM、64KB的Flash,跑FreeRTOS绰绰有余。我见过有人在只有4KB RAM的芯片上跑RTOS,那才是真正的「螺蛳壳里做道场」。
  3. 工具链成熟:Keil、IAR、STM32CubeIDE都能用,咱们用CubeMX生成初始化代码,省去很多底层配置的麻烦。

当然,如果你用的是STM32F4、F7或者G系列,移植方法大同小异。我会在课程中标注不同系列的差异点。

硬件准备清单:

  • 一块STM32F103开发板(推荐正点原子或野火的Mini板)
  • 一个J-Link或ST-Link调试器
  • 若干杜邦线和LED(用于实验验证)
  • 一台装了Keil MDK或STM32CubeIDE的电脑

知识体系:一张图看懂课程脉络

下面这张图,是我梳理的这门课的知识体系。你可以把它当作一张「地图」,学完一章就回来看看,知道自己走到了哪里。

课程知识体系总览 裸机开发回顾 RTOS核心概念 FreeRTOS简介 移植环境搭建 内核配置与裁剪 任务创建与管理 任务同步与通信 中断与临界区 内存管理 实战:多任务应用 调试与性能优化 常见问题与避坑 课程总结与项目复盘 说明:课程从裸机基础出发,逐步深入到RTOS核心机制,最后通过实战项目巩固所学。每个模块都配有代码示例和调试技巧。 建议按顺序学习,但如果你对某部分已经熟悉,可以直接跳到对应章节。

写在开始之前

这门课不是「速成班」。我不会告诉你「三天学会RTOS」——那是不可能的。但我可以保证,只要你跟着我一步步做,30章之后,你一定能独立完成一个基于RTOS的嵌入式项目。

嗯,准备好了吗?那咱们就开始吧。