引言

STM32 开发工具链的选择直接影响开发效率、代码可移植性和调试体验。Keil 和 IAR 是商业 IDE,开箱即用;GCC 则是开源免费,常配合 Makefile 或 CMake 使用。本文不讨论图形化配置工具(如 STM32CubeMX),而是聚焦于纯手工搭建,以揭示每种工具链的底层机制。

1. 工程文件组成

一个 STM32 裸机工程通常包含以下文件:

  • 启动文件(startup_xxx.s):设置堆栈、中断向量表、调用 SystemInit 和 main。
  • 系统初始化(system_stm32f1xx.c):配置时钟。
  • 外设寄存器定义(stm32f1xx.h):芯片头文件。
  • 主程序(main.c):用户代码。
  • 链接脚本(仅 GCC 需要,.ld 文件)或分散加载文件(IAR 的 .icf,Keil 的 .sct)。

2. Keil MDK 搭建

2.1 工程创建

  1. 新建项目,选择芯片型号(如 STM32F103C8)。
  2. 添加启动文件 startup_stm32f103xb.s(Keil 自带或从 ST 库获取)。
  3. 添加 stm32f1xx.hsystem_stm32f1xx.cmain.c
  4. 配置魔术棒(Options for Target):
    • Target 标签:设置晶振频率(如 8MHz),勾选 Use MicroLIB(减少代码体积)。
    • C/C++ 标签:定义宏 STM32F10X_MD,添加头文件路径。
    • Debug 标签:选择 ST-Link 或 J-Link。

2.2 代码示例

// main.c
#include "stm32f1xx.h"

void delay(void) {
    for (int i = 0; i < 1000000; i++);
}

int main(void) {
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    GPIOC->CRH &= ~(0xF << 20);
    GPIOC->CRH |= (0x2 << 20);  // PC13 推挽输出
    while (1) {
        GPIOC->ODR ^= (1 << 13);
        delay();
    }
}

2.3 编译与调试

点击 Build 即可生成 .axf 文件。Keil 内置调试器,可直接下载并单步调试,支持寄存器窗口和实时变量查看。

3. IAR EWARM 搭建

3.1 工程创建

  1. 创建新工作区,新建项目,选择芯片型号。
  2. 添加启动文件(IAR 版本,如 startup_stm32f103xb.s)。
  3. 添加源文件,注意 IAR 使用 .icf 链接配置文件,默认会生成,但可自定义。
  4. 配置 Project Options:
    • General Options → Target:选择器件。
    • C/C++ Compiler → Preprocessor:定义 STM32F10X_MD,添加 include 路径。
    • Linker → Config:勾选 Override default,选择 .icf 文件(如 stm32f103xb_flash.icf)。
    • Debugger:选择 ST-Link 或 J-Link。

3.2 代码示例

与 Keil 相同,但注意 IAR 对语法检查更严格,例如 #include 路径区分大小写。

3.3 编译与调试

IAR 的编译器优化选项丰富(如 High Speed、Balanced),生成的代码效率高。调试器支持代码覆盖率分析,但界面较复杂。

4. GCC + Makefile 搭建

4.1 工具链安装

在 Linux 或 Windows(MSYS2/WSL)下安装 arm-none-eabi-gccmakeopenocd

4.2 工程结构

project/
├── src/
│   ├── main.c
│   └── system_stm32f1xx.c
├── inc/
│   └── stm32f1xx.h
├── startup/
│   └── startup_stm32f103xb.s
├── linker/
│   └── stm32f103xb_flash.ld
└── Makefile

4.3 链接脚本示例(stm32f103xb_flash.ld)

/* 内存布局 */
MEMORY
{
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
  RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}

_estack = 0x20005000;  /* 栈顶地址 */

SECTIONS
{
  .isr_vector : {
    KEEP(*(.isr_vector))
  } > FLASH
  .text : { *(.text*) } > FLASH
  .data : { *(.data*) } > RAM AT> FLASH
  .bss : { *(.bss*) } > RAM
}

4.4 Makefile 核心规则

CROSS_COMPILE = arm-none-eabi-
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
CFLAGS = -mcpu=cortex-m3 -mthumb -Wall -O2 -DSTM32F10X_MD -Iinc
LDFLAGS = -Tlinker/stm32f103xb_flash.ld -nostdlib

OBJS = src/main.o src/system_stm32f1xx.o startup/startup_stm32f103xb.o

all: firmware.elf firmware.bin

firmware.elf: $(OBJS)
	$(CC) $(LDFLAGS) -o $@ $^

firmware.bin: firmware.elf
	$(OBJCOPY) -O binary $< $@

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

%.o: %.s
	$(CC) $(CFLAGS) -c -o $@ $<

clean:
	rm -f *.o src/*.o startup/*.o firmware.*

4.5 编译与烧录

运行 make 生成 .bin 文件,然后用 openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c "program firmware.bin 0x08000000 verify reset exit" 烧录。调试可使用 GDB 配合 OpenOCD。

5. 对比总结

| 特性 | Keil MDK | IAR EWARM | GCC + Makefile | |------|----------|-----------|----------------| | 许可证 | 商业(免费版有限制) | 商业(昂贵) | 开源免费 | | 易用性 | 高,适合新手 | 中,学习曲线陡 | 低,需熟悉命令行 | | 代码密度 | 良好 | 优秀(优化强) | 良好(依赖优化选项) | | 调试体验 | 优秀,集成度高 | 优秀,支持高级分析 | 一般,需配置 GDB | | 跨平台 | Windows | Windows/Linux | 全平台 | | 可移植性 | 低(工程文件私有) | 低 | 高(标准工具链) |

6. 选型建议

  • 快速原型/学习:选择 Keil,上手快,资料多。
  • 商业产品/追求极致性能:IAR 的优化能减小 Flash 占用,适合量产。
  • 开源项目/CI/CD:GCC 配合 CMake 更易集成到自动化流程。

注意事项

  • 启动文件必须与芯片型号匹配,否则中断向量表错误。
  • 链接脚本中的 RAM 大小不能超过芯片实际值,否则运行时崩溃。
  • 使用 GCC 时,需在启动文件中正确设置 _estack 符号,否则栈溢出。
  • 三种工具链的 __attribute__ 语法略有差异,跨平台代码需用宏封装。

结语

无论选择哪种工具链,理解其背后的编译、链接和调试原理才是关键。建议至少掌握一种商业 IDE 和一种开源方案,以便在不同项目中灵活切换。希望本文能帮助你搭建出第一个 STM32 工程,并做出明智的工具选择。