Skip to content

04 - 编译与烧录

本章目标:掌握 STM32F103C8T6 项目的编译流程和固件烧录方法,能够独立完成从源代码到硬件运行的完整过程。


目录


构建系统介绍

什么是 CMake?

CMake 是一个跨平台的构建系统生成器。它不直接编译代码,而是生成平台特定的构建文件(如 Makefile),然后由构建工具(如 make)执行实际的编译过程。

为什么选择 CMake?

优势说明
跨平台支持 Linux、Windows、macOS
可扩展易于添加新源文件和库
依赖管理自动处理文件依赖关系
增量编译只重新编译修改过的文件
广泛支持主流 IDE 和 CI/CD 系统都支持

项目构建配置

本项目的 CMakeLists.txt 配置概览:

cmake
# 最低版本要求
cmake_minimum_required(VERSION 3.16)

# 设置交叉编译环境
set(CMAKE_SYSTEM_NAME      Generic)
set(CMAKE_SYSTEM_PROCESSOR ARM)

# 指定编译器
set(CMAKE_C_COMPILER       arm-none-eabi-gcc)
set(CMAKE_ASM_COMPILER     arm-none-eabi-gcc)

# 项目定义
project(STM32F1 VERSION 0.0.1 LANGUAGES C ASM)

# 编译选项
add_compile_options(
    -mcpu=cortex-m3      # 目标处理器
    -mthumb              # Thumb 指令集
    -O2                  # 优化级别
    -Wall -Wextra        # 警告选项
    -ffunction-sections  # 函数分段
    -fdata-sections      # 数据分段
    -DUSE_HAL_DRIVER     # 启用 HAL 库
    -DSTM32F103xB        # 芯片型号
)

# 链接选项
target_link_options(STM32F1.elf PRIVATE
    -mcpu=cortex-m3
    -mthumb
    -flto               # 链接时优化
    -T...FLASH.ld        # 链接脚本
    -nostartfiles
    -specs=nano.specs    # 精简 C 库
    -specs=nosys.specs   # 无系统调用
    -Wl,--gc-sections    # 垃圾回收未使用段
    -Wl,-Map=STM32F1.map # 生成映射文件
)

构建目标

目标命令说明
默认目标make编译生成 .elf 和 .bin 文件
烧录make flash通过 OpenOCD 烧录固件
擦除make erase擦除芯片 Flash

编译步骤详解

步骤概览

┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ 创建 build  │ -> │ cmake 配置  │ -> │ make 编译   │ -> │ 输出文件    │
│   目录      │    │   项目      │    │   项目      │    │ .elf/.bin   │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘

步骤 1:进入项目目录

cmd
:: 进入模板项目目录
cd C:\path\to\ST-Forge\project\0_template

步骤 2:创建构建目录

cmd
:: 创建 build 目录(用于存放编译产物)
mkdir build

:: 进入 build 目录
cd build

提示:使用独立的 build 目录可以保持源代码目录整洁,便于清理编译产物。

步骤 3:运行 CMake 配置

重要:Windows 上必须指定 CMake 生成器。始终使用 cmake -G "MinGW Makefiles" ..。省略 -G 会导致 CMake 使用 Visual Studio 生成器,编译会失败。

cmd
:: 配置项目(生成 MinGW Makefile)
cmake -G "MinGW Makefiles" ..

预期输出

-- The C compiler identification is GNU 10.3.1
-- The ASM compiler identification is GNU 10.3.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: arm-none-eabi-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting ASM compile features
-- Detecting ASM compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: C:/path/to/ST-Forge/project/0_template/build

配置过程说明

阶段说明
编译器检测CMake 检测 arm-none-eabi-gcc 编译器
特性探测确定编译器支持的语言特性
配置生成生成 Makefile 和其他构建文件

步骤 4:执行编译

cmd
:: 编译项目
make

提示:如果 make 命令不可用,请尝试 mingw32-make

预期输出

