买STM32F103开发板这件事我在购物车里放了快两个月。原因和不少刚入门的朋友一样一边听人说STM32是嵌入式入门必经之路一边又听人说寄存器、标准库、HAL库、CubeMX这一堆词堆下来直接劝退。直到上周终于把这块几十块钱的stm32开发板买回来了拆开快递那一刻才想明白一个道理——资料再多不如先把板子通上电、点个灯、看它跑起来后面每一步都是在给前面一步补课。这篇内容就记录我从开箱到跑通常用外设的完整过程以及过程中踩过的坑和排查思路。适合刚入手F103开发板、或者正在犹豫买哪块板子的朋友不管你是电子相关专业学生还是上班后想转嵌入式方向这套路线基本通用。1. 开发板到手先别急着通电型号、丝印和下载器确认很多人的第一反应是拆开包装直接插USB看到电源灯亮了就觉得完事大吉。我建议你先花十分钟做三件事看芯片型号、确认第一脚、认下载器接口。这三件事看着基础后面每一个坑都和它们有关。1.1 先看芯片型号决定你的学习空间最常见的STM32F103开发板芯片型号就那么几种淘宝上卖得最多的是STM32F103C8T6这颗小芯片LQFP48封装巴掌大的板子。但如果你买的是所谓精英板指南者这类带屏带按键的大板子芯片往往是STM32F103ZET6LQFP144封装资源多出一大截。两种情况我都用过先别急着比好坏先把资源差异搞清楚。型号封装FlashSRAM主频典型板子STM32F103C8T6LQFP4864KB20KB72MHz蓝板/黑板小系统板STM32F103RBT6LQFP64128KB20KB72MHz部分中型板STM32F103ZET6LQFP144512KB64KB72MHz正点原子精英板/指南者C8T6的64KB Flash听起来不大但把标准库全量编译进去也就二三十KB跑完一个入门学习周期完全够。ZET6多出来的Flash和引脚主要影响你后面能不能玩FSMC驱动LCD、能不能同时挂多个外设、能不能跑稍微复杂一点的状态机。如果手里已经买了C8T6别担心本文后面所有例程它都能跑。如果还没买我一般建议从C8T6开始便宜、资料多、坏了不心疼。1.2 确认第一脚、BOOT跳线和PCB丝印芯片第一脚这个事新手经常在看原理图或者接传感器时搞反。LQFP封装的芯片左上角会有一个圆形凹点或者斜切角那个位置就是第一脚然后逆时针数。PCB上也会在对应的位置印一个小圆点或者一个带缺口的框两个对得上就说明板厂画封装没问题。开发板上一般还有一个BOOT0跳线帽和BOOT1跳线帽有些小板只有BOOT0。正常从Flash启动时BOOT0接GND或悬空就行。如果你想用串口ISP下载程序才需要把BOOT0拉高到3.3V再复位。新手最容易犯的错是拿到板子后乱拨跳线帽结果板子既不运行程序也下载不了程序其实只要记住一个原则默认跳线帽不要动等学到串口ISP再研究。1.3 下载器选型ST-Link最省心串口ISP备用F103开发板的程序下载方式主要有三种ST-Link/SWD、J-Link、串口ISP。我的建议很直接首选ST-Link V2二十几块钱那种就够。接法就四根线3.3V、SWDIO、SWCLK、GND对应板上SWD排针。它比J-Link便宜比串口ISP稳定得多而且Keil原生支持得很好。串口ISP不是没用当芯片被你不小心禁用SWD引脚后它是最后的自救手段所以USB转TTL模块也建议备一个。选USB转TTL时要确认模块上有3.3V输出不能只会输出5V否则串口电平直接怼到芯片IO上运气好没烧运气不好就冒烟了。提示ST-Link V2有两种常见外形塑料壳那种和金属壳那种功能上没本质区别。要注意的是有些声称ST-Link/V2的模块芯片方案老旧Win10/11驱动兼容性差一点到手先插电脑看看设备管理器能不能识别成STM32 STLink。2. 开发环境的选择Keil、VSCode与库的取舍环境搭建能劝退一半新手不是因为难而是信息太乱。有人让你装Keil MDK有人让你用VSCode加插件还有人直接甩给你一个用CubeMX自动生成的视频。我的经验是不要在最开始追求最好的工具先求最不容易出问题的工具组合。2.1 Keil MDK最稳妥的选择C51共存要注意如果你是在校生实验室的师兄师姐大概率在用Keil MDK。网上教程、例程、答疑几乎全部基于Keil遇到问题搜关键词也最容易搜到答案。Keil MDK 5的安装本身不复杂但有一个热搜问题很典型Keil5怎么兼容C51就是8051单片机和STM32我之前也在这上面折腾过。原因在于C51和MDK是Keil的两个独立产品线装完一个再装另一个时如果安装目录不同就会出现两个独立的IDE图标写51打开一个写STM32打开另一个工程文件还互不关联。解决办法其实很简单先装C51再装MDK两个安装包在安装时指向同一个安装目录比如都装到C:\Keil_v5。安装程序到后面会提示你是否合并到这个目录选是最后打开Keil时新建工程能看到里面既有8051系列芯片也有ARM系列芯片一个IDE通吃。STM32F103芯片包也就是Device Pack也要装。Keil MDK装完后新建工程时如果找不到ST芯片就是没装pack。可以在Pack Installer里在线安装Keil.STM32F1xx_DFP网速慢就卡半天更快的办法是去ST官网或Keil官网下载对应.pack文件双击直接安装速度稳定得多。装完记得在设备列表里看到STM32F1系列这一步很多人装了但没刷新关了Keil重开就好。2.2 标准库、HAL库还是LL库我的建议接下来是比选开发环境更重要的库选型这个选择会直接影响你看资料、看代码的顺畅程度。现在主流的三种标准外设库StdPeriph通常简称为标准库、HAL库、LL库。标准库是ST官方早期出的直接面向寄存器封装了一层函数比如你要把PA1配置成推挽输出写GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP底层做了什么很透明学起来对寄存器理解帮助很大。网上老帖子、开发板教程、大学课程大部分基于标准库对新手最友好。缺点是ST已经不再更新新系列芯片不再支持。HAL库是现在ST主推的配合STM32CubeMX图形化配置能自动生成初始化代码开发效率高适合做实际项目。缺点是对新手来说封装层次厚出问题不好查根因很多人反映照着CubeMX生成也跑不起来不知道卡在哪。我的建议是入门阶段用标准库把GPIO、定时器、串口、中断这几大块通过手写初始化搞懂本质等你自己能不看例程写出点灯和串口打印后再切到HAL库也不迟。很多老工程师的经验也是标准库学底层逻辑HAL库做实际项目效率。2.3 VSCode方案什么时候值得用VSCode配EIDE插件或者PlatformIO确实能做出很舒服的STM32开发环境代码补全、Git、主题都比Keil现代。但我不建议零基础一上来就搞这套原因不是VSCode本身不好而是你一旦遇到烧录问题排查链路比Keil长得多。热搜词里VSCode里编译成功却怎么也烧录不进开发板几乎成了日经问题多数情况就是工具链里编译和烧录是分离的你只配置了编译没配置烧录器。VSCode方案什么时候值得用我觉得至少等你能在Keil里熟练完成新建工程、编译下载、单步调试之后再折腾EIDE或PlatformIO。到那时候你是带着经验去配置工具的出了问题你会知道问题出在驱动、接线还是配置上而不是一脸懵地到处搜。3. 标准库新建工程的完整步骤和第一次点灯标题叫开发板买回来了那第一步自然是让板子跑起来。我以标准库V3.5.0为例讲一遍完整的新建工程流程。这部分我尽量细写因为后面所有外设实验都是在这个工程基础上扩展的工程本身搭得不干净后面迟早返工。3.1 工程目录结构怎么搭先别直接打开Keil新建工程先在电脑上建一个清晰的文件夹结构。我习惯这样ProjectName/ ├─ User/ main.c、stm32f10x_it.c、任何你自己写的代码 ├─ Core/ core_cm3.c、system_stm32f10x.c、启动文件 ├─ StdPeriph/ 标准库的src和inc按需全拷进来也行 ├─ Output/ Keil生成的中间文件和hex可选 └─ List/ 编译列表文件可选标准库V3.5.0解压后里面有一个Libraries文件夹包含CMSIS和STM32F10x_StdPeriph_Driver。前者里的core_cm3.c、system_stm32f10x.c和启动文件就是Core的核心后者就是整个标准外设驱动库。我不建议你一开始只添加用到的外设源文件虽然Keil支持文件夹过滤但对新手来说全量添加省心编译可能慢几秒但好处是后面写I2C、SPI、CAN时不用来回加文件。3.2 Keil工程配置里最容易漏的三个地方新建工程时芯片型号选择STM32F103C8或具体的型号是否勾选Copy STARTUP会由Keil自动处理这个不用太纠结。真正容易漏的是下面这三项第一宏定义。在Options的C/C选项卡里Defines写STM32F10X_MD, USE_STDPERIPH_DRIVER。这里STM32F10X_MD对应中等密度C8T6、RBT6都属于MDUSE_STDPERIPH_DRIVER告诉编译器启用标准库驱动不写这个你调用GPIO_Init这些函数会报错或者链接不上。第二Include路径。把User、Core、StdPeriph/inc这些头文件目录全部加进去路径里不要带中文建议用相对路径。漏了任何一个目录编译会报cannot open source file xxx.h这类错误几乎都是这里的路径问题。第三Debugger配置。在Options的Debug选项卡里右侧下拉框从默认的ULINK2改成ST-Link Debugger再进Settings确认能识别到设备ID然后在Flash Download里勾选Reset and Run。不勾这个每次下载完程序板子不跑你还得手动按复位次数多了很烦。3.3 点亮板载LED看现象再往下学工程配置完成后写第一个程序。先看原理图确认板载LED接在哪个引脚不同板子差异很大常见的是PC13、PB0、PA1。我这块板是PC13低电平点亮。标准库代码大概是#include stm32f10x.h void LED_Init(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_2MHz; GPIO_Init(GPIOC, GPIO_InitStructure); } int main(void) { LED_Init(); while(1) { GPIO_ResetBits(GPIOC, GPIO_Pin_13); Delay_ms(500); GPIO_SetBits(GPIOC, GPIO_Pin_13); Delay_ms(500); } }Delay函数先用简单的软件延时就行比如for(i0;i7200000;i);粗调一下时间不要过早纠结精确定时后面学完SysTick定时器再回头优化。编译下载后看到LED闪起来你这个工程就从新建变成了可用后面所有外设实验都在这块地基上长。4. 编译成功却烧录失败几个真实排查案例VSCode里编译成功却怎么也烧录不进开发板这种问题我见过太多人问了实际上在Keil里也照样会发生。编译成功只代表源代码语法和链接正确烧录是一个完全独立的环节涉及软件配置、硬件接线、芯片状态三个方面。我把常见情况按出现频率从高到低排一下你照着这条链路排查大概率十分钟解决。4.1 芯片没供电、接线不对、驱动没装先说最不起眼的目标板没供电。很多人用ST-Link直接给板子供电但某些ST-Link的3.3V输出电流有限板子上如果接了模块、屏或者蜂鸣器电流一上去电压就掉下载器识别不到芯片。排查方法下载时看板子电源灯是不是还亮用万用表量一下3.3V引脚电压。我自己的习惯是板子用USB单独供电ST-Link只接SWDIO、SWCLK、GND三条线共地不供电这样供电问题永远不会烧到下载器。接线方面SWDIO对应PA13SWCLK对应PA14GND对GND这个别接反了。还有一类问题是杜邦线接触不良老杜邦线用久了插孔松动看起来插进去了实际没导通。怎么判断在Keil的Debug Settings里看IDCODE能不能识别出芯片序列号识别不到就先怀疑硬件连接。驱动的话设备管理器里能看到STM32 STLink就没问题。如果插上没反应装一下ST-Link驱动或者换个USB口、换根数据线试试。数据线最坑有些线只能充电不能传数据也会导致设备管理器里根本看不到设备。4.2 JTAG/SWD引脚被禁用后的自救F103的PA13、PA14、PA15、PB3、PB4这五个引脚默认复用给JTAG/SWD功能。平时你用SWD下载需要PA13/PA14这是没问题的。但有些实验比如按键接在这些引脚上你会想把这几个引脚配置成普通GPIO于是调用了GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE)或者更狠的GPIO_Remap_SWJ_Disable。区别在哪GPIO_Remap_SWJ_JTAGDisable只关闭JTAG保留SWD这种是安全的因为PA13/PA14还留着给你下载。而GPIO_Remap_SWJ_Disable是把SWJ全部关闭PA13/PA14也被释放成普通IO程序一运行SWD下载通道就没了板和Keil彻底断开报错信息五花八门常见的是No target connected或Error: Flash Download failed。自救办法三种一是用串口ISP擦除Flash拉高BOOT0复位后芯片进入ISP模式此时会忽略用户程序对引脚的配置你可以用串口工具重新烧一个正常程序进去。二是有条件的话用另外一个ST-Link的connect under reset方式也就是按住复位引脚。三是很多开发板上做了一键下载电路直接断电重新进BOOT模式本质上还是走串口ISP。提示如果登录芯片只剩串口ISP一条路注意你的USB转TTL模块RX要接板子的PA9USART1_TX、TX接PA10USART1_RX两个模块之间还要共地。接反了会出现能发不能收或完全没反应的情况。4.3 用复位时序解决连接不上的终极手段除了引脚被禁用芯片进入Sleep模式或看门狗开启后导致调试连接不稳定也会出现能查到IDCODE但一擦除就失败。这时最常用的手段是Connect under Reset也就是让调试器在芯片复位瞬间建立连接抢在用户程序把引脚复用改掉之前握手成功。Keil里设置位置在Debug Settings的Mode选项卡把Connect改成with Reset同时确认Reset类型是硬件复位Hardware Reset。某些ST-Link固件对这个功能支持得不稳定表现为偶尔能连上偶尔连不上。处理办法是降低SWD时钟频率在Settings里把Max Clock从默认的4MHz甚至更高降到1MHz或800kHz。别小看这个细节长杜邦线、芯片供电不稳、干扰强的场合降频通常立竿见影。5. 按这个顺序学外设弯路最少板子能下载程序后真正的学习才刚开始。F103外设很多但如果按下面这条路线走每一步都能复用上一步的代码不会出现学完就忘的问题。核心原则是用现象驱动学习先能看到结果再回头补原理。5.1 GPIO和按键先搞定输入输出和消抖点灯之后第二个实验应该做按键输入。F103的GPIO输入模式有上拉、下拉和浮空三种。按键电路最简单的设计是按键一端接GND另一端接MCU引脚MCU引脚配置成上拉输入。按键没按时引脚读到高电平按下时接地读到低电平。如果反过来按键接VCC就要配置下拉输入。这里基本功是消抖。机械按键按下和松开的瞬间会有几十毫秒的抖动直接判断电平会出现一次按键被识别成多次的情况。最简单的消抖是检测到电平变化后延时20ms再读一次确认。标准库读取引脚用GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)配合一个状态变量记录上一次电平就能实现按下触发一次、按住不重复触发的逻辑。这个逻辑看着简单却是后面矩阵键盘、编码器、菜单系统的基础。5.2 定时器PWM、输入捕获做超声波测距按键之后进入定时器这是F103外设中的核心难点也是热搜词里stm32定时器模式定时器捕获测频率超声波测距的集中地。我建议分三小步走。第一步是定时器做延时和定时中断。F103的TIM2、TIM3、TIM4、TIM5挂在APB1上这里有个坑当APB1预分频系数不是1时定时器的时钟又是APB1的两倍也就是72MHz。很多人初始化定时器后发现时间完全不对多半就是没有搞清楚这个倍频关系。标准库里的RCC_APB1PeriphClockCmd只负责开时钟具体频率得看SystemInit里AHB、APB的预分频配置。第二步是PWM输出。用定时器某个通道输出PWM控制LED亮度做出呼吸灯效果。PWM的两个关键参数是频率和占空比通过TIM_TimeBaseStructure里的Prescaler和Period来算PWM频率 定时器时钟 / (Prescaler 1) / (Period 1)。比如72MHz想要1kHz的PWM可以56分频、Period1280算出来约1kHz。这个计算是后面控制舵机、电机调速的看家本事。第三步是输入捕获。用定时器捕获外部信号高电平的脉宽最经典的应用是HC-SR04超声波测距Trig引脚给超过10us的高电平触发Echo引脚会返回一个脉宽对应距离的高电平距离 脉宽时间 × 340米每秒 ÷ 2。配合定时器输入捕获能精确测出Echo的高电平时间。这里要注意溢出处理如果测距周期太长导致CNT溢出多次就得在捕获中断里累加溢出次数否则距离一大就乱跳。测频率同理在两个捕获事件之间统计CNT计数差和溢出次数频率计算就非常准。5.3 串口UART调试输出的命脉GPIO、定时器都只是玩单个引脚串口才是你建立芯片和外界沟通概念的起点。F103的USART1固定引脚是PA9TX、PA10RX也可以重映射到PB6/PB7。USART2是PA2/PA3USART3是PB10/PB11。重映射时要打开AFIO时钟比如RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE)再调用GPIO_PinRemapConfig。新手拿到原理图后第一件事就是查引脚下标主板资料里stm32 uart管脚定义被搜得多就是这个原因。串口值得专门花时间搞透两件事一是波特率计算USART的波特率由APB时钟和USARTDIV分频决定标准库里只要设置USART_InitStructure.USART_BaudRate即可不用手算分频系数。二是中断接收一定要学会用USART_GetITStatus和USART_ReceiveData在中断里收字节配合环形缓冲区这是后面和ESP8266、蓝牙模块、K210通讯的基础。K210做视觉识别后通过串口把识别结果发给STM32控制电机、做报站功能就是非常典型的使用场景。5.4 SPI/I2C和ILI9341读ID的怪现象串口搞定后再学SPI和I2C就比较轻松了。液晶屏ILI9341是很多开发板的标配通过SPI或并口驱动。很多人拿到屏的第一个实验是读ID也就是发送命令后读回LCD控制器的识别码。可热搜里ILI9341读ID是a1a1的情况特别常见屏幕本身能用但读出来的值却是0xA1A1A1。我后来排查明白这个值其实是数据线悬空时读到的高电平0xA1就是重复的10100001说明MCU和LCD之间的MISO或者数据线没有真正通。原因通常有几个一是屏幕端子座接触不良二是代码里读ID的命令寄存器地址不对ILI9341正确的读ID命令是0xD3读回三个字节而如果这个屏实际用的是ST7789或者ILI9488读ID命令就会变成别的返回的数据自然不对三是时序不满足读命令时RS/DC引脚电平不对。排查优先级建议先查接线再用逻辑分析仪看MISO上到底有没有数据最后再怀疑屏幕驱动芯片型号。这给我们一个教训买板子送的屏幕未必和例程代码里的型号完全一致屏幕丝印上通常有型号标识核对一下比乱改初始化代码更高效。5.5 CAN通信连不上的常见原因如果你学完上面这些还想往汽车电子、工业控制方向走CAN通信是避不开的。F103的CAN控制器挂在不同总线上需要外接CAN收发器芯片如TJA1050、MCP2551才能连接到总线。热搜词stm32 can通信突然连不上基本覆盖了新手最常见的几个原因。第一是接线。CAN是差分信号CAN_H要接到另一个节点的CAN_HCAN_L接CAN_L不能交叉。交叉后总线电平异常通信完全建立不起来。第二是终端电阻。CAN总线两端各需要120欧姆终端电阻如果只有两个节点通常在两个节点上分别并一个120欧——很多开发板上有跳线帽默认已经接入拆掉就会出问题。第三是波特率。两个节点必须完全一致F103的CAN波特率通过分频和同步跳转宽度来配置最好直接用同一个库函数参数初始化。第四是ACK错误如果总线上只有你一个节点在发而CAN协议要求必须有另一个节点回应ACK否则发送节点会进Bus-Off状态表现为还能发但发不出去。查CAN错误寄存器CAN_ESR能看到具体的错误码这是最直接的定位手段。突然连不上优先检查是不是有人动了终端电阻跳线或者节点掉电。6. 调试三板斧和几个典型踩坑现场跑通外设之后学习的重心就变成了怎么高效地定位问题。F103开发阶段的调试手段其实就三板斧串口打印、查看寄存器、按键/指示灯状态别把问题想复杂了。6.1 printf重定向把串口变成调试窗口对新手来说最直观的调试方式是把任何变量的值通过串口打出来。标准库工程里重定向printf很简单在某个源文件里实现int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, (uint8_t)ch); return ch; }前提是USART1已经初始化好并且包含stdio.h。之后你就可以在各种中断、回调里用printf(cnt%d\r\n, cnt)。我自己的习惯是把关键状态都打出来比如定时器捕获值、按键状态变化、CAN错误寄存器值。看到输出就知道程序跑到哪里了。比单步调试效率高得多因为很多问题是时间相关的Keil的仿真器单步反而会掩盖问题。6.2 系统架构、APB分频和时钟配置很多为什么我的定时器不准为什么我的串口乱码之类的问题最后都能回溯到系统时钟配置。F103默认用外部8MHz晶振HSE经过PLL 9倍频得到72MHz系统时钟。从系统时钟出来AHB预分频器、APB1预分频器、APB2预分频器依次分配。APB1最高36MHzAPB2最高72MHz。USART1挂APB2USART2/3挂APB1所以如果APB1设成36MHz而USART2按72MHz配置波特率必然翻倍错乱。我在实际调试中遇到过一次很坑的情况板子上一开始用的内部RC振荡器没有外部晶振HSE起振失败后系统自动切到HSI主频只有8MHz而代码里所有外设频率都按72MHz配置定时器和串口全乱套。检查的方法很简单用RCC_GetSYSCLKSource看当前时钟源或者写个GPIO翻转程序用频率计测实际主频。6.3 delay卡死SysTick和优化等级delay卡死这个话题在论坛里几乎每周都有人问。症状是程序跑着跑着一进延时函数就出不来了或者刚上电就死在延时里。常见原因有两种。一种是SysTick被别的地方抢占或者优先级配置不对。SysTick中断优先级如果设得太低而某个其他中断又疯狂触发SysTick就一直没有机会执行延时时间疯狂拉长看起来就像卡死了。这种现象在处理串口接收或者高频外部中断时特别容易复现。解决办法是把SysTick的抢占优先级设成最高比如NVIC_PriorityGroupConfig里把SysTick放在PreemptionPriority为0的组。另一种是编译器优化导致的。FeilandLevel选-O2甚至-O3时编译器可能把一段没有副作用的空循环整个优化掉Delay直接变成瞬间跳过你的时序全部错乱。你以为是卡死其实是执行太快。这种问题排查起来非常迷惑因为代码看着没问题。我后来学乖了软件延时函数里的循环变量用volatile修饰或者干脆全部换成定时器延时从根上避免优化问题。还有一个很容易被忽略的场景开了看门狗忘记喂。很多开发板例程外面包了一层喂狗逻辑但你复制代码时没把喂狗部分一起拷过来看门狗一到期就复位程序反复重启。表现就是现象卡住、LED乱闪、串口偶尔吐几个乱码。最后多说一点我拿到这块F103开发板之后前三天走了不少弯路比如工程缺宏定义、ST-Link接线接反、ULINK和ST-Link混淆最搞笑的是一开始USB转TTL选了个5V输出的模块差点把串口烧了。现在回头看很多坑只需要在动手前多确认一遍芯片型号、第一脚、下载接线、时钟配置这四个基础点就能避免一大半。接下来我的计划是先过GPIO、定时器、串口这三关再做CAN通讯最后试着用F103驱动一个超声波传感器和一块小屏幕攒一个带温湿度和测距的小面板。板子已经买回来了学习路线也算理清了后面每一步遇到的新坑我都会继续记下来更新。希望这篇内容也能帮你把手里的stm32开发板真正跑起来。