STM32F103的开发板到手了,接下来该怎么走
板子刚拿到手的那一刻,心情大概都是一样的——包装拆开,一块蓝色的PCB躺在防静电袋里,USB线、杜邦线、可能还有几个传感器模块散在旁边,然后就是一阵短暂的茫然:接下来该干嘛?我当年第一次拿到STM32F103C8T6最小系统板的时候,盯着上面那颗LQFP48封装的芯片看了半天,心里想的全是“这玩意儿怎么连个屏幕都没有,代码写进去我怎么知道它在跑”。如果你现在正处于这个阶段,那这篇内容就是写给你的。我会把从零开始折腾STM32F103的完整路径拆开讲清楚,包括工具链怎么选、第一个工程怎么建、点灯之后该练什么、哪些坑几乎每个人都会踩一遍。不管你是电子专业的学生、想转嵌入式的软件工程师,还是纯粹想用单片机做点小玩意儿的爱好者,下面这些内容都能直接拿去用。
1. 先搞清楚你手里这块板子到底有什么
1.1 F103系列芯片的定位与常见型号差异
STM32F103属于意法半导体(ST)的ARM Cortex-M3内核微控制器,主频最高72MHz,这个系列在嵌入式圈子里被用得极广,原因很简单:性能够用、外设丰富、资料多、价格便宜。但“STM32F103”这个说法其实涵盖了一大票型号,不同型号之间的差异比你想象的大。
最常见的几种封装和型号:
| 型号 | 封装 | Flash | RAM | 典型开发板 |
|---|---|---|---|---|
| STM32F103C8T6 | LQFP48 | 64KB | 20KB | 蓝色最小系统板(俗称“蓝板”) |
| STM32F103RCT6 | LQFP64 | 256KB | 48KB | 正点原子Mini板等 |
| STM32F103ZET6 | LQFP144 | 512KB | 64KB | 正点原子战舰板、野火指南者等 |
| STM32F103VET6 | LQFP100 | 512KB | 64KB | 一些工业评估板 |
你买到的开发板如果是那种十几块钱的蓝色小板,大概率是C8T6。这块板子的特点是引脚全部引出、带一个8MHz晶振和一个32.768kHz晶振、有复位按键和BOOT跳线、板载一个Micro USB口(只供电,不带USB转串口芯片)。注意最后这一点,很多新手拿到蓝板后插上USB发现电脑没反应,以为板子坏了,其实它只是取了5V供电,数据线根本没连到芯片的USB引脚上。
如果你买的是正点原子、野火、普中这类品牌的开发板,通常会板载USB转串口芯片(CH340或CP2102)、多个按键、LED、OLED接口、传感器接口等,价格从几十到两三百不等。板载资源越多,上手越方便,但核心学习路径是一样的。
1.2 开发板上哪些东西你第一周就会用到
拿到板子后,先别急着写代码,花十分钟把板子上的关键部件认一遍:
- 电源部分:确认供电方式。蓝板通过Micro USB取5V,板上有AMS1117-3.3稳压到3.3V。如果你用的是ST-Link下载器供电,注意ST-Link的3.3V输出电流有限,带不动太多外设。
- 晶振:8MHz主晶振给系统提供时钟源,经过PLL倍频到72MHz。32.768kHz晶振给RTC用。如果板子上没有焊接晶振(有些廉价板会省),你就只能用内部RC振荡器,精度差很多。
- BOOT跳线:BOOT0和BOOT1的组合决定芯片从哪启动。正常运行时BOOT0接GND,从Flash启动。串口下载时需要把BOOT0接VCC,下载完再拨回去。这个跳线帽丢了会很麻烦,建议多备几个。
- 复位按键:别小看它,调试的时候按一下比拔插USB快得多。
- LED:通常PC13接了一个LED(蓝板上是PC13),有些板子是PA5或PB0。点灯实验就是拿它开刀。
- 下载接口:蓝板引出SWD接口(SWDIO、SWCLK、GND、3.3V),需要配合ST-Link或DAP-Link使用。品牌开发板通常板载了下载器电路,一根USB线就能下载。
注意:如果你买的是蓝板且没有ST-Link,先别急着买。有些商家会把ST-Link和板子打包卖,价格差不了几块钱,但省事很多。单独买ST-Link的话,认准带SWD接口的版本,别买成只支持JTAG的老款。
2. 工具链的选择:Keil、CubeIDE还是VS Code
2.1 Keil MDK:资料最多但需要处理授权
Keil MDK是STM32开发里资料最丰富的工具链,国内绝大多数教程、例程、视频都是基于Keil的。它的优势在于:编译器成熟、调试器集成度高、器件支持包(Device Family Pack)安装方便、网上遇到问题一搜就有答案。
但Keil有两个让人头疼的地方。第一是授权问题,MDK-Lite版本限制32KB代码,F103C8T6的64KB Flash刚好卡在边界上,稍微加点功能就超了。第二是Keil5默认不包含ARM编译器,需要单独安装ARM Compiler 5或6,而且Keil5和Keil4的工程不兼容,网上很多老例程是Keil4的,直接打开会报错。
安装Keil5的正确顺序:
- 下载MDK-ARM安装包,安装到默认路径(不要有中文和空格)。
- 安装器件支持包:到Keil官网下载STM32F1系列的DFP包,或者用Pack Installer在线安装。
- 安装ARM Compiler:Keil5.37之后不再自带AC5,需要单独下载ARM Compiler 5并手动添加到Keil的安装目录下。
- 处理授权:如果你有正版License就正常激活,没有的话……嗯,自己想办法。
实操心得:Keil的工程路径千万不要有中文和空格,否则编译时会出现各种莫名其妙的错误。我见过有人把工程放在“D:\我的项目\STM32学习\”下面,然后编译报错找不到文件,折腾了一下午才发现是路径问题。
2.2 STM32CubeIDE:官方免费但生态稍弱
STM32CubeIDE是ST官方推出的免费IDE,基于Eclipse和GCC编译器,集成了CubeMX配置工具。它的最大优势是免费、跨平台、自带CubeMX,新建工程时可以直接图形化配置引脚和外设,自动生成初始化代码。
缺点是编译速度比Keil慢(GCC优化不如ARMCC),调试体验也稍逊一筹,而且网上基于CubeIDE的教程相对少一些。如果你不想折腾授权问题,CubeIDE是个很省心的选择。
2.3 VS Code + 插件:适合有经验的开发者
用VS Code开发STM32需要自己搭建工具链:安装ARM GCC、OpenOCD或PyOCD、配置tasks.json和launch.json。这套方案灵活度最高,适合已经熟悉命令行的开发者。但新手不建议一上来就搞这套,光是配置编译和调试环境就能耗掉你两三天,而且出了问题不好排查。
我个人的建议是:第一周用Keil或CubeIDE把流程跑通,等对编译、下载、调试的流程有感觉了,再考虑迁移到VS Code。工具是为你服务的,不是用来折磨你的。
3. 第一个工程:从新建到点亮LED
3.1 用标准库还是HAL库
这是每个STM32新手都会遇到的问题。标准库(Standard Peripheral Library)是ST早期推出的库,直接操作寄存器,代码效率高、逻辑清晰,国内大量教程基于标准库。HAL库(Hardware Abstraction Layer)是ST现在主推的库,配合CubeMX使用,跨系列移植方便,但代码臃肿、执行效率低。
对于F103来说,我的建议是:先学标准库,再了解HAL库。原因很简单,标准库能让你更清楚地看到寄存器的操作逻辑,理解时钟树、GPIO模式、中断向量这些底层概念。等你把F103玩明白了,再去看HAL库会发现很多东西都是相通的。
3.2 新建标准库工程的完整步骤
以Keil5为例,新建一个标准库工程的流程:
创建工程目录结构。建议这样组织:
Project/ ├── CMSIS/ # 内核相关文件 ├── Library/ # 标准库文件 ├── User/ # 用户代码 ├── Start/ # 启动文件 ├── Obj/ # 编译输出 └── Project.uvprojx # Keil工程文件复制必要文件。从ST官网下载标准库压缩包,解压后把以下文件复制到对应目录:
stm32f10x.h、system_stm32f10x.c/h、core_cm3.c/h→ CMSIS目录stm32f10x_gpio.c/h、stm32f10x_rcc.c/h等 → Library目录startup_stm32f10x_md.s→ Start目录(C8T6属于中容量,用md后缀的启动文件)
在Keil中添加文件分组。打开Keil,新建工程,选择芯片型号STM32F103C8,然后在Project窗口中建立CMSIS、Library、User、Start四个分组,把对应文件添加进去。
配置头文件路径。在Options for Target → C/C++ → Include Paths中添加所有包含.h文件的目录。
添加宏定义。在C/C++ → Define中添加
USE_STDPERIPH_DRIVER, STM32F10X_MD。这两个宏缺一不可,前者启用标准库,后者告诉编译器芯片是中容量型号。编写main.c。一个最简单的点灯程序:
#include "stm32f10x.h" void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); while(1) { GPIO_SetBits(GPIOC, GPIO_Pin_13); Delay(5000000); GPIO_ResetBits(GPIOC, GPIO_Pin_13); Delay(5000000); } }- 配置下载器。在Options for Target → Debug中选择ST-Link Debugger,然后在Settings → Flash Download中勾选Reset and Run,这样下载完程序自动运行。
3.3 编译通过但下载失败:最常见的三个原因
“编译成功却烧录不进去”是新手遇到最多的问题,没有之一。根据我的经验,90%的情况是以下三个原因之一:
原因一:BOOT跳线不对。如果你用串口下载,BOOT0必须接VCC,下载完再拨回GND。如果用SWD下载,BOOT0接GND即可。很多人拨了跳线帽忘记拨回来,结果程序下载进去了但不运行。
原因二:ST-Link驱动或固件问题。ST-Link在Keil中需要正确识别。如果Debug Settings里显示“No ST-Link detected”,先检查USB线是否支持数据传输(有些线只能充电),再检查驱动是否安装。ST-Link的固件也可能需要升级,用ST-Link Utility或STM32CubeProgrammer可以升级固件。
原因三:芯片被读保护或写保护。如果你买的是二手板或者之前烧过程序,芯片可能被设置了读保护。用STM32CubeProgrammer连接后,在Option Bytes里把Read Out Protection设为Disabled,然后执行Full Chip Erase。
实操心得:遇到下载失败,先别急着换板子。按这个顺序排查:换USB线 → 检查BOOT跳线 → 换一个USB口 → 用STM32CubeProgrammer测试连接 → 检查芯片是否被保护。我遇到过最离谱的情况是USB线内部断了,外观完全看不出来,换线就好了。
4. 点灯之后该练什么:一条清晰的进阶路线
4.1 外设学习的推荐顺序
点灯只是让你确认工具链没问题,接下来才是真正的学习。我建议按这个顺序推进:
- GPIO输入:按键检测、按键消抖。学会用轮询和中断两种方式读按键。
- 串口通信:USART的配置、发送和接收、printf重定向。串口是调试利器,越早掌握越好。
- 定时器:定时中断、PWM输出、输入捕获。定时器是STM32最强大的外设之一,值得花时间深入。
- 中断系统:NVIC的配置、中断优先级、外部中断EXTI。
- ADC:模拟量采集、DMA传输。
- I2C和SPI:驱动OLED、EEPROM、传感器等外设。
- DMA:解放CPU,提高数据传输效率。
- RTC:实时时钟、闹钟、备份寄存器。
- 看门狗:独立看门狗和窗口看门狗,提高系统可靠性。
每个外设的学习方法都差不多:先看参考手册里对应章节的框图,理解工作原理;然后看标准库的例程,理解配置流程;最后自己动手改代码,做一个小项目验证。
4.2 用串口打印调试信息:比点灯更重要的技能
很多新手学完点灯就卡住了,因为除了LED亮灭,他们不知道程序到底在干什么。串口打印是解决这个问题的关键。把printf重定向到USART1之后,你可以在代码任何位置打印变量值、函数执行状态、错误信息,调试效率提升十倍不止。
printf重定向的代码:
int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); return ch; }然后在Keil的Options for Target → Target中勾选Use MicroLIB。注意,如果不勾选MicroLIB,需要自己实现_sys_exit等底层函数,否则会报错。
注意:重定向printf之后,串口助手的波特率要和代码中USART_InitStructure.USART_BaudRate设置的一致。我见过有人代码里写9600,串口助手开115200,然后说“串口输出乱码”,这属于自己坑自己。
4.3 从例程到项目:做一个超声波测距小车
学完基础外设后,最好的巩固方式是做一个小项目。超声波测距是个不错的选择,涉及定时器输入捕获、串口输出、GPIO控制,难度适中。
HC-SR04超声波模块的工作原理:Trig引脚给至少10微秒的高电平触发测距,模块自动发送8个40kHz方波,然后Echo引脚变高,高电平持续时间就是声波往返时间。距离 = 高电平时间 × 340m/s ÷ 2。
用STM32F103实现时,可以用定时器的输入捕获功能测量Echo高电平时间。具体做法:配置TIM2的通道1为输入捕获模式,上升沿触发,捕获到上升沿后记录当前计数值,然后切换为下降沿触发,捕获到下降沿后再记录计数值,两次差值乘以计数周期就是高电平时间。
这个项目能让你把GPIO、定时器、中断、串口这几个外设串起来用,做完之后对STM32的理解会上一个台阶。
5. 那些年我们一起踩过的坑
5.1 时钟配置错误导致串口乱码
STM32F103的时钟树是新手最容易搞错的地方。系统时钟默认是内部8MHz RC振荡器,经过PLL倍频后可以到72MHz。但如果你在SystemInit()之后又手动改了时钟配置,或者外部晶振没有起振,系统时钟就会跑偏,导致串口波特率不对,输出全是乱码。
排查方法:用示波器测MCO引脚(PA8)输出的时钟频率,或者在代码里读取RCC_GetClocksFreq()的返回值,通过串口打印出来确认。如果外部晶振不起振,检查晶振是否焊接、负载电容是否匹配(通常22pF)、晶振频率是否设置正确。
5.2 中断优先级配置不当导致程序卡死
STM32F103的中断优先级分为抢占优先级和响应优先级,NVIC_PriorityGroupConfig()决定了如何分配这两者的位数。如果配置不当,可能出现中断嵌套死锁、中断无法触发等问题。
常见错误:在中断服务函数里调用了Delay函数。Delay通常基于SysTick,而SysTick的中断优先级如果低于当前中断,就会导致死循环。正确的做法是在中断里只做标记,主循环里处理具体逻辑。
5.3 变量被优化导致调试时看不到值
Keil的编译器优化等级默认是Level 3,会把一些“看起来没用”的变量优化掉。调试时你会发现某个变量在Watch窗口里显示“not in scope”或者值不对。解决方法:把优化等级调到Level 0,或者给变量加volatile关键字。
5.4 栈溢出导致程序跑飞
STM32F103C8T6只有20KB RAM,如果定义了大数组或者递归调用太深,很容易栈溢出。表现是程序运行一段时间后莫名其妙复位或者进入HardFault。排查方法:在启动文件里把Stack_Size改大一些(比如从0x400改成0x800),或者在Keil的Target设置里调整。
6. 关于学习节奏和心态的一些实话
嵌入式开发的学习曲线是前陡后缓。刚开始接触寄存器、时钟树、中断向量这些东西会觉得非常抽象,但一旦跨过某个门槛,后面学新外设就是查手册、看例程、改代码的重复过程。
我见过太多人买了开发板,点了个灯,然后就没有然后了。板子在抽屉里吃灰,偶尔翻出来看一眼又放回去。问题不在于难度,而在于没有明确的目标。如果你只是“想学STM32”,这个目标太模糊了,很容易放弃。但如果你给自己定一个具体的目标,比如“做一个能显示温湿度的桌面时钟”或者“做一个能循迹的小车”,学习过程就会完全不一样。
另外,不要试图把参考手册从头读到尾。STM32F103的参考手册有1000多页,没有人能全部记住。正确的做法是:用到哪个外设就查哪个章节,先看框图理解原理,再看寄存器描述了解配置方法,最后对照标准库的例程写代码。
还有一点很重要:学会看英文资料。ST的官方文档、数据手册、参考手册、应用笔记都是英文的,虽然国内有很多翻译版,但翻译质量参差不齐,而且更新不及时。直接看英文原版虽然一开始慢,但准确度最高,长期来看效率更高。
最后说一个我自己的习惯:每次调试成功一个功能,我都会把关键配置和踩过的坑记在一个Markdown文件里。比如“串口重定向printf需要勾选MicroLIB”、“TIM2输入捕获需要先配置滤波器和分频器”之类的。这些笔记后来成了我自己的速查手册,比翻例程快得多。你也可以试试这个方法,坚持几个月,你会发现自己对STM32的理解比只看教程的人深得多。