[  5%] Building C object CMakeFiles/STM32F1.elf.dir/main.c.obj
[ 11%] Building C object CMakeFiles/STM32F1.elf.dir/system.c.obj
[ 17%] Building C object CMakeFiles/STM32F1.elf.dir/__/third_party/STM32CubeF1/Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/system_stm32f1xx.c.obj
[ 23%] Building ASM object CMakeFiles/STM32F1.elf.dir/__/third_party/STM32CubeF1/Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/gcc/startup_stm32f103xb.s.obj
[ 29%] Building C object CMakeFiles/STM32F1.elf.dir/__/third_party/STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c.obj
...
[ 88%] Building C object CMakeFiles/STM32F1.elf.dir/__/third_party/STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c.obj
[ 94%] Linking C executable STM32F1.elf
Generating STM32F1.bin
   text    data     bss     dec     hex filename
   1234     108    1024    2366     93e STM32F1.elf
[100%] Built target STM32F1.elf

步骤 5:验证编译结果

cmd
:: 检查生成的文件
dir STM32F1.*

预期输出

2026/04/15  10:00            12,288 STM32F1.bin
2026/04/15  10:00            46,080 STM32F1.elf
2026/04/15  10:00            23,552 STM32F1.map

完整编译流程(一键执行)

cmd
:: 进入项目目录
cd C:\path\to\ST-Forge\project\0_template

:: 清理(可选)
rmdir /s /q build

:: 创建 build 目录,进入,配置并编译
mkdir build && cd build && cmake -G "MinGW Makefiles" .. && make

输出文件说明

编译完成后,build 目录中会生成以下文件:

文件类型详解

文件格式大小用途
STM32F1.elfELF较大调试符号、反汇编、GDB 调试
STM32F1.binBinary较小直接烧录到 Flash 的二进制文件
STM32F1.map文本中等内存映射、符号地址、段分布

ELF 文件 (.elf)

ELF (Executable and Linkable Format) 是包含调试信息的可执行文件格式。

特点

  • 包含完整的符号表和调试信息
  • 包含段信息(代码段、数据段、BSS 段等)
  • 用于 GDB 调试和反汇编分析

查看 ELF 信息

cmd
:: 查看文件头信息
arm-none-eabi-readelf -h STM32F1.elf

:: 查看段信息
arm-none-eabi-readelf -S STM32F1.elf

:: 查看符号表
arm-none-eabi-nm STM32F1.elf

:: 反汇编
arm-none-eabi-objdump -d STM32F1.elf > disassembly.txt

Binary 文件 (.bin)

Binary 文件 是纯二进制格式,直接对应 Flash 中的原始数据。

特点

  • 不包含任何元数据或调试信息
  • 文件大小等于实际占用的 Flash 空间
  • 直接用于烧录到芯片

生成方式(由 CMakeLists.txt 自动执行):

cmd
:: 从 ELF 生成 BIN
arm-none-eabi-objcopy -O binary STM32F1.elf STM32F1.bin

Map 文件 (.map)

Map 文件 记录了链接过程中的内存布局信息。

内容包含

  • 所有符号的内存地址
  • 各段(section)的大小和位置
  • 函数和变量的地址映射
  • 内存使用统计

查看 Map 文件

cmd
:: 在 Map 文件中搜索内存配置信息
findstr "Memory Configuration" STM32F1.map

:: 查看特定符号地址
findstr "main" STM32F1.map

代码大小分析

cmd
:: 查看各段大小
arm-none-eabi-size --format=berkeley STM32F1.elf

输出解读

   text    data     bss     dec     hex filename
   1234     108    1024    2366     93e STM32F1.elf
说明存储位置
text代码段(只读)Flash
data已初始化数据段Flash(初始值)+ RAM(运行时)
bss未初始化数据段RAM
dec十进制总大小-

Flash 占用 = text + data RAM 占用 = data + bss


烧录步骤详解

硬件连接

在烧录之前,请确保 ST-Link 与 Blue Pill 正确连接:

ST-Link V2          Blue Pill (STM32F103C8T6)
┌─────────┐         ┌──────────────┐
│  SWDIO  │ ─────── │ SWDIO (PA13) │
│  SWCLK  │ ─────── │ SWCLK (PA14) │
│  GND    │ ─────── │ GND          │
│  3.3V   │ ─────── │ 3.3V (可选)  │
└─────────┘         └──────────────┘

