01 环境搭建(HAL 库)
STM32F103C8T6 | CubeMX → Keil MDK → DAP-Link
开发流程总览
flowchart LR
A["CubeMX<br/>图形化配置"] --> B["生成代码<br/>MDK-ARM Project"]
B --> C["Keil MDK<br/>编辑/编译"]
C --> D["DAP-Link<br/>烧录"]
D --> E["调试<br/>(Debug/串口)"]
E -.->|修改代码| C
E -.->|改配置| A
详细工作原理
ARM Cortex-M3 上电启动流程
STM32F103 采用 Cortex-M3 内核,上电复位后固件执行顺序如下:
Reset Vector (0x0000_0004) → SystemInit() → __main() → main()
- 复位后内核从 0x0000_0000 读取栈顶指针(MSP),从 0x0000_0004 读取复位向量地址并跳转。
- SystemInit():由 CMSIS 库提供,配置 Flash 预取缓冲区、设置系统时钟(HSI → PLL → HCLK/PCLK1/PCLK2),初始化 VTOR(向量表偏移寄存器)。
- __main():C 运行时初始化(非 main 函数),包括 ZI 区清零、RW 区复制、堆栈初始化、调用
__rt_entry 进入 C 环境后调用 main()。
- main():用户代码入口。
启动文件 startup_stm32f103xb.s 关键内容:
- 栈(Stack)设置:定义
Stack_Size(默认 0x400 = 1KB),在 RAM 末尾分配栈空间,初始化 MSP。
- 堆(Heap)设置:定义
Heap_Size(默认 0x200 = 512B),供 malloc 等动态分配使用。
- 中断/异常向量表:从 0x0000_0000 开始依次存放堆栈指针、Reset_Handler、NMI_Handler、HardFault_Handler 等。每个向量占 4 字节(函数地址)。
- Reset_Handler:调用
SystemInit → __main,最终跳转到 main。
SWD 必须使能的原因
STM32F103 复位后,PA13(SWDIO)/ PA14(SWCLK)默认功能为 JTAG/SWD 调试接口。若 CubeMX 中 SYS → Debug 不设为 Serial Wire,这两引脚会被初始化为普通 GPIO,之后调试器无法连接芯片,俗称"锁死"。
PB3(JTDO)/ PB4(NJTRST)同样复用为 SWO 等功能。因此所有使用 SWD 调试的项目必须在 CubeMX 中显式开启 Serial Wire(SWD)。
3 种启动模式(BOOT0 / BOOT1)

| BOOT0 |
BOOT1 |
启动区域 |
地址映射 |
典型用途 |
| 0 |
X |
Flash 主存 |
0x0800_0000 ~ 0x0801_FFFF(128KB) |
正常运行 |
| 1 |
0 |
System Memory |
0x1FFF_F000 ~ 0x1FFF_F7FF(内置 Bootloader) |
串口 ISP 下载 |
| 1 |
1 |
SRAM |
0x2000_0000 ~ 0x2000_4FFF(20KB) |
调试/测试 |
- Flash 启动(默认):代码直接从 0x0800_0000 执行,但 Cortex-M3 复位后从 0x0000_0000 取向量——因此 STM32 通过 Flash 地址别名映射将 0x0800_0000 映射到 0x0000_0000。
- System Memory 启动:进入内置 Bootloader(USART1/USART2/USB DFU),可用串口线通过 ISP 协议擦除/下载 Flash。芯片"锁死"时的救命通道。
- SRAM 启动:用于调试时将程序直接加载到 RAM 运行,不占用 Flash 写入寿命。
工具链角色
| 工具 |
在流程中的角色 |
| STM32CubeMX |
图形化配置引脚、时钟、外设参数,生成 HAL 初始化 C 代码 |
| Keil MDK |
编辑代码、交叉编译(ARMCC/CLANG)、烧录、调试 |
| DAP-Link / ST-Link |
SWD 协议调试器,物理连接 PC USB ↔ 开发板 SWDIO/SWCLK |
| 串口助手 |
通过 USB-TTL 查看 MCU 串口输出的调试信息 |
CubeMX 生成基础工程
- 打开 CubeMX → New Project
- MCU Selector → 搜索并选中 STM32F103C8T6
- System Core → SYS → Debug → 设为 Serial Wire (SWD)
- ⚠️ 这一步必须做!否则烧录一次后 SWD 引脚被 GPIO 占用,芯片永久锁死,只能用串口 ISP 擦除
- System Core → RCC → HSE → 设为 Crystal/Ceramic Resonator
- 时钟树暂时保留默认(HSI 8MHz),第 08 节专门讲 72MHz 配置
- Project Manager → Project 标签页:
- Project Name:
LED_Test
- Toolchain / IDE:MDK-ARM (Keil)
- Minimum Firmware Version:不影响功能,选最新
- 点 GENERATE CODE → 选择目录 → 生成后选择 Open Project 直接启动 Keil
ARMCC 编译器说明
CubeMX 生成 Keil 工程时默认使用 ARMCC V6(即 ARMCLANG)。如果你需要使用老版 V5 编译器(某些旧驱动库只支持 V5):
- 将 ARMCC V5.06 目录复制到 Keil 的安装目录(ARMCC 需要单独从 Keil 官网下载)
- Keil 中 Project → Manage → Project Items
- 在 Folder 视图顶部的 ARM Compiler 下拉框中新增 V5.06 路径
- 选中新加的 V5.06 → 重新编译
- ⚠️ 若首次配置编译报错,再走一遍上述流程即可通过(常见 bug,原因未知)
如果你的工程使用 CubeMX 默认的 ARMCLANG(V6),不需要任何额外配置。
烧录配置(每次新建工程需配置一次)
- Options for Target (Alt+F7) → Debug 标签
- 右侧 Use 下拉选择 CMSIS-DAP Debugger
- 点击 Settings → 如果 DAP 已正确连接,SWD 设备列表里应该能识别到
- 切换到 Flash Download 标签 → 勾选 Reset and Run(烧录后自动复位运行)
- 烧录:Flash → Download (F8)
如果 Settings 里没识别到 DAP:检查 SWDIO/SWCLK/GND 三根线是否正确连接,重新插拔 USB
调试功能(Keil Debugger)
- Options for Target → Debug → 确认已选 CMSIS-DAP
- Debug 标签 → 在右下角关闭编译器优化:
-O0(否则变量值和单步行为不准确)
- Start/Stop Debug Session (Ctrl+F5) 进入调试模式
- 添加 Watch 窗口:View → Watch → Watch1
- 在 Watch 窗口输入变量名即可实时查看和修改变量值
- 常用调试操作:全速运行 (F5)、单步进入 (F11)、单步跳过 (F10)、跳出 (Ctrl+F11)、下断点 (单击行号左侧)
常见问题
| 问题 |
原因 |
解决方法 |
| 烧录后无法识别 DAP |
接线松动或芯片进入低功耗 |
重新插拔 USB,检查 SWD 接线 |
| 芯片锁死 "No target connected" |
SWD 引脚被 GPIO 占用 |
按住复位键 → 点烧录 → 松开复位 |
| 编译报错 "..\*.h: No such file" |
Include Paths 配置丢失 |
CubeMX 重新生成,或在 Options → C/C++ → Include Paths 中手动添加 |
| 串口输出乱码 |
波特率不一致 |
确认 MCU 和串口助手都设成了相同波特率(如 115200) |