一、课程导论与开发环境搭建:为什么要在STM32上做状态机与任务调度?

各位同学好,我是你们这门课的老周。做嵌入式开发十几年了,从最早的51单片机一路做到现在的多核ARM芯片。今天咱们开始第一讲,聊聊一个很基础但也很关键的问题:为什么要在STM32上搞状态机和任务调度?

说白了,裸机开发不等于“一把梭”。很多新手写代码,上来就是一个大while循环,里面塞满各种延时、标志位判断。刚开始跑得挺好,等需求一多——按键要响应、屏幕要刷新、传感器要采集、串口要收发——整个程序就乱成一锅粥了。

我当年刚入行时也踩过这个坑。一个温控项目,客户要求同时处理加热PID、按键设置、LCD显示和报警输出。我用最原始的“轮询+延时”写法,结果按键按下去要等半秒才有反应,气得客户差点退货。后来我花了三天时间重构,用状态机把逻辑拆开,再用一个简单的调度器管理任务,问题全解决了。

所以这门课,就是要把这些实战经验系统地教给你。

1.1 为什么是状态机 + 任务调度?

先看两个概念:

  • 状态机:把复杂的逻辑拆成若干个“状态”,每个状态只做一件事。比如按键消抖,拆成“等待按下”、“确认按下”、“等待释放”三个状态,清晰又好调。
  • 任务调度:让多个任务“轮流”占用CPU,每个任务跑一小段时间就主动让出来。这样看起来就像多个任务在同时运行。

这两者结合起来,就能在裸机上实现类似RTOS的效果,但又没有RTOS的复杂度和资源开销。

核心观点:状态机解决“逻辑混乱”问题,任务调度解决“响应延迟”问题。两者结合,是裸机开发的黄金搭档。

我个人的习惯是:能用状态机拆的逻辑,绝不用if-else硬堆;能用调度器管理的任务,绝不用延时死等。这个习惯帮我省了无数调试时间。

1.2 开发板选型:我推荐哪几款?

STM32家族太大了,从F0到H7,几十个系列。咱们这门课选板子,主要看三点:

  • 资源够用:至少要有定时器、串口、GPIO、ADC这些外设
  • 调试方便:支持SWD或JTAG,最好板载调试器
  • 性价比高:别为了学个调度器花大几百买旗舰板

我个人最推荐这三款:

型号 内核 Flash RAM 板载调试器 参考价格
STM32F103C8T6 Cortex-M3 64KB 20KB 需外接 约15元
STM32F407VET6 Cortex-M4F 512KB 192KB 需外接 约45元
STM32G431CBT6 Cortex-M4F 128KB 32KB 需外接 约25元

我的建议:如果你是初学者,直接买一块STM32F103C8T6的“蓝色药丸”板,十几块钱,淘宝遍地都是。配合一个ST-Link V2调试器(十几块),总成本不到30元。这门课的所有例子都能在这块板上跑通。

如果你预算宽裕,也可以考虑带板载调试器的开发板,比如Nucleo系列或WeAct Studio的板子,插上USB就能用,省事。

1.3 IDE安装与配置:选哪个?怎么装?

STM32开发的主流IDE有三个:

  • Keil MDK:老牌工具,生态最全,但收费
  • STM32CubeIDE:ST官方免费IDE,基于Eclipse,功能强大
  • IAR EWARM:编译优化好,但也是收费的

我个人推荐STM32CubeIDE。原因很简单:免费、官方维护、集成了CubeMX配置工具、调试功能也够用。咱们这门课就用它。

安装步骤:

  1. 去ST官网下载STM32CubeIDE(搜索“STM32CubeIDE download”)
  2. 运行安装程序,一路默认选项即可
  3. 安装完成后,打开IDE,它会自动检测并安装必要的驱动和工具链
  4. 连接你的开发板,在IDE里创建一个新工程,选择对应的芯片型号

注意:安装过程中如果提示安装Java运行环境,请同意。STM32CubeIDE基于Eclipse,需要JRE支持。另外,Windows用户建议关闭杀毒软件再安装,否则可能被拦截。

1.4 工程模板建立:从零开始搭一个干净的项目

很多教程喜欢用CubeMX自动生成代码,一键生成HAL库工程。但咱们这门课不一样——我要带你手搭工程模板。为什么?

因为自动生成的代码里,有大量你暂时不需要的初始化、中断处理、回调函数。这些代码会干扰你对状态机和调度器的理解。咱们要的是一个干净、可控、每一行代码你都看得懂的工程。

下面是我个人习惯的工程目录结构:

project_root/
├── Core/
│   ├── Inc/          # 头文件
│   │   ├── main.h
│   │   ├── gpio.h
│   │   ├── timer.h
│   │   ├── scheduler.h
│   │   └── state_machine.h
│   └── Src/          # 源文件
│       ├── main.c
│       ├── gpio.c
│       ├── timer.c
│       ├── scheduler.c
│       └── state_machine.c
├── Drivers/
│   ├── CMSIS/        # ARM官方CMSIS库
│   └── STM32F1xx_HAL_Driver/  # HAL库(可选)
├── startup/          # 启动文件
│   └── startup_stm32f103xb.s
└── Makefile          # 编译脚本(如果用命令行)

在STM32CubeIDE里创建这个模板的步骤:

  1. 新建C工程,选择“Empty Project”
  2. 手动添加启动文件(startup_stm32f103xb.s)和链接脚本(STM32F103C8Tx_FLASH.ld)
  3. 在Core/Src下创建main.c,写一个最简单的空循环:
#include "main.h"

int main(void)
{
    // 硬件初始化
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_TIM2_Init();

    // 调度器初始化
    Scheduler_Init();

    // 主循环
    while (1)
    {
        Scheduler_Run();
    }
}

void SystemClock_Config(void)
{
    // 配置系统时钟为72MHz(HSE + PLL)
    // 具体代码后续章节详细讲解
}

void MX_GPIO_Init(void)
{
    // 初始化LED引脚(PC13)
    // 初始化按键引脚(PA0)
}

void MX_TIM2_Init(void)
{
    // 配置TIM2为1ms中断,用于调度器时钟
}

一个小技巧:我习惯在main.c里只放主循环和硬件初始化,把调度器、状态机、外设驱动都拆到单独的文件里。这样后期维护时,找问题特别快。你想想看,如果所有代码都堆在main.c里,几千行代码翻起来多痛苦。

1.5 本章知识体系总览

下面这张图,把咱们第一章的核心内容串起来了。你可以把它当作整个课程的地图:

第一章:课程导论与开发环境搭建 裸机状态机+任务调度 为什么需要状态机? 为什么需要任务调度? 开发板选型 IDE安装与配置 工程目录结构 手搭工程模板 目标:一个干净、可控、可扩展的裸机框架 为后续章节的状态机与调度器实现打好基础

1.6 本章小结

咱们这一章,主要做了三件事:

  • 理清了思路:知道了为什么要在STM32上做状态机和任务调度——说白了就是为了让代码更清晰、响应更及时。
  • 选好了装备:推荐了STM32F103C8T6作为入门板,STM32CubeIDE作为开发环境。
  • 搭好了地基:建立了一个干净的工程模板,后续所有章节的代码都会在这个模板上生长。

记住:好的开始是成功的一半。一个结构清晰的工程模板,能让你在后续开发中少走很多弯路。我见过太多人因为工程结构混乱,最后连bug都找不到在哪。所以,别嫌麻烦,把模板搭好再往下走。