注意

  • 3.3V 引脚仅在开发板未通过 USB 供电时连接
  • 确保 Blue Pill 的 BOOT0 跳线设置为 0(正常启动模式)

USB 权限

Windows 上无需手动设置 USB 权限。ST-Link 驱动(STSW-LINK009)会处理设备访问。如果烧录失败出现"未找到设备",请通过设备管理器验证驱动安装。

OpenOCD 配置

本项目使用 OpenOCD 作为烧录工具,配置文件位于 CMakeLists.txt:

cmake
# 烧录配置
add_custom_target(flash
    COMMAND openocd
            -f interface/stlink.cfg      # ST-Link 接口配置
            -f target/stm32f1x.cfg        # STM32F1 目标配置
            -c "program STM32F1.bin verify reset exit 0x08000000"
    DEPENDS STM32F1.elf
    COMMENT "Flashing STM32F1.bin via OpenOCD"
)

配置说明

参数说明
-f interface/stlink.cfg使用 ST-Link 调试器
-f target/stm32f1x.cfg目标芯片为 STM32F1 系列
program ... verify烧录并验证
reset exit烧录后复位并退出
0x08000000Flash 起始地址

烧录步骤

方法一:通过设备管理器

  1. Win + X,选择"设备管理器"
  2. 展开"通用串行总线设备"或"STMicroelectronics"
  3. 查找类似 "STLink Virtual COM Port" 或 "STMicroelectronics STLink dongle" 的条目

方法二:通过 PowerShell

powershell
# 检查 ST-Link 设备
Get-PnpDevice | Where-Object { $_.FriendlyName -like "*ST-LINK*" }

预期应看到状态为 OK 的 ST-Link 设备。

步骤 2:执行烧录

cmd
:: 在 build 目录中执行
make flash

预期输出

Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
...
Info : clock speed 1000 kHz
Info : STLINK V2J14S0 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.2V
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f1x.cpu on 3333
...
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000134
** Programming Started **
** Programming Finished **
** Verify Started **
** Verified OK **

步骤 3:验证烧录结果

烧录成功后,开发板会自动复位运行程序。观察开发板上的 LED 或串口输出验证程序是否正常运行。

擦除芯片

如需完全擦除芯片 Flash:

cmd
:: 在 build 目录中执行
make erase

预期输出

Open On-Chip Debugger 0.10.0
...
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
target halted due to debug-request
stm32f1x mass erase complete

手动烧录(不使用 make)

如果需要手动执行烧录命令,注意在 Windows 上 OpenOCD 可能需要使用完整路径:

cmd
:: 手动烧录(如果 openocd 已加入 PATH,可直接使用)
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c "program STM32F1.bin verify reset exit 0x08000000"

:: 如果 openocd 未加入 PATH,使用完整路径
C:\Tools\openocd\bin\openocd.exe -f interface/stlink.cfg -f target/stm32f1x.cfg -c "program STM32F1.bin verify reset exit 0x08000000"

:: 手动擦除
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c "init; halt; stm32f1x mass_erase 0; exit"

常见问题排查

症状

Error: libusb_open() failed with LIBUSB_ERROR_ACCESS
Error: no device found

原因分析

  • ST-Link 驱动未安装
  • 硬件连接问题
  • USB 线缆仅支持充电(不支持数据传输)

解决方案

  1. 检查设备管理器

    Win + X,选择"设备管理器",查看是否出现带黄色感叹号的未知设备或 ST-Link 设备。

  2. 安装 ST-Link 驱动(STSW-LINK009)

    从 ST 官网下载并安装 STSW-LINK009 驱动程序。安装完成后重新插拔 ST-Link。

  3. 使用 Zadig 替代驱动(备选方案)

    如果安装 ST 官方驱动后仍无法识别:

    • 下载 Zadig
    • 在设备列表中找到 ST-Link
    • 将驱动替换为 WinUSBlibusb-win32
    • 替换后重新尝试烧录

