2、嵌入式开发环境搭建:Keil/IAR环境配置、交叉编译工具链、调试器(J-Link/ST-Link)配置、工程模板建立

好,咱们正式开始动手了。这一章我带你把开发环境搭起来。说实话,很多新手上来就写代码,结果编译报错、烧录失败、调试器连不上,折腾半天才发现是环境没配好。我当年也踩过这个坑,所以这一章咱们一步到位,把 Keil、IAR、交叉编译链、调试器、工程模板全搞定。

2.1 开发工具选型:Keil 还是 IAR?

先聊个老生常谈的问题:Keil 和 IAR 选哪个?我个人习惯是看芯片厂家推荐。比如 STM32 系列,官方 SDK 和例程基本都是 Keil 的,你用 IAR 还得自己移植启动文件,麻烦。但如果是 TI 的 CC13xx 或者 Nordic 的 nRF5 系列,IAR 的支持反而更好。

说白了,两个工具都能干活。但你要注意一点:医疗贴片产品通常对代码体积和功耗有严格要求。IAR 的编译器优化确实比 Keil 强一些,尤其在高等级优化下,代码能小 10%~15%。我做过一个血糖监测贴片,Flash 只有 64KB,用 IAR 的 -Oz 优化才塞进去。所以如果你做的是资源受限的贴片,我建议优先考虑 IAR。

核心建议:

  • 芯片厂商主推哪个,就用哪个——省去移植启动文件和链接脚本的麻烦
  • 如果 Flash/RAM 紧张,优先 IAR(优化更激进)
  • 团队协作时统一工具链,别一半人用 Keil 一半人用 IAR,否则链接脚本和宏定义容易打架

2.2 Keil MDK 环境配置

Keil 的安装我就不啰嗦了,网上教程一堆。我重点说几个容易忽略的配置点。

2.2.1 编译器版本选择

Keil 自带的 Arm Compiler 有 5 和 6 两个版本。V5 是传统编译器,兼容性好;V6 基于 Clang,编译速度快,但有些老代码会报错。我的经验是:新项目用 V6,老项目维护用 V5。切换方法很简单:

Project -> Options for Target -> Target -> ARM Compiler
选择 "Use default compiler version 6" 或 "V5"

嗯,这里要注意:如果你用了某些第三方库(比如 RTX 实时操作系统),V6 下可能编译不过。我遇到过一回,折腾了半天才发现是编译器版本不兼容。

2.2.2 输出文件配置

医疗贴片升级需要生成 .hex 或 .bin 文件。Keil 默认只生成 .axf,你得手动打开:

Options for Target -> Output -> 勾选 "Create HEX File"
如果需要 .bin 文件,在 User 选项卡添加:
fromelf --bin --output=@L.bin @L.axf

我个人习惯同时生成 .hex 和 .bin。.hex 用于调试器烧录,.bin 用于 OTA 升级——因为 .bin 没有地址信息,体积更小,适合无线传输。

2.3 IAR Embedded Workbench 环境配置

IAR 的配置思路和 Keil 类似,但有几个地方不一样。

2.3.1 芯片支持包安装

IAR 不像 Keil 那样有 Pack Installer,你得手动下载芯片的 Device Support Package。比如 STM32L4 系列,去 IAR 官网下载对应的 .zip,然后:

Tools -> Configure Device Description -> Import
选择下载的 .ddf 文件

我曾经犯过一个低级错误:装完 IAR 直接打开例程,结果编译报错说找不到芯片定义。后来才发现忘了装支持包。所以建议你装完 IAR 后,第一件事就是把目标芯片的支持包装上。

2.3.2 链接脚本配置

IAR 的链接脚本是 .icf 文件,相当于 Keil 的 .sct。医疗贴片通常有 Bootloader 和 App 两个区,你得手动修改 .icf 文件来划分 Flash 空间。比如:

define symbol __ICFEDIT_region_ROM_start__  = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__    = 0x08003FFF;  // 16KB Bootloader
define symbol __ICFEDIT_region_RAM_start__  = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__    = 0x20001FFF;  // 8KB RAM

这个文件我建议你建好模板后备份一份,每次新项目直接改改地址就行,不用从头写。

2.4 交叉编译工具链

如果你用 Linux 开发,或者用 VS Code + GCC,那就需要自己搭交叉编译链。医疗贴片常用的芯片是 Cortex-M0/M4,对应的工具链是 arm-none-eabi-gcc

2.4.1 安装与验证

从 ARM 官网下载 gcc-arm-none-eabi 包,解压后配置环境变量。验证是否装好:

arm-none-eabi-gcc --version
arm-none-eabi-gdb --version

我建议你用 10.3 或更高版本,老版本对 Cortex-M33 的支持有问题。之前有个同事用 9.x 版本编译 M33 的代码,跑起来总死机,换了 10.3 就好了。

2.4.2 Makefile 模板

用 GCC 就得自己写 Makefile。我分享一个简化版模板:

# 编译器
CC = arm-none-eabi-gcc
LD = arm-none-eabi-gcc
OBJCOPY = arm-none-eabi-objcopy

