3. 开发环境搭建:Keil/IAR环境配置、编译器选项设置、Linker脚本编写基础
好,咱们进入第三章。说实话,很多做Bootloader的新手,代码写得挺漂亮,结果栽在环境配置上。我见过太多人花三天调一个链接错误,最后发现是编译器选项没设对。所以这一章,咱们把环境搭建这件事彻底讲透。
3.1 选择Keil还是IAR?我的建议
先说说这两个工具。Keil MDK和IAR Embedded Workbench,是ARM Cortex-M开发的两大主流。我个人习惯用Keil多一些,因为它的界面更直观,调试器集成得也好。但IAR的代码优化确实更狠,尤其对代码尺寸的控制,IAR能比Keil再小10%-15%。
怎么选?我建议这样:
- 如果你刚入门,或者团队里其他人都在用Keil,那就用Keil。遇到问题好找人问。
- 如果你对代码尺寸有极致要求,比如Flash只剩2KB空间了,那IAR是更好的选择。
- 如果你做车规级产品,很多Tier1要求用IAR,因为它的编译器认证更全。
3.2 Keil环境配置要点
Keil的配置,说白了就是三个地方:Target、C/C++、Linker。咱们一个一个说。
3.2.1 Target选项卡
这里最关键的是选对芯片型号和浮点单元。我见过有人把STM32F103选成F407,编译通过了,下载进去直接跑飞。
| 选项 | 推荐设置 | 说明 |
|---|---|---|
| Device | 你的具体芯片型号 | 比如STM32G070RBT6 |
| Xtal | 实际晶振频率 | 别乱填,会影响串口波特率 |
| Use Cross-Module Optimization | 勾选 | 能优化代码尺寸 |
| Use MicroLIB | 强烈建议勾选 | 减少代码体积,Bootloader必备 |
3.2.2 C/C++选项卡
这里我重点说几个选项:
- Optimization:Bootloader建议选 -O1 或 -Os。别选 -O0,代码太大。也别选 -O3,有时候优化过头会把你的延时函数优化掉。
- Define:这里要定义你的芯片宏,比如 STM32G070xx。不定义的话,很多外设驱动编译不过。
- Language C:选 C99 或 C11。C89太老了,很多语法不支持。
避坑指南: 我曾经在 -O3 优化下,一个简单的 for 循环延时被编译器直接优化没了,导致Bootloader启动时序全乱。后来我加了 volatile 关键字才解决。所以,Bootloader里所有跟硬件时序相关的变量,记得加 volatile。
3.3 IAR环境配置要点
IAR的配置逻辑跟Keil差不多,但名字不一样。我刚开始用IAR时,找那个链接脚本找了半小时。
3.3.1 General Options
这里主要设置芯片型号和浮点单元。IAR的芯片支持包叫"device files",记得更新到最新版。
3.3.2 C/C++ Compiler
IAR的编译器选项在"Options > C/C++ Compiler"里:
- Optimization:选 High (Balanced) 或 High (Size)。IAR的尺寸优化确实厉害。
- Language conformance:选 C99 或 C11。
- Preprocessor:这里定义宏,跟Keil的Define一样。
3.4 Linker脚本编写基础
Linker脚本,说白了就是告诉编译器:你的代码放哪,数据放哪。Bootloader的Linker脚本尤其重要,因为你要把Bootloader和应用程序放在不同的Flash区域。
我以STM32G070为例,写一个最简单的Linker脚本:
/* Linker script for STM32G070 Bootloader */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* Bootloader占用前32KB */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 36K /* RAM 36KB */
}
SECTIONS
{
.text :
{
KEEP(*(.isr_vector)) /* 中断向量表,必须保留 */
*(.text*) /* 代码段 */
*(.rodata*) /* 只读数据 */
. = ALIGN(4);
_etext = .; /* 代码结束地址 */
} > FLASH
.data :
{
_sdata = .; /* 数据段起始地址 */
*(.data*)
. = ALIGN(4);
_edata = .; /* 数据段结束地址 */
} > RAM AT> FLASH
.bss :
{
_sbss = .; /* BSS段起始地址 */
*(.bss*)
. = ALIGN(4);
_ebss = .; /* BSS段结束地址 */
} > RAM
}
这段脚本里,有几个关键点:
- FLASH起始地址0x08000000,长度32KB。这意味着Bootloader最大只能32KB。
- 中断向量表必须放在最前面,用KEEP关键字保护,防止被优化掉。
- .data段在RAM里运行,但初始值存在Flash里。所以用了"AT> FLASH"。
- _sdata、_edata这些符号,是给启动代码用的。启动代码会把这些数据从Flash拷贝到RAM。
3.5 编译器选项对比总结
最后,我整理了一个对比表,方便你参考:
| 配置项 | Keil | IAR | 我的建议 |
|---|---|---|---|
| 优化等级 | -O1 或 -Os | High (Balanced) | Bootloader用尺寸优化 |
| 微库 | Use MicroLIB | 无对应选项 | Keil必勾,IAR默认就小 |
| 链接脚本 | .sct文件 | .icf文件 | 语法不同,但逻辑一样 |
| 调试器 | JLink/ST-Link | JLink/I-jet | JLink最通用 |
嗯,环境搭建这部分就这些。说白了,工具只是工具,关键是你得理解它背后在干什么。下一章咱们开始写Bootloader的启动代码,那才是真正有意思的地方。
课后作业: 打开你的Keil或IAR,创建一个空工程,配置好芯片型号和Linker脚本。然后写一个空的main函数,编译通过。看看生成的.map文件,确认代码段和数据段的地址对不对。