问题 2:CMake 生成器错误

症状

CMake Error: Could not create named generator Visual Studio

或生成的项目文件无法用 make 编译。

原因分析

  • 未指定 -G "MinGW Makefiles" 参数,CMake 默认选择了 Visual Studio 生成器

解决方案

cmd
:: 必须始终指定 MinGW 生成器
cmake -G "MinGW Makefiles" ..

:: 如果 build 目录中已有错误的缓存,先清理
rmdir /s /q build
mkdir build && cd build
cmake -G "MinGW Makefiles" ..

问题 3:编译器找不到

症状

CMake Error: CMAKE_C_COMPILER not found
'arm-none-eabi-gcc' is not recognized as an internal or external command

原因分析

  • ARM 工具链未安装
  • 工具链未添加到系统 PATH

解决方案

  1. 检查编译器是否在 PATH 中
cmd
where arm-none-eabi-gcc
  1. 如果未找到,配置 PATH 环境变量

    • 右键"此电脑" > "属性" > "高级系统设置" > "环境变量"
    • 在"系统变量"中找到 Path,点击"编辑"
    • 添加 ARM 工具链的 bin 目录路径,例如:C:\Program Files (x86)\GNU Tools Arm Embedded\10.3-2021.10\bin
    • 点击"确定"保存,重新打开 CMD 窗口使更改生效
  2. 验证安装

cmd
arm-none-eabi-gcc --version

问题 4:链接错误

症状

