L0 嵌入式开发引入
本课目标
理解嵌入式程序与 PC 程序的差异,完成工程配置、代码构建、固件下载、单步调试和变量观测。文中的 Blue Pill 与 Stellaris-Nexus 使用相同主控,但板载 LED 引脚不同,迁移时请查阅引脚速查。
一、嵌入式开发工具链与基础认知
1. 前言:从“黑框框”到物理世界
在刚刚结束的 C 语言培训中,大家熟悉的编程环境是 Dev-C++ 或 Visual Studio,运行结果通常显示在黑色的控制台窗口(Console)中。
进入嵌入式开发(Embedded Development),我们的代码将不再运行于拥有 3 GHz CPU 和 8 GB 内存的个人电脑上,而是运行在 STM32 微控制器(MCU) 中。


1.1 通用计算机 vs 嵌入式系统
| 特性 | 通用计算机 (PC) | 嵌入式系统 (以 STM32F103 为例) |
|---|---|---|
| 核心目标 | 数据处理、通用计算 | 控制硬件、实时响应 |
| 主频 | 2GHz ~ 5GHz | 72MHz (Blue Pill) |
| 内存 (RAM) | 8GB ~ 32GB | 20KB |
| 存储 (Flash) | 512GB SSD | 64KB |
| 输入/输出 | 键盘、鼠标、显示器 | GPIO 引脚、传感器、电机 |
| 操作系统 | Windows / Linux / macOS | 无 (裸机) 或 RTOS (实时操作系统) |
核心认知的转变: 以前写代码是为了计算出一个结果(
printf输出); 现在写代码是为了改变引脚的电平状态(控制 LED、电机、读取传感器)。
2. 为什么我们需要全新的工具链?
很多同学会问:“我能不能继续用 Dev-C++ 开发 STM32?”
答案是不能。原因在于架构不同:
Dev-C++ (MinGW/GCC):生成的机器码是给 x86 架构(Intel/AMD CPU)看的。
STM32:使用的是 ARM Cortex-M 架构。
我们需要一套能将 C 代码翻译成 ARM 机器码,并能将其写入芯片、进行调试的工具集合,这就是嵌入式工具链(Toolchain)。
从本学期开始,C.ONE战队电控组已采用目前行业内最现代化、高效的工具组合: STM32CubeMX + CLion (CMake) + Ozone 进行STM32单片机的嵌入式开发与调试
3. 开发环境架构图解
为了让大家理解各个软件的作用,请看下面的工作流图:
3.1 各环节详解
1. 硬件抽象与代码生成:STM32CubeMX
角色:地基搭建者。
作用:STM32 内部有数百个寄存器,手动配置极易出错。CubeMX 提供图形化界面,让我们通过鼠标点击来配置芯片的引脚功能(如:哪个引脚接灯,哪个引脚接电机)。
产出:它会自动生成底层的初始化代码(基于 HAL 库)和工程配置文件(CMakeLists.txt)。
2. 代码编辑与构建系统:CLion + CMake + STM32CubeCLT
角色:集成开发环境(IDE)。
CLion:JetBrains 出品的强大 C/C++ 编辑器。提供智能代码补全、跳转、重构功能,效率远超 Keil 或 Dev-C++。
CMake:构建工具。它负责解析工程结构,告诉编译器哪些文件需要编译。
STM32CubeCLT:ST 官方提供的命令行工具集,包含了交叉编译器(GNU Tools for STM32)。它负责将 C 代码真正翻译成 ARM 芯片能执行的二进制代码。
3. 调试与可视化:Ozone
角色:可视化调试器。
作用:它不仅仅是用来把程序“烧”进芯片,更重要的是调试。
核心功能:
断点调试:让程序停在某一行。
变量观测:实时查看传感器数值。
波形绘制:像示波器一样将变量(如电机速度)绘制成曲线,这在后续调节 PID 控制算法时至关重要。
4. 本节课硬件平台:Blue Pill (STM32F103C8T6)
在切换到战队自研开发板之前,我们首先使用经典的 Blue Pill 核心板进行入门演示。
芯片型号:STM32F103C8T6
内核:ARM Cortex-M3
主频:72 MHz
板载资源:
PC13 引脚:连接了一颗贴片 LED(低电平点亮)。这是我们今天的“Hello World”目标。
Reset 按键:用于复位。
SWD 接口:由 4 根排针组成(3.3V, DIO, CLK, GND),用于连接下载器。
硬件连接示意
请确保手中的 ST-Link/J-Link 下载器与 Blue Pill 按照以下对应关系连接:
| 下载器引脚 | 开发板引脚 | 说明 |
|---|---|---|
| 3.3V / VCC | 3.3V | 禁止接 5V,否则烧毁芯片 |
| SWDIO / DIO | DIO | 数据线 |
| SWCLK / CLK | CLK | 时钟线 |
| GND | G | 地线 |
(留图片:实际接线)
二、Hello World —— 点亮第一盏 LED
在 C 语言课上,我们的“Hello World”是在屏幕上打印一行字。 在嵌入式开发中,我们的“Hello World”是让板载的 LED 灯闪烁起来(Blinky)。这代表着你成功控制了物理世界的一个基本单元。
1. 装修设计:STM32CubeMX 配置
请跟随以下步骤,完成芯片底层驱动的配置。
1.1 新建工程与选型
打开 STM32CubeMX,点击 ACCESS TO MCU SELECTOR。
在左上角搜索栏输入:
STM32F103C8。在右侧列表中选中芯片,点击右上角 Start Project。
收藏常用型号
如果想在之后快速找到STM32F103C8T6,可以将其添加至星标收藏夹中