# 编译选项
CFLAGS = -mcpu=cortex-m4 -mthumb -O2 -Wall -fdata-sections -ffunction-sections
LDFLAGS = -T linker.ld -Wl,--gc-sections

# 源文件
SRCS = main.c startup.c system.c
OBJS = $(SRCS:.c=.o)

# 目标
all: firmware.elf firmware.bin

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

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

clean:
	rm -f *.o *.elf *.bin

这个模板我用了好几年,基本改改芯片型号和源文件列表就能用。注意 -fdata-sections -ffunction-sections 这两个选项,配合 --gc-sections 能去掉未使用的函数和数据,对医疗贴片这种资源受限的设备特别有用。

2.5 调试器配置:J-Link 与 ST-Link

调试器是开发者的眼睛。没有它,你只能靠串口打印猜问题。我强烈建议你从一开始就配好调试器。

2.5.1 J-Link 配置

J-Link 是 Segger 的产品,稳定性和速度都很好。在 Keil 中配置:

Options for Target -> Debug -> Use: J-LINK/J-TRACE Cortex
点击 Settings -> Port: SWD(医疗贴片通常只有 SWD 接口)
Max Clock: 4MHz(别设太高,否则线长容易出错)

在 IAR 中类似:

Project -> Options -> Debugger -> Setup -> Driver: J-Link/J-Trace
点击 Download -> 勾选 "Use flash loader(s)"

小技巧:J-Link 的 SWD 时钟频率不要超过 10MHz。我试过 12MHz,结果调试器经常断开。4MHz 是最稳妥的,速度也够用。

2.5.2 ST-Link 配置

ST-Link 是 ST 官方的调试器,便宜但功能够用。配置方法和 J-Link 差不多,但有个坑:ST-Link 的固件版本要更新。我遇到过 ST-Link 连不上芯片,折腾半天发现是固件太老,不支持新芯片。更新方法:

下载 ST-Link Utility 或 STM32CubeProgrammer
连接 ST-Link -> Firmware Update -> 选择最新固件

另外,ST-Link 的 SWD 接口默认是 1.8V 还是 3.3V?这个要看你的贴片供电电压。医疗贴片很多用 1.8V 供电,ST-Link 的 Vref 引脚必须接 1.8V,否则烧录会失败。我吃过这个亏,烧坏过一块板子。

2.6 工程模板建立

好了,工具都配好了,接下来咱们建一个工程模板。这个模板以后每个项目都能复用,省得每次从头配。

2.6.1 模板目录结构

我习惯这样组织:

project_template/
├── bootloader/          # Bootloader 源码
│   ├── src/
│   ├── inc/
│   └── linker.icf       # 或 .sct
├── app/                 # 应用程序源码
│   ├── src/
│   ├── inc/
│   └── linker.icf
├── drivers/             # 芯片外设驱动
│   ├── CMSIS/
│   └── HAL/
├── middleware/          # 中间件(OTA、加密等)
├── output/              # 编译输出
├── tools/               # 工具脚本
│   └── flash.bat
├── Makefile             # 或 .uvprojx / .ewp
└── README.md

这个结构的好处是:Bootloader 和 App 分开,驱动和中间件独立,方便复用。我所有医疗贴片项目都基于这个模板,改改芯片型号和引脚定义就能用。

2.6.2 模板核心文件

每个模板必须包含以下文件:

  • startup_xxx.s:启动文件,中断向量表、堆栈初始化
  • system_xxx.c:系统时钟配置,比如 HSI/HSE、PLL 设置
  • linker.ld / .icf / .sct:链接脚本,定义 Flash/RAM 分区
  • main.c:空的主函数,只包含 while(1)
  • hal_conf.h:外设开关配置,只开启需要的模块

警告:启动文件和链接脚本一定要和芯片型号严格对应。我见过有人把 M0 的启动文件用在 M4 上,结果中断向量表错位,程序跑飞。建议直接从芯片厂商的 SDK 里复制,别自己手写。

2.6.3 模板验证

模板建好后,做三件事验证:

  1. 空工程编译:只包含 main.c 和启动文件,编译通过,无警告
  2. LED 闪烁:写一个简单的 GPIO 翻转程序,烧录后看 LED 是否闪烁
  3. 调试器连接:设置断点,单步执行,确认调试器正常工作

这三步都过了,模板才算合格。我每次建新模板都会走一遍这个流程,虽然简单,但能避免后续很多莫名其妙的问题。

2.7 避坑指南

最后,分享几个我踩过的坑:

  • 路径不要有中文和空格:Keil 和 IAR 对中文路径支持很差,编译报错都找不到原因。我建议所有路径用英文,下划线代替空格。
  • 调试器线长不要超过 20cm:SWD 信号在高速下容易受干扰,线越长越容易出错。我吃过这个亏,后来一律用 10cm 的排线。
  • 工程模板要版本控制:用 Git 管理模板,每次修改都提交。这样如果新项目出了问题,可以回退到稳定版本。

好了,环境搭建就到这里。下一章咱们开始写 Bootloader 的核心代码——Flash 分区管理与跳转逻辑。到时候你会用上今天配好的工具链和模板。