undefined reference to `main'
cannot find -lc

原因分析

  • 缺少 main 函数
  • 链接脚本配置错误
  • 库文件缺失

解决方案

cmd
:: 1. 确保 main.c 中有 main 函数
findstr "int main" main.c

:: 2. 检查链接脚本是否存在
dir STM32F103C8TX_FLASH.ld

:: 3. 清理并重新编译
rmdir /s /q build
mkdir build && cd build && cmake -G "MinGW Makefiles" .. && make

问题 5:找不到头文件

症状

fatal error: stm32f1xx_hal.h: No such file or directory

原因分析

  • third_party 子模块未初始化
  • 头文件路径配置错误

解决方案

cmd
:: 初始化并更新子模块
cd C:\path\to\ST-Forge
git submodule update --init --recursive

:: 检查 HAL 库是否存在
dir third_party\STM32CubeF1\Drivers\STM32F1xx_HAL_Driver\Inc\

:: 重新配置并编译
cd project\0_template
rmdir /s /q build
mkdir build && cd build && cmake -G "MinGW Makefiles" .. && make

问题 6:烧录失败 - 目标未响应

症状

Error: target not halted
Error: flash programming failed

原因分析

  • 芯片处于低功耗模式
  • SWD 引脚被占用
  • 芯片损坏

解决方案

cmd
:: 1. 尝试连接时复位
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c "init; reset halt; flash write_image erase STM32F1.bin 0x08000000; verify_image STM32F1.bin 0x08000000; reset run; shutdown"
  • 检查 BOOT0 跳线位置,确保 BOOT0 = 0(连接到 GND)
  • 尝试降低 SWD 时钟速度,在 OpenOCD 配置中添加:adapter speed 1000

问题 7:CMake 版本过低

症状

CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
  CMake 3.16 or higher is required.

解决方案

  1. 检查当前 CMake 版本
cmd
cmake --version
  1. 下载最新版本

    前往 cmake.org 下载最新版 CMake 安装程序(.msi 文件),安装时勾选"Add CMake to the system PATH for all users"。


问题 8:make 命令失败

症状

make: *** No targets specified and no makefile found.  Stop.

'make' is not recognized as an internal or external command

原因分析

  • 未在 build 目录中执行
  • 未运行 cmake 配置
  • make 未加入 PATH

解决方案

cmd
:: 1. 检查 make 是否可用
where make

:: 如果 make 找不到,尝试使用 mingw32-make
where mingw32-make

:: 2. 确保在 build 目录中
cd C:\path\to\ST-Forge\project\0_template\build

:: 3. 如果 build 目录不存在,创建并配置
mkdir build && cd build && cmake -G "MinGW Makefiles" ..

:: 4. 然后编译(根据上一步检测结果选择 make 或 mingw32-make)
make
:: 或
mingw32-make

进阶技巧

增量编译

CMake 支持增量编译,只重新编译修改过的文件:

cmd
:: 修改源文件后,只需执行
make

CMake 会自动检测修改并只编译必要的文件。

清理编译产物

cmd
:: 清理所有编译产物
make clean

:: 完全清理(包括 CMake 缓存)
rmdir /s /q build

查看详细编译信息

cmd
:: 显示完整编译命令
make VERBOSE=1

并行编译

cmd
:: 使用多核并行编译(指定线程数)
make -j4

:: 自动使用所有 CPU 核心(CMD)
make -j%NUMBER_OF_PROCESSORS%

查看编译数据库

cmd
:: CMake 生成的 compile_commands.json 可用于 IDE 和静态分析工具
type build\compile_commands.json

调试构建

cmd
:: 查看 CMake 配置信息
cmake --system-information

:: 查看编译器标志
make VERBOSE=1

自定义编译选项

如需修改优化级别或添加自定义标志,编辑 CMakeLists.txt:

cmake
# 修改优化级别
add_compile_options(-O0)  # 无优化(调试用)
add_compile_options(-O1)  # 基本优化
add_compile_options(-O2)  # 标准优化(默认)
add_compile_options(-O3)  # 最大优化
add_compile_options(-Os)  # 优化大小

# 添加调试符号
add_compile_options(-g)

# 添加自定义定义
add_compile_options(-DDEBUG)
add_compile_options(-DMY_DEFINE=123)

编译流程总结

┌─────────────────────────────────────────────────────────────────────────┐
│                        编译流程完整图                                    │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  源文件 (.c, .s)                                                        │
│       │                                                                 │
│       ▼                                                                 │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐                 │
│  │   预处理    │ -> │   编译      │ -> │   汇编      │                 │
│  │  (cpp)      │    │  (gcc)      │    │  (as)       │                 │
│  └─────────────┘    └─────────────┘    └─────────────┘                 │
│       │                  │                   │                         │
│       ▼                  ▼                   ▼                         │
│  预处理文件 (.i)    汇编文件 (.s)     目标文件 (.o)                     │
│                                            │                           │
│                                            ▼                           │
│                                     ┌─────────────┐                    │
│                                     │   链接      │                    │
│                                     │   (ld)      │                    │
│                                     └─────────────┘                    │
│                                            │                           │
│                                            ▼                           │
│                                     ELF 文件 (.elf)                    │
│                                            │                           │
│                                            ▼                           │
│                                     ┌─────────────┐                    │
│                                     │   转换      │                    │
│                                     │  (objcopy)  │                    │
│                                     └─────────────┘                    │
│                                            │                           │
│                                            ▼                           │
│                                     BIN 文件 (.bin)                    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

快速参考

常用命令速查

操作命令
配置项目cd build && cmake -G "MinGW Makefiles" ..
编译项目make
清理编译产物make clean
完全清理rmdir /s /q build
烧录固件make flash
擦除芯片make erase
查看文件大小arm-none-eabi-size STM32F1.elf
查看符号表arm-none-eabi-nm STM32F1.elf
反汇编arm-none-eabi-objdump -d STM32F1.elf

文件位置

文件位置
源代码project\0_template\*.c
构建配置project\0_template\CMakeLists.txt
链接脚本project\0_template\STM32F103C8TX_FLASH.ld
编译产物project\0_template\build\

下一步

恭喜您完成编译与烧录的学习!接下来建议:

  1. 实践调试:继续阅读 05_debugging 学习 GDB 调试
  2. 深入理解:阅读 03_code_walkthrough 理解代码逻辑
  3. 修改代码:尝试修改 main.c 并重新编译烧录
  4. 添加功能:尝试添加新的源文件并更新 CMakeLists.txt

参考资料


上一章代码详解 | 下一章调试方法

Built with VitePress