1.2 核心配置(非常重要)
进入主界面后,你会看到一颗绿色的芯片。请按顺序操作,缺一不可:
第一步:配置调试接口 (System Core)
在左侧列表点击 SYS。
Debug 选项选择 Serial Wire。
注意:此时芯片引脚图上的 PA13 和 PA14 会变绿。__如果不配这一步,芯片烧录一次后可能会锁死,无法再次下载程序!

第二步:配置时钟 (Clock Configuration)
点击顶部的 Clock Configuration 选项卡。
Blue Pill 板载 8MHz 晶振。虽然它可以跑 72MHz,但为了演示简单,我们暂时保持默认(通常是 8MHz 内部时钟)或直接在 HCLK 框中输入
72并回车,允许软件自动寻找解决方案。
1.3 GPIO 引脚配置
我们的目标是控制板载 LED。
查阅原理图:Blue Pill 的板载 LED 连接在 PC13 引脚上。
配置操作:
在右侧芯片图上找到 PC13 引脚。
鼠标左键点击,选择 GPIO_Output(通用输出模式)。
右键点击该引脚,选择 Enter User Label,输入
LED(全大写,方便后续代码调用)。

1.4 工程管理 (Project Manager)
点击顶部的 Project Manager 选项卡,进行最后设置:
Project Name:输入
- 警告:路径和工程名不要包含中文或空格,否则 CMake 和交叉编译工具链可能因路径解析失败而报错。
Toolchain / IDE:选择 CMake (如果版本较老无此选项,可选 STM32CubeIDE,CLion 同样支持)。

Code Generator(左侧菜单):
勾选 Generate peripheral initialization as a pair of '.c/.h' files。
解释:这会把每个外设(如 GPIO、UART)的初始化代码分开存放,保持代码整洁。

点击右上角 GENERATE CODE 按钮。生成完毕后,关闭弹窗。
2. 编写逻辑:CLion 开发环境
2.1 导入工程
打开 CLion。
选择 Open,找到刚才生成的文件夹
RM_Blinky_Demo。点击 Trust Project。
CLion 会自动识别
CMakeLists.txt并配置环境。如果弹出配置预设(Profiles),直接点击 OK 即可。
2.2 工程结构初探
在左侧项目视图中,重点关注以下文件:
Core/Src/main.c:主程序入口,我们 90% 的代码都写在这里。Core/Inc/:头文件存放处。Drivers/:ST 官方提供的 HAL 库(地基),通常不需要动。
2.3 嵌入式开发的“黄金法则”
双击打开 main.c,向下滚动,你会发现大量形如 /* USER CODE BEGIN ... */ 的注释块。
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */⚠️ 极其重要的规则: 你的代码必须且只能写在
BEGIN和END之间!为什么? 如果你需要回到 CubeMX 修改配置(比如增加一个电机)并重新生成代码,CubeMX 会保留标签内的代码,而删除标签外的所有代码。如果你没写在里面,你的心血就会瞬间消失。

2.4 编写控制逻辑
找到 main() 函数中的 while (1) 循环。
我们使用 HAL 库提供的函数来控制引脚。
HAL_GPIO_TogglePin(Port, Pin):翻转引脚电平(高变低,低变高)。HAL_Delay(ms):毫秒级延时。
请在 /* USER CODE BEGIN 3 */ 和 /* USER CODE END 3 */ 之间输入以下代码:
/* USER CODE BEGIN 3 */
// 翻转 LED 引脚的状态
// LED_GPIO_Port 和 LED_Pin 是 CubeMX 根据刚才的 Label 自动生成的宏
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
// 延时 500 毫秒
HAL_Delay(500);
/* USER CODE END 3 */体验 CLion 的强大:当你输入 _HAL_GPIO_T_ 时,IDE 会自动提示补全。
2.5 逻辑流图解
这段代码在芯片内部的运行逻辑如下:
3. 编译构建:生成固件
代码写好了,现在需要把它翻译成芯片能听懂的机器码。
在 CLion 顶部工具栏,找到绿色的锤子图标(Build)。
点击构建。
观察底部的 Build 面板。
成功标志: 如果进度条跑完,显示 Build Finished,且没有红色的 Error,说明编译成功! 你可以在 build/Debug 目录下找到生成的 .elf 文件。这两个文件就是即将装入芯片的“灵魂”。

