Skip to content

L0 嵌入式开发引入

本课目标

理解嵌入式程序与 PC 程序的差异,完成工程配置、代码构建、固件下载、单步调试和变量观测。文中的 Blue Pill 与 Stellaris-Nexus 使用相同主控,但板载 LED 引脚不同,迁移时请查阅引脚速查

一、嵌入式开发工具链与基础认知

1. 前言:从“黑框框”到物理世界

在刚刚结束的 C 语言培训中,大家熟悉的编程环境是 Dev-C++ 或 Visual Studio,运行结果通常显示在黑色的控制台窗口(Console)中。

进入嵌入式开发(Embedded Development),我们的代码将不再运行于拥有 3 GHz CPU 和 8 GB 内存的个人电脑上,而是运行在 STM32 微控制器(MCU) 中。

{89825DB2-AE36-4F49-83C6-3DA907518FF8}.png{3CE33D53-6D49-4AC6-BD12-53A9519D49DE}.png

1.1 通用计算机 vs 嵌入式系统

特性通用计算机 (PC)嵌入式系统 (以 STM32F103 为例)
核心目标数据处理、通用计算控制硬件、实时响应
主频2GHz ~ 5GHz72MHz (Blue Pill)
内存 (RAM)8GB ~ 32GB20KB
存储 (Flash)512GB SSD64KB
输入/输出键盘、鼠标、显示器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 / VCC3.3V禁止接 5V,否则烧毁芯片
SWDIO / DIODIO数据线
SWCLK / CLKCLK时钟线
GNDG地线

(留图片:实际接线)


二、Hello World —— 点亮第一盏 LED

在 C 语言课上,我们的“Hello World”是在屏幕上打印一行字。 在嵌入式开发中,我们的“Hello World”是让板载的 LED 灯闪烁起来(Blinky)。这代表着你成功控制了物理世界的一个基本单元。


1. 装修设计:STM32CubeMX 配置

请跟随以下步骤,完成芯片底层驱动的配置。

1.1 新建工程与选型

  1. 打开 STM32CubeMX,点击 ACCESS TO MCU SELECTOR

  2. 在左上角搜索栏输入:STM32F103C8

  3. 在右侧列表中选中芯片,点击右上角 Start Project

收藏常用型号

如果想在之后快速找到STM32F103C8T6,可以将其添加至星标收藏夹中

44c5b67f-14ce-4a84-a635-0cfe10a60216.png

1.2 核心配置(非常重要)

进入主界面后,你会看到一颗绿色的芯片。请按顺序操作,缺一不可

第一步:配置调试接口 (System Core)

  • 在左侧列表点击 SYS

  • Debug 选项选择 Serial Wire

  • 注意:此时芯片引脚图上的 PA13 和 PA14 会变绿。__如果不配这一步,芯片烧录一次后可能会锁死,无法再次下载程序!

137b493e-8549-479e-aa7f-d2a8ba77b836.png

第二步:配置时钟 (Clock Configuration)

  • 点击顶部的 Clock Configuration 选项卡。

  • Blue Pill 板载 8MHz 晶振。虽然它可以跑 72MHz,但为了演示简单,我们暂时保持默认(通常是 8MHz 内部时钟)或直接在 HCLK 框中输入 72 并回车,允许软件自动寻找解决方案。

1.3 GPIO 引脚配置

我们的目标是控制板载 LED。

  • 查阅原理图:Blue Pill 的板载 LED 连接在 PC13 引脚上。

  • 配置操作

    1. 在右侧芯片图上找到 PC13 引脚。

    2. 鼠标左键点击,选择 GPIO_Output(通用输出模式)。

    3. 右键点击该引脚,选择 Enter User Label,输入 LED(全大写,方便后续代码调用)。

{4CE2BF02-AC99-4A69-9A59-4EBCE69E93DC}.png

1.4 工程管理 (Project Manager)

点击顶部的 Project Manager 选项卡,进行最后设置:

  1. Project Name:输入

    • 警告:路径和工程名不要包含中文或空格,否则 CMake 和交叉编译工具链可能因路径解析失败而报错。
  2. Toolchain / IDE:选择 CMake (如果版本较老无此选项,可选 STM32CubeIDE,CLion 同样支持)。

    d7ac8aee-2630-4d62-8000-0e6aa6e65660.png

  3. Code Generator(左侧菜单):

    • 勾选 Generate peripheral initialization as a pair of '.c/.h' files

    • 解释:这会把每个外设(如 GPIO、UART)的初始化代码分开存放,保持代码整洁。

    42fa5595-6177-4e18-b271-605eb38ef65f.png

  4. 点击右上角 GENERATE CODE 按钮。生成完毕后,关闭弹窗。


2. 编写逻辑:CLion 开发环境

2.1 导入工程

  1. 打开 CLion

  2. 选择 Open,找到刚才生成的文件夹 RM_Blinky_Demo

  3. 点击 Trust Project

  4. CLion 会自动识别 CMakeLists.txt 并配置环境。如果弹出配置预设(Profiles),直接点击 OK 即可。

2.2 工程结构初探

在左侧项目视图中,重点关注以下文件:

  • Core/Src/main.c:主程序入口,我们 90% 的代码都写在这里。

  • Core/Inc/:头文件存放处。

  • Drivers/:ST 官方提供的 HAL 库(地基),通常不需要动。

2.3 嵌入式开发的“黄金法则”

双击打开 main.c,向下滚动,你会发现大量形如 /* USER CODE BEGIN ... */ 的注释块。

