2. 开发环境搭建:MDK/IAR安装、工程模板建立、第一个点灯程序
说实话,我见过不少初学者,拿到开发板第一件事就是往 Keil 里点「Build」——结果报错一堆,连芯片型号都没选对。环境搭建这事儿,看着简单,但坑不少。今天咱们就把 MDK 和 IAR 这两个主流工具链捋一遍,再手把手建个工程模板,最后让 LED 亮起来。
2.1 工具链选择:MDK vs IAR
先说说这两个工具。MDK(Keil)和 IAR 是 ARM Cortex-M 开发的两大主流 IDE。我个人习惯用 MDK,因为它的工程管理更直观,调试器集成度高。但 IAR 的编译优化确实更狠,代码密度能压得更低。
| 对比项 | MDK (Keil) | IAR Embedded Workbench |
|---|---|---|
| 编译器 | ARMCC / ARMCLANG | IAR C/C++ Compiler |
| 调试器支持 | J-Link, ST-Link, ULINK | J-Link, ST-Link, I-Jet |
| 工程模板 | 自带 Pack Installer | 需手动配置 |
| 代码优化 | 中等 | 较高(尤其 -Oz) |
| 学习曲线 | 较平缓 | 稍陡 |
我的建议是:初学者先用 MDK,等熟悉了寄存器操作和调试流程,再尝试 IAR。我在项目中遇到过用 IAR 编译出的固件比 MDK 小 15% 的情况,但那是后期优化阶段的事了。
2.2 MDK 安装与配置
安装 MDK 其实不复杂,但有几个关键点容易忽略。
2.2.1 安装步骤
- 下载 MDK-ARM:从 Keil 官网获取最新版本(目前是 5.38a)。
- 安装主程序:一路 Next,注意安装路径不要有中文。
- 安装器件包:运行 Pack Installer,搜索 STM32F103 并安装对应的 DF Pack。
- 注册 License:用正版或评估版(评估版有 32KB 代码限制,点灯够用)。
TOOLS.INI 文件中的路径配置。我曾经因为系统用户名是中文,导致编译器找不到头文件,折腾了半小时才发现是路径编码问题。
2.2.2 配置调试器
我用的是 ST-Link,便宜又稳定。在 MDK 里点 Project → Options for Target → Debug,选择 ST-Link Debugger,然后点 Settings 确认 SWD 模式、速度选 4MHz 就行。
2.3 工程模板建立
每次新建工程都从头配置?太累了。我习惯做一个「最小工程模板」,以后直接复制改改就能用。
2.3.1 模板目录结构
STM32F103_Template/
├── Core/
│ ├── startup_stm32f103xb.s // 启动文件
│ ├── system_stm32f103xe.c // 系统时钟配置
│ └── stm32f1xx.h // 外设寄存器定义
├── Drivers/
│ └── CMSIS/
│ ├── core_cm3.h
│ └── cmsis_compiler.h
├── User/
│ ├── main.c
│ └── stm32f1xx_it.c // 中断服务函数
├── Project/
│ └── template.uvprojx // MDK 工程文件
└── Output/ // 编译输出目录
这个结构是我从实际项目中提炼出来的。核心文件就三个:启动文件、系统初始化、主程序。其他外设驱动按需添加。
2.3.2 启动文件选择
启动文件要根据芯片型号选。STM32F103C8T6 是 64KB Flash,用 startup_stm32f103xb.s。F103ZET6 是 512KB Flash,用 startup_stm32f103xe.s。选错了,程序跑不起来。
2.4 第一个点灯程序
好了,环境搭好了,模板建好了,该让硬件动起来了。点灯是嵌入式界的「Hello World」,但背后涉及的知识点可不少。
2.4.1 硬件原理
STM32F103C8T6 最小系统板上,LED 通常接在 PC13 引脚(高电平熄灭,低电平点亮)。为什么是低电平点亮?因为 PC13 内部上拉到 VCC,输出 0 时电流从 VCC 经 LED 流入引脚。
2.4.2 寄存器操作
点灯需要配置两个寄存器组:RCC(时钟控制)和 GPIO(通用输入输出)。
// 1. 使能 GPIOC 时钟(RCC_APB2ENR 寄存器)
RCC->APB2ENR |= (1 << 4); // 第4位对应 GPIOC 时钟使能
// 2. 配置 PC13 为推挽输出(GPIOx_CRH 寄存器)
GPIOC->CRH &= ~(0xF << 20); // 先清除 CNF13 和 MODE13 位
GPIOC->CRH |= (0x2 << 20); // MODE13 = 10(输出模式,最大速度 2MHz)
GPIOC->CRH |= (0x0 << 22); // CNF13 = 00(通用推挽输出)
// 3. 点亮 LED(GPIOx_BSRR 寄存器)
GPIOC->BSRR = (1 << 29); // BR13 = 1,复位引脚(输出低电平)
你可能会问:为什么不用库函数?因为寄存器编程能让你真正理解硬件是怎么工作的。我在调试一个 SPI 通信问题时,就是靠直接读寄存器值才找到时钟相位配置错误的。
2.4.3 完整代码
#include "stm32f1xx.h"
void delay(uint32_t count) {
while (count--);
}
int main(void) {
// 使能 GPIOC 时钟
RCC->APB2ENR |= (1 << 4);
// 配置 PC13 为推挽输出
GPIOC->CRH &= ~(0xF << 20);
GPIOC->CRH |= (0x2 << 20); // 输出模式,2MHz
while (1) {
GPIOC->BSRR = (1 << 29); // 点亮 LED
delay(500000);
GPIOC->BSRR = (1 << 13); // 熄灭 LED(BS13 = 1)
delay(500000);
}
}
2.5 调试与验证
代码写完了,下载到板子上。如果 LED 没亮,别慌。我遇到过的情况:
- LED 不亮:检查 RCC 时钟使能位是否正确。用调试器看 RCC->APB2ENR 寄存器的值。
- LED 常亮不闪:delay 函数优化掉了。把 delay 循环里的变量声明为
volatile。 - 下载失败:检查 SWD 接线,或者按住复位键再点下载。
嗯,这里要注意:调试器不要带电插拔。我烧过一个 ST-Link,就是因为调试过程中拔了线,导致电平冲突烧了接口芯片。
2.6 知识结构总览
下面这张图把本章的核心逻辑串起来了。从工具链选择到寄存器操作,每一步都是后面章节的基础。
这张图把本章的知识点串成了流水线:选工具 → 装环境 → 建模板 → 写代码 → 调硬件。每一步都踩实了,后面学外设驱动、中断系统、低功耗模式时,你才知道底层是怎么跑的。
好了,LED 已经在你手上闪烁了。虽然只是点亮一个灯,但你已经完成了从零到一的关键一步——你亲手配置了时钟、操作了寄存器、控制了硬件引脚。这比任何库函数调用都更有价值。