三、上帝视角 —— 使用 Ozone 进行调试
在编写代码时,我们是创造者;但在调试时,我们需要成为观察者。
传统的调试方法是往串口打印数据(printf),这就像是用听诊器“听”诊。而使用专业的硬件调试器(Ozone + J-Link/ST-Link),则像是把系统送进了 CT 扫描仪。我们可以让时间静止,查看每一根血管(变量)的流动,这就是嵌入式开发的“上帝视角”。
1. 启动与配置 (The Setup)
Ozone 不仅仅是一个烧录软件,它是一个独立的调试环境。
1.1 创建调试工程
启动 Ozone。
在弹出的 New Project Wizard 向导中,按以下步骤配置:
Device(设备):输入
STM32F103C8并选中。Peripherals(外设库):保持默认或留空。
Target Interface(目标接口):选择 SWD。接口速度(Speed)通常默认 4MHz 即可。
Program File(目标文件):点击三个点的浏览按钮,找到刚才 CLion 工程目录下
cmake-build-debug文件夹内的.elf文件(例如RM_Blinky_Demo.elf)。
1.2 认识驾驶舱
配置完成后,你会进入 Ozone 的主界面。这里信息量很大,我们暂时只关注核心区域:
Source 窗口(左上):显示你的 C 语言源码。
Disassembly 窗口(右上):显示汇编代码(机器码)。
Console 窗口(底部):显示调试日志。
2. 烧录与全速运行 (Download & Run)
2.1 一键下载
点击左上角的绿色电源图标 Download & Reset Program。 此时发生了以下物理过程:
电脑通过 USB 将
.elf文件传输给下载器。下载器通过 SWD 接口将程序写入 STM32 的 Flash 存储器。
芯片自动复位,并暂停在
main函数的第一行。
现象:你会发现 Source 窗口中,黄色光标停在了
main()函数的开头。此时芯片处于“冻结”状态,LED 保持熄灭。
2.2 操纵时间
在工具栏中,有几个控制时间的按钮,请务必记住快捷键:
F5 (Download & Reset):从头开始。
F10 (Step Over):单步运行(走一步)。
F9 (Go / Run):全速运行(解除冻结)。
2.3 实验:手动控制 LED
连续按 F10,让黄色光标一行行向下走。
当光标执行过
HAL_GPIO_TogglePin这一行时,抬头看你的开发板。现象:LED 亮了/灭了!
再次按 F10 经过
HAL_Delay,光标会停滞一下(因为在跑延时循环),然后继续。
3. 变量可视化:数据的示波器
为了演示 Ozone 最强大的功能,我们需要在这个死循环里增加一个变化的变量。
3.1 准备工作(回到 CLion)
请回到 CLion,在 while(1) 循环上方定义一个变量,并在循环内让它自增:
/* USER CODE BEGIN 2 */
int counter = 0; // 定义一个计数器
/* USER CODE END 2 */
while (1)
{
/* USER CODE BEGIN 3 */
counter++; // 每次循环自增
if (counter > 100) counter = 0; // 防止无限大(可选)
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(100); // 改快一点,方便看波形
/* USER CODE END 3 */
}修改后,点击 CLion 的锤子重新编译。
回到 Ozone,点击左上角的 Refresh 按钮(或按 F5),它会自动加载新的
.elf文件。
3.2 开启数据图表 (Data Graph)
这是 RoboMaster 比赛调节电机 PID 时最常用的功能。
添加观测点:
在 Source 窗口代码中找到
counter变量。选中
counter,鼠标拖拽到右下角的 Watched Data 窗口中。此时你可以看到
counter的当前数值(Value)。
生成波形:
在 Watched Data 窗口中,右键点击
counter。选择 Toggle Graph。
一个新的图表窗口会出现。
见证奇迹:
按 F9 全速运行程序。
现象:你会看到一条呈锯齿状(0 到 100)或直线向上的波形在实时绘制!

4. 调试原理图解
为了让大家理解 Ozone 是如何做到这一点的,请看下图:
- 解析:调试器就像一个“间谍”,它利用 ARM 内核自带的调试接口(CoreSight),在不打断 CPU 运行的情况下,悄悄潜入内存读取变量的值,并画在屏幕上。
5. 本课总结与作业
5.1 总结
今天我们完成了从 0 到 1 的跨越:
STM32CubeMX:帮我们将复杂的硬件引脚变成了简单的图形化配置。
CLion + CMake:提供了现代化的代码编写和编译环境。
Ozone:给了我们透视芯片内部运行状态的能力。
这套 "配置-编码-观测" 的闭环工作流,是未来开发所有 RoboMaster 机器人的基础。
5.2 课后挑战 (Challenge)
为了巩固今天的知识,请完成以下任务:
SOS 信号灯:修改
main.c中的代码,利用HAL_Delay和HAL_GPIO_TogglePin,让 LED 发出“三短、三长、三短”的摩斯密码闪烁信号。(选做)探索频率:尝试将
HAL_Delay(500)改为HAL_Delay(10),用肉眼观察 LED 的变化。如果改成HAL_Delay(1)呢?LED 是在闪烁还是常亮?为什么?