c
/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

⚠️ 极其重要的规则: 你的代码必须且只能写在 BEGIN 和 END 之间!为什么? 如果你需要回到 CubeMX 修改配置(比如增加一个电机)并重新生成代码,CubeMX 会保留标签内的代码,而删除标签外的所有代码。如果你没写在里面,你的心血就会瞬间消失。

ec2f6ebf-200d-4ad7-903c-5e4c0d52f8e3.png

2.4 编写控制逻辑

找到 main() 函数中的 while (1) 循环。

我们使用 HAL 库提供的函数来控制引脚。

  • HAL_GPIO_TogglePin(Port, Pin):翻转引脚电平(高变低,低变高)。

  • HAL_Delay(ms):毫秒级延时。

请在 /* USER CODE BEGIN 3 */ 和 /* USER CODE END 3 */ 之间输入以下代码:

c
  /* 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. 编译构建:生成固件

代码写好了,现在需要把它翻译成芯片能听懂的机器码。

  1. 在 CLion 顶部工具栏,找到绿色的锤子图标(Build)。

  2. 点击构建。

  3. 观察底部的 Build 面板。

成功标志: 如果进度条跑完,显示 Build Finished,且没有红色的 Error,说明编译成功! 你可以在 build/Debug 目录下找到生成的 .elf 文件。这两个文件就是即将装入芯片的“灵魂”。

cf28ee1a-be5d-449d-90cb-daf0feac0e67.png


三、上帝视角 —— 使用 Ozone 进行调试

在编写代码时,我们是创造者;但在调试时,我们需要成为观察者

传统的调试方法是往串口打印数据(printf),这就像是用听诊器“听”诊。而使用专业的硬件调试器(Ozone + J-Link/ST-Link),则像是把系统送进了 CT 扫描仪。我们可以让时间静止,查看每一根血管(变量)的流动,这就是嵌入式开发的“上帝视角”。


1. 启动与配置 (The Setup)

Ozone 不仅仅是一个烧录软件,它是一个独立的调试环境。

1.1 创建调试工程

  1. 启动 Ozone

  2. 在弹出的 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。 此时发生了以下物理过程:

  1. 电脑通过 USB 将 .elf 文件传输给下载器。

  2. 下载器通过 SWD 接口将程序写入 STM32 的 Flash 存储器。

  3. 芯片自动复位,并暂停在 main 函数的第一行。

现象:你会发现 Source 窗口中,黄色光标停在了 main() 函数的开头。此时芯片处于“冻结”状态,LED 保持熄灭。

2.2 操纵时间

在工具栏中,有几个控制时间的按钮,请务必记住快捷键:

  • F5 (Download & Reset):从头开始。

  • F10 (Step Over):单步运行(走一步)。

  • F9 (Go / Run):全速运行(解除冻结)。

2.3 实验:手动控制 LED

  1. 连续按 F10,让黄色光标一行行向下走。

  2. 当光标执行过 HAL_GPIO_TogglePin 这一行时,抬头看你的开发板

  3. 现象:LED 亮了/灭了!

  4. 再次按 F10 经过 HAL_Delay,光标会停滞一下(因为在跑延时循环),然后继续。


3. 变量可视化:数据的示波器

为了演示 Ozone 最强大的功能,我们需要在这个死循环里增加一个变化的变量。

3.1 准备工作(回到 CLion)

请回到 CLion,在 while(1) 循环上方定义一个变量,并在循环内让它自增:

c
  /* 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 时最常用的功能。

  1. 添加观测点

    • 在 Source 窗口代码中找到 counter 变量。

    • 选中 counter,鼠标拖拽到右下角的 Watched Data 窗口中。

    • 此时你可以看到 counter 的当前数值(Value)。

  2. 生成波形

    • 在 Watched Data 窗口中,右键点击 counter

    • 选择 Toggle Graph

    • 一个新的图表窗口会出现。

  3. 见证奇迹

    • 按 F9 全速运行程序。

    • 现象:你会看到一条呈锯齿状(0 到 100)或直线向上的波形在实时绘制!

afea1cfb7c330be3ef589c2a58d9588b.png


4. 调试原理图解

为了让大家理解 Ozone 是如何做到这一点的,请看下图:

  • 解析:调试器就像一个“间谍”,它利用 ARM 内核自带的调试接口(CoreSight),在不打断 CPU 运行的情况下,悄悄潜入内存读取变量的值,并画在屏幕上。

5. 本课总结与作业

5.1 总结

今天我们完成了从 0 到 1 的跨越:

  1. STM32CubeMX:帮我们将复杂的硬件引脚变成了简单的图形化配置。

  2. CLion + CMake:提供了现代化的代码编写和编译环境。

  3. Ozone:给了我们透视芯片内部运行状态的能力。

这套 "配置-编码-观测" 的闭环工作流,是未来开发所有 RoboMaster 机器人的基础。

5.2 课后挑战 (Challenge)

为了巩固今天的知识,请完成以下任务:

  1. SOS 信号灯:修改 main.c 中的代码,利用 HAL_Delay 和 HAL_GPIO_TogglePin,让 LED 发出“三短、三长、三短”的摩斯密码闪烁信号。

  2. (选做)探索频率:尝试将 HAL_Delay(500) 改为 HAL_Delay(10),用肉眼观察 LED 的变化。如果改成 HAL_Delay(1) 呢?LED 是在闪烁还是常亮?为什么?

最近更新