一、课程导论与开发环境搭建:为什么要在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配置工具、调试功能也够用。咱们这门课就用它。
安装步骤:
- 去ST官网下载STM32CubeIDE(搜索“STM32CubeIDE download”)
- 运行安装程序,一路默认选项即可
- 安装完成后,打开IDE,它会自动检测并安装必要的驱动和工具链
- 连接你的开发板,在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里创建这个模板的步骤:
- 新建C工程,选择“Empty Project”
- 手动添加启动文件(startup_stm32f103xb.s)和链接脚本(STM32F103C8Tx_FLASH.ld)
- 在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 本章知识体系总览
下面这张图,把咱们第一章的核心内容串起来了。你可以把它当作整个课程的地图:
1.6 本章小结
咱们这一章,主要做了三件事:
- 理清了思路:知道了为什么要在STM32上做状态机和任务调度——说白了就是为了让代码更清晰、响应更及时。
- 选好了装备:推荐了STM32F103C8T6作为入门板,STM32CubeIDE作为开发环境。
- 搭好了地基:建立了一个干净的工程模板,后续所有章节的代码都会在这个模板上生长。
记住:好的开始是成功的一半。一个结构清晰的工程模板,能让你在后续开发中少走很多弯路。我见过太多人因为工程结构混乱,最后连bug都找不到在哪。所以,别嫌麻烦,把模板搭好再往下走。