1. 拿到板子先别急着点灯,搞懂STM32F103的脾气再说
STM32F103这块板子,在嵌入式圈子里基本就是“新手村第一只怪”。便宜、资料多、外设全,关键是踩坑的人足够多,你遇到的每一个问题,大概率十年前就有人在论坛里骂过了。我手上这块是经典的F103ZET6核心板,配了CH340串口、LED、按键、SPI Flash和一堆排针,淘宝上五六十块钱就能拿下。很多人拿到板子的第一反应是插上USB看灯亮不亮,然后打开Keil新建工程,结果编译报一堆错,或者烧录的时候提示“Flash Download failed”。这篇文章就是帮你把从开箱到跑通第一个工程之间的所有坑,一次性填平。
你可能会问,2025年了,为什么还要学F103?原因很简单:它是ARM Cortex-M3的教科书级实现,标准库和HAL库的代码量足够你理解单片机到底怎么工作。你把这颗芯片吃透了,后面换GD32、换ESP32、换任何国产替代,底层逻辑都是通的。而且F103的定时器、ADC、CAN、USB这些外设,在工业控制、消费电子、毕业设计里出现的频率极高,学完就能直接用。
这篇文章适合谁看?如果你刚拿到一块STM32F103开发板,或者正在纠结要不要买,或者买了之后卡在环境配置、烧录、外设调试的某个环节,那接下来的内容就是给你准备的。我会从硬件确认、工具链搭建、工程模板创建、外设调试、常见故障排查这几个维度,把F103的开发流程完整走一遍。每个步骤我都会解释为什么这么做,以及我实际踩过的坑。
2. 硬件确认与开发板选型:别让第一脚就踩空
2.1 拿到板子后必须做的三项物理检查
很多人拿到开发板直接插USB,结果电脑没反应,就开始怀疑板子坏了。其实在通电之前,有三件事必须先确认。
第一,确认芯片型号和封装。STM32F103系列有C8T6、RCT6、ZET6等多种型号,Flash容量从64KB到512KB不等,引脚数也不同。你买的是“最小系统板”还是“核心板+底板”结构,直接决定了你能用的外设数量。比如F103C8T6只有48个引脚,PA口和PB口部分复用,做项目时引脚不够用是常态。我建议新手直接上ZET6或者RCT6,引脚多、Flash大,后面做综合项目不用换板子。
第二,确认供电方式。大部分开发板支持USB供电和DC电源两种方式。USB供电时,板载的3.3V稳压芯片(通常是AMS1117)负责降压。如果你外接了多个传感器或者屏幕,USB的500mA可能不够,这时候需要换成DC 5V或7-12V供电。注意:有些板子的USB口只负责数据通信,不供电,必须单独接电源。我遇到过一块板子,USB插上电脑能识别串口,但芯片不工作,查了半天发现是供电跳线帽没插。
第三,确认BOOT跳线。STM32的启动模式由BOOT0和BOOT1两个引脚决定。BOOT0=0时从主Flash启动,这是正常运行模式;BOOT0=1时从系统存储器启动,用于串口下载程序。很多板子出厂时BOOT0跳线帽插在1的位置,你烧录完程序后忘记拔回来,结果程序不运行,还以为代码写错了。记住:烧录时BOOT0接1,运行时要接0。
2.2 开发板选型的三个关键参数
如果你还没买板子,或者想再买一块做对比,下面这三个参数决定了你的学习体验。
| 参数 | 推荐值 | 原因 |
|---|---|---|
| 芯片型号 | STM32F103ZET6 / RCT6 | Flash≥256KB,引脚≥64,外设齐全 |
| 调试器接口 | SWD四线(SWDIO/SWCLK/GND/3.3V) | 比JTAG少引脚,支持在线调试和烧录 |
| 板载外设 | LED、按键、串口、SPI Flash、I2C EEPROM | 覆盖基础实验,不用额外接线 |
调试器方面,ST-Link V2是性价比最高的选择,淘宝二三十块钱,支持SWD和SWIM两种模式。J-Link功能更强但价格贵,新手用ST-Link完全够。注意:市面上有些ST-Link是山寨的,固件版本太老会导致Keil识别不到,买之前问清楚是否支持升级固件。
2.3 芯片第一脚怎么确认:一个让新手抓狂的问题
“stm32芯片第一脚怎么确认”是搜索热词里出现频率很高的问题。F103的LQFP封装,芯片表面有一个小圆点,圆点对应的引脚就是第1脚。如果没有圆点,就看芯片边缘的倒角,倒角对应的也是第1脚。还有一种方法是看丝印,正着看芯片上的文字,左下角通常是第1脚。
但这里有个坑:不同封装的第1脚位置不一样。LQFP48和LQFP64的引脚排列不同,你照着别人的接线图接,结果接错了,芯片发烫甚至烧毁。我的经验是:拿到板子后,先用万用表蜂鸣档测一下板子上标注的“GND”和“3.3V”是否与芯片引脚对应,确认无误再通电。
3. 开发环境搭建:Keil、VS Code还是STM32CubeIDE
3.1 Keil MDK的安装与芯片包配置
Keil MDK是STM32开发的老牌工具,资料最多,但安装过程有几个坑。
首先,Keil5和Keil4的芯片包不通用。你下载的“Keil5兼容C51和STM32安装”教程,通常是先装MDK-ARM,再装C51,最后用管理员权限运行。但如果你只做STM32,不需要装C51,直接装MDK-ARM就行。安装路径不要有中文和空格,否则编译时会出现奇怪的错误。
其次,芯片包的安装。Keil安装完成后,需要单独下载STM32F1系列的Device Family Pack(DFP)。打开Keil,点击Pack Installer,搜索“STM32F1”,找到Keil::STM32F1xx_DFP,点击Install。如果网络慢,可以去官网下载离线包,双击安装。安装完成后,新建工程时才能在Device列表里找到STM32F103ZET6。
这里有个常见问题:安装完芯片包后,Keil里还是找不到芯片。原因是Pack Installer没有刷新,或者安装路径不对。解决办法是关闭Keil重新打开,或者在Pack Installer里点击“Check for Updates”。
3.2 VS Code + STM32开发环境的配置
“vscode配置stm32开发环境”是近两年的热门话题。VS Code的优势是轻量、插件丰富、代码补全好用,但配置起来比Keil麻烦。
你需要安装以下插件:Cortex-Debug(用于调试)、STM32 VS Code Extension(ST官方插件)、C/C++(微软官方插件)。然后安装ARM GCC工具链和OpenOCD。具体步骤是:下载arm-none-eabi-gcc,解压后把bin目录添加到系统PATH;下载OpenOCD,同样添加到PATH。
配置launch.json时,需要指定调试器类型(ST-Link)、接口(SWD)、目标芯片型号。一个典型的launch.json配置如下:
{ "version": "0.2.0", "configurations": [ { "name": "STM32 Debug (ST-Link)", "type": "cortex-debug", "request": "launch", "servertype": "openocd", "device": "STM32F103ZE", "configFiles": [ "interface/stlink.cfg", "target/stm32f1x.cfg" ], "svdFile": "./STM32F103.svd" } ] }注意:svdFile是可选的,但强烈建议加上,这样调试时能看到外设寄存器的实时值。SVD文件可以从ST官网下载。
VS Code方案的优势是跨平台,Linux和macOS都能用。但如果你在Windows上,Keil的调试体验更稳定,尤其是查看外设寄存器时,Keil的界面更直观。
3.3 标准库还是HAL库:新手的第一道选择题
STM32的库函数经历了标准库(SPL)、HAL库、LL库三个阶段。F103的教程大部分基于标准库,因为标准库的代码结构清晰,直接操作寄存器,适合理解底层原理。HAL库是ST现在主推的,配合STM32CubeMX可以图形化配置引脚和时钟,生成代码效率高,但封装层次深,出了问题不好排查。
我的建议是:先学标准库,把GPIO、定时器、中断、串口这些基础外设的寄存器配置搞清楚。等你理解了“为什么要把RCC->APB2ENR的第3位置1”之后,再用HAL库做项目,你会发现HAL库只是帮你把寄存器操作封装成了函数调用。
如果你时间紧,直接上HAL库+STM32CubeMX也行,但遇到问题时要知道怎么翻参考手册。比如串口接收中断不触发,你要去查USART_CR1寄存器的RXNEIE位有没有使能,而不是只盯着HAL_UART_Receive_IT函数。
4. 第一个工程:从新建模板到点亮LED
4.1 标准库工程模板的创建步骤
新建一个标准库工程,需要以下文件:启动文件(startup_stm32f10x_hd.s)、系统初始化文件(system_stm32f10x.c)、内核文件(core_cm3.c/h)、外设驱动文件(stm32f10x_gpio.c等)、中断服务文件(stm32f10x_it.c)、主函数(main.c)。
具体操作:在Keil中新建Project,选择芯片型号,然后在Manage Run-Time Environment里勾选CMSIS的CORE和Device的Startup。但这种方式生成的工程不包含标准库的外设驱动,需要手动添加。更稳妥的做法是直接复制ST官方提供的标准库例程,删掉不需要的文件,保留GPIO、RCC、MISC这几个基础驱动。
添加头文件路径时,注意把“User”、“Libraries/STM32F10x_StdPeriph_Driver/inc”、“Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x”都加进去。然后在stm32f10x.h里定义“USE_STDPERIPH_DRIVER”和“STM32F10X_HD”两个宏。这两个宏不定义,编译时会报一堆未定义错误。
4.2 GPIO输出配置:点亮第一颗LED
LED接在PB5上,低电平点亮。配置步骤如下:
- 使能GPIOB时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
- 定义GPIO_InitTypeDef结构体,设置Pin=GPIO_Pin_5,Mode=GPIO_Mode_Out_PP,Speed=GPIO_Speed_50MHz;
- 调用GPIO_Init(GPIOB, &GPIO_InitStructure);
- 在主循环里调用GPIO_ResetBits(GPIOB, GPIO_Pin_5)点亮,GPIO_SetBits熄灭。
这里有个细节:GPIO_Speed_50MHz不是指输出频率,而是指输出驱动器的响应速度。速度越高,功耗和电磁干扰越大。对于LED这种低速外设,用2MHz就够了。但很多人直接抄例程用50MHz,也能工作,只是不够严谨。
4.3 烧录失败的常见原因与排查
“load error: flash download failed”是Keil烧录时最常见的报错。原因通常有三个:
第一,调试器连接问题。检查ST-Link的SWDIO、SWCLK、GND、3.3V四根线是否接对。有些板子的SWD接口顺序是VCC、SWDIO、SWCLK、GND,有些是GND、SWCLK、SWDIO、VCC,接反了不仅烧录失败,还可能烧坏调试器。
第二,Flash算法未配置。在Keil的Options for Target -> Debug -> Settings -> Flash Download里,确认勾选了“STM32F10x High-density Flash”算法。如果列表是空的,点击“Add”手动添加。
第三,芯片被写保护。如果之前烧录过程序启用了读保护,需要先解除保护。用ST-Link Utility连接芯片,点击Target -> Option Bytes,把Read Out Protection设为Disabled,然后重新烧录。
还有一个容易被忽略的原因:Keil的“Use MicroLIB”选项。如果勾选了这个选项,但代码里用了printf,需要重定向fputc函数,否则程序会卡死在printf里。我建议新手先不勾选MicroLIB,等需要用到printf时再处理。
5. 外设调试实战:定时器、串口、I2C和超声波
5.1 定时器模式详解:从延时到PWM
STM32F103的定时器分为高级定时器(TIM1、TIM8)、通用定时器(TIM2-TIM5)和基本定时器(TIM6、TIM7)。新手最常用的是通用定时器的定时中断和PWM输出。
“stm32延时函数delay卡死”是常见问题。很多人用空循环做延时,结果优化等级一变,延时就不准了。正确的做法是用SysTick定时器或者通用定时器做硬件延时。SysTick是内核自带的24位递减计数器,配置简单,不占用外设资源。
配置SysTick的步骤:设置重装载值(SystemCoreClock/1000 - 1),清除当前值,设置优先级,使能中断。然后在SysTick_Handler里维护一个毫秒计数器。这样delay_ms函数只需要等待计数器变化即可,不会卡死。
PWM输出用TIM3的通道1(PA6)举例:配置TIM3的ARR和PSC决定PWM频率,配置CCR1决定占空比。频率计算公式是:Fpwm = 72MHz / ((ARR+1)(PSC+1))。假设你要1kHz的PWM,ARR=999,PSC=71,代入公式得到72MHz/(100072)=1kHz。占空比 = CCR1/(ARR+1),CCR1=500时占空比50%。
5.2 串口通信:接收中断与环形缓冲区
串口是调试和通信的基础。F103的USART1接PA9(TX)和PA10(RX)。配置步骤:使能USART1和GPIOA时钟,配置PA9为复用推挽输出,PA10为浮空输入,设置波特率、数据位、停止位、校验位,使能接收中断。
“stm32串口接收”的难点在于数据不定长。如果每次接收都进中断,数据量大时CPU会被频繁打断。解决方案是用环形缓冲区(Ring Buffer)+空闲中断(IDLE)。空闲中断在总线空闲时触发,此时把缓冲区里的数据一次性取走。
环形缓冲区的实现:定义一个数组和读写指针,写指针在中断里移动,读指针在主循环里移动。注意读写指针的边界处理,避免溢出。我见过有人用“读指针==写指针”判断空,“写指针+1==读指针”判断满,结果缓冲区大小必须是2的幂,否则会浪费一个字节。
5.3 I2C通信:BH1750光照传感器与OLED显示
“stm32 bh1750 oled i2c proteus完整原理图”是搜索热词,说明很多人用Proteus做仿真。但Proteus的I2C仿真经常出问题,建议直接上实物调试。
BH1750的I2C地址是0x23(ADDR接地)或0x5C(ADDR接VCC)。初始化流程:发送上电命令(0x01),发送连续测量命令(0x10),延时180ms,读取2字节数据,合并后除以1.2得到光照值(单位lx)。
OLED(SSD1306)的I2C地址通常是0x78。初始化需要发送一系列命令,包括关闭显示、设置时钟分频、设置多路复用率、设置显示偏移、设置起始行、设置充电泵、设置内存寻址模式、开启显示等。这些命令在数据手册里都有,但顺序不能乱,否则屏幕不亮。
I2C调试的常见问题:SDA和SCL接反、上拉电阻缺失、地址错误。F103的硬件I2C有已知的锁死问题,很多项目直接用软件模拟I2C。软件I2C的引脚可以任意指定,时序用延时控制,稳定性反而更好。
5.4 超声波测距:HC-SR04与定时器捕获
“stm32超声波测距”是毕业设计的热门题目。HC-SR04模块有四个引脚:VCC、GND、Trig、Echo。Trig接GPIO输出,Echo接GPIO输入或定时器捕获引脚。
测距原理:Trig发送10us高电平脉冲,模块发出8个40kHz超声波,Echo引脚输出高电平,高电平持续时间就是超声波往返时间。距离 = 时间 * 340m/s / 2。
用定时器捕获测量Echo高电平时间:配置TIM2的通道1为输入捕获模式,上升沿触发,记录CCR1的值;然后改为下降沿触发,再次记录CCR1的值。两次差值乘以定时器周期就是高电平时间。注意定时器溢出问题,如果距离超过4米,高电平时间超过定时器溢出周期,需要处理溢出中断。
实际调试时,超声波模块容易受温度、湿度、障碍物材质影响。我建议在代码里加一个滑动平均滤波,连续测5次去掉最大最小值再取平均,数据会稳定很多。
6. 常见问题速查与避坑指南
6.1 编译与烧录问题排查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 编译报错“undefined symbol” | 头文件路径未添加或宏未定义 | 检查Include Paths和Preprocessor Symbols |
| 烧录提示“No target connected” | SWD线接错或调试器驱动未装 | 检查接线,重装ST-Link驱动 |
| 程序下载后不运行 | BOOT0跳线帽未拔回0 | 断电,将BOOT0接GND,重新上电 |
| 串口无输出 | 波特率不匹配或TX/RX接反 | 确认波特率115200,交换TX/RX |
| 定时器中断不触发 | 未使能NVIC或中断标志未清除 | 检查NVIC配置和中断服务函数 |
6.2 调试技巧:用LED和串口定位问题
当程序跑飞时,不要急着用调试器单步执行。先在关键位置加LED翻转和串口打印,快速定位问题区间。比如在main函数开头点亮LED,如果LED不亮,说明程序没进main,问题在启动文件或时钟配置;如果LED亮但串口没输出,问题在串口配置。
Keil的Event Recorder和STM32CubeMonitor可以实时查看变量变化,但配置复杂。新手用printf重定向到串口就够了。重定向fputc函数时,注意在Keil的Target选项里勾选“Use MicroLIB”,否则需要自己实现半主机模式。
6.3 电源与信号完整性的坑
F103对电源噪声比较敏感。如果ADC采样值跳动大,先检查VDDA和VSSA是否接了滤波电容。我遇到过一块板子,ADC采样值随LED亮灭变化,查了半天发现是LED的限流电阻太小,拉低了整体电压。
SWD调试线太长也会导致连接不稳定。建议SWD线不超过20cm,且远离电机、继电器等干扰源。如果必须长距离调试,用屏蔽线并加磁环。
7. 从F103到更复杂的项目:下一步怎么走
F103跑通之后,你可以往几个方向扩展。第一个方向是RTOS,移植FreeRTOS或RT-Thread,学习任务调度、信号量、消息队列。第二个方向是通信协议,比如CAN总线、Modbus(agile_modbus stm32)、LIN收发器。第三个方向是电机控制,用F103的定时器输出互补PWM,驱动步进电机或伺服电机(stm32控制伺服电机485)。
如果你对USB感兴趣,F103自带USB外设,可以做HID设备(键盘、鼠标)或虚拟串口。但F103的USB库比较老,调试起来坑多,建议先跑通官方例程再改。
最后说一个我自己的体会:学STM32最怕的是“只看不练”。你看十遍定时器捕获的教程,不如自己焊一个超声波模块调一晚上。遇到问题先查参考手册,再搜论坛,最后再问人。F103的资料足够多,你遇到的99%的问题,网上都有答案。关键是你要有耐心去翻数据手册,去理解每一个寄存器的含义。这块板子买回来,别让它吃灰,从点灯开始,一步一步来,三个月后你就能独立做项目了。