STM32F103开发板入门实战:环境搭建、外设解析与避坑指南 1. 拿到板子先别急着点灯STM32F103开发板入门全景拆解STM32F103这块板子大概是国内嵌入式爱好者手里保有量最大的一块开发板了。不管你是电子专业的学生、刚转行嵌入式的工程师还是单纯想搞点硬件小玩意的爱好者大概率第一块ARM Cortex-M3的板子就是它。我前后买过不下五块F103的开发板从最早的“最小系统板”到后来带各种外设的“学习板”踩过的坑比点亮过的LED还多。这篇文章不打算写成又一份“从零开始学STM32”的教科书式教程而是想把我这些年用F103做项目的实际经验、选型思路、踩坑记录和排查方法尽可能完整地分享出来。如果你刚拿到板子或者正在犹豫要不要入手一块F103来入门那这篇内容应该能帮你少走不少弯路。STM32F103是意法半导体ST基于ARM Cortex-M3内核的32位微控制器主频最高72MHz片上Flash从16KB到1MB不等SRAM从6KB到96KB封装形式多样从LQFP48到LQFP144都有。市面上最常见的开发板一般搭载STM32F103C8T664KB Flash、20KB SRAM、LQFP48封装或者STM32F103ZET6512KB Flash、64KB SRAM、LQFP144封装。C8T6的板子便宜、小巧适合入门和快速验证ZET6的板子外设丰富、引脚多适合做完整项目。两者在编程模型上完全一致学会一个另一个自然就会了。这块芯片能做什么简单说凡是需要“感知—处理—控制”这个链路的场景它基本都能胜任。读取传感器数据、驱动电机、控制继电器、做串口通信、实现USB设备、跑简单的RTOS、做数据采集和上报这些都不在话下。它不适合做什么跑Linux、做图像处理、搞复杂的人机交互界面这些不是它的强项该上Cortex-A系列或者带GPU的芯片就得上别硬撑。适合谁来学我觉得有三类人最适合从F103入手第一类是完全没接触过单片机的初学者F103的资料丰富程度在所有ARM芯片里是断层领先的遇到问题几乎都能搜到答案第二类是从51单片机过渡过来的开发者51的寄存器操作思维可以平滑迁移但需要重新理解时钟树、中断优先级、外设复用这些概念第三类是需要快速做产品原型的工程师F103的生态成熟各种模块和代码模板拿来就能用开发效率很高。2. 开发环境搭建从Keil到VS Code的选型与配置2.1 Keil MDK还是VS Code两条路线的取舍开发STM32F103绕不开的第一个问题就是用哪个IDE。目前主流的选择有两个Keil MDK和VS Code 命令行工具链。我两种都用过很长时间各有各的适用场景。Keil MDK是国内STM32开发者最熟悉的工具优势在于上手快、调试方便、芯片包安装简单。你装好MDK之后通过Pack Installer下载STM32F1系列的Device Family Pack新建工程时选好芯片型号基本就能直接编译下载了。它的调试器集成度很高配合ST-Link或者J-Link打断点、看寄存器、看内存都是一键操作。缺点是编辑器体验一般代码补全和跳转功能比较弱而且MDK是收费软件虽然社区版有32KB代码限制但对于F103C8T6的64KB Flash来说稍微写大一点的工程就可能超限。VS Code ARM GCC工具链是近几年越来越流行的方案。你需要安装arm-none-eabi-gcc、OpenOCD或者ST-Link的工具、Make或者CMake然后配置VS Code的tasks.json和launch.json来实现编译和调试。这套方案的优势是编辑器体验好、工具链免费无限制、跨平台支持好缺点是配置门槛高新手容易在环境搭建阶段就卡住。我的建议是如果你是完全的新手先用Keil MDK把第一个工程跑起来把注意力放在理解芯片本身而不是折腾环境上。等你对STM32的开发流程比较熟悉了再考虑迁移到VS Code。如果你已经有Linux下的开发经验或者对命令行工具比较熟悉那直接上VS Code GCC也完全可以。2.2 Keil5兼容C51和STM32的安装要点很多人电脑上同时装了Keil C51和Keil MDK这两个版本如果安装顺序不对会出现互相覆盖的问题。正确的做法是先装C51再装MDK而且两个装在不同的目录下。装完之后需要用Keil的注册工具分别对两个版本进行注册。如果你只学STM32那直接装MDK就行不用折腾C51。安装MDK的时候有几个细节要注意。第一安装路径不要有中文和空格否则后面编译时可能出现莫名其妙的错误。第二Pack Installer下载芯片包时如果网络不稳定可以手动下载对应的.pack文件然后离线安装。STM32F1系列的包名字一般是Keil.STM32F1xx_DFP.x.x.x.pack下载下来双击就能装。第三装完MDK之后记得安装ST-Link的驱动否则调试器识别不到。2.3 VS Code里编译成功却烧录不进去的排查思路这个问题在热词里出现了说明遇到的人不少。VS Code里编译成功说明工具链没问题烧录不进去通常是以下几个原因第一烧录工具配置不对。VS Code本身不负责烧录你需要通过OpenOCD、ST-Link Utility或者pyocd等工具来烧录。检查你的launch.json或者tasks.json里配置的烧录命令是否正确接口是SWD还是JTAG目标芯片型号是否匹配。第二ST-Link驱动问题。在Windows上ST-Link需要正确的驱动才能被识别。如果设备管理器里ST-Link显示为未知设备需要重新安装驱动。有时候ST-Link的固件版本太老也会导致问题可以用ST-Link Utility来升级固件。第三芯片被读保护了。如果你之前烧录过程序并开启了读保护再次烧录时需要先解除保护。用ST-Link Utility连接芯片在Target菜单里选择Option Bytes把Read Out Protection改成Disabled然后应用。第四BOOT引脚状态不对。STM32F103的BOOT0和BOOT1引脚决定了启动模式。如果BOOT0接高电平芯片会从系统存储器启动也就是进入Bootloader模式这时候你烧录的程序不会运行。正常运行时BOOT0应该接低电平。有些开发板上有跳线帽来切换BOOT0的状态检查一下是不是跳错了。第五复位电路问题。有些开发板的复位电路设计得不太好导致ST-Link无法正常复位芯片。可以尝试在烧录时手动按住复位键点击烧录后再松开。3. 核心外设与功能模块的实操解析3.1 GPIO与按键电路设计看似简单却容易翻车GPIO是STM32最基础的外设但也是容易出问题的地方。F103的GPIO有8种模式输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、开漏复用、推挽复用。新手最容易搞混的是什么时候用上拉输入什么时候用浮空输入。按键电路的设计就是一个典型例子。如果你用按键一端接GPIO另一端接GND那么GPIO需要配置为上拉输入这样按键未按下时引脚被内部上拉电阻拉到高电平按下时被拉到低电平。如果你用按键一端接GPIO另一端接VCC那GPIO需要配置为下拉输入。如果配置成浮空输入按键未按下时引脚电平不确定会出现随机触发的问题。还有一个坑是按键抖动。机械按键在按下和松开的瞬间会产生几十毫秒的抖动如果不做处理一次按键会被识别成多次。解决方法有两种硬件上在按键两端并联一个0.1uF的电容软件上做延时消抖。软件消抖的典型做法是检测到按键状态变化后延时20ms再检测一次如果状态一致才确认按键有效。注意F103的GPIO在配置为输出时默认的输出速度是2MHz。如果你需要驱动高速信号比如SPI的时钟线需要把输出速度配置为50MHz。但速度越高电磁干扰也越大所以不要无脑选最高速度根据实际需求来。3.2 定时器模式详解从延时到PWM到输入捕获STM32F103的定时器是它最强大的外设之一。F103C8T6有3个通用定时器TIM2、TIM3、TIM4和1个高级定时器TIM1。ZET6则更多。定时器的功能包括基本定时、PWM输出、输入捕获、输出比较、编码器接口等。基本定时功能最常用的场景是替代delay函数做精确延时。用SysTick做延时是阻塞式的延时期间CPU什么都干不了。用定时器中断做延时则可以非阻塞在延时期间CPU可以处理其他任务。配置定时器中断的步骤是使能定时器时钟、配置预分频器和自动重装载值、使能更新中断、配置NVIC、编写中断服务函数。PWM输出是控制电机速度、LED亮度的常用手段。F103的定时器每个通道都可以独立输出PWM频率由预分频器和自动重装载值决定占空比由比较寄存器决定。计算PWM频率的公式是PWM频率 72MHz / ((预分频器1) * (自动重装载值1))。比如你想要1kHz的PWM预分频器设为71自动重装载值设为999那么频率就是72MHz / (72 * 1000) 1kHz。输入捕获用来测量外部信号的频率和脉宽。比如超声波测距模块的Echo引脚输出的高电平脉宽就代表了距离用输入捕获可以精确测量这个脉宽。配置输入捕获时需要注意触发边沿的选择测频率一般用上升沿触发测脉宽需要先上升沿后下降沿。3.3 串口通信与USB虚拟串口串口是STM32调试和通信最常用的接口。F103一般有3个USARTC8T6有3个ZET6有5个。配置串口的步骤是使能USART和GPIO时钟、配置GPIO为复用推挽输出TX和浮空输入RX、配置USART的波特率、数据位、停止位、校验位、使能USART、如果需要中断则配置NVIC。USB虚拟串口是F103的一个亮点功能。F103自带USB 2.0全速设备控制器可以通过软件实现CDCCommunication Device Class设备在电脑上虚拟出一个串口。相比传统的UART转USB芯片比如CH340USB虚拟串口不需要额外的硬件芯片直接通过USB线就能通信。配置USB虚拟串口需要用ST的USB库代码量比较大但ST提供了现成的例程改改就能用。提示USB虚拟串口在Windows上需要安装驱动ST提供了VCP驱动。在Linux和macOS上一般免驱。如果你在Windows上插上USB后设备管理器里出现黄色感叹号说明驱动没装好。3.4 超声波测距模块的完整实现超声波测距是F103入门项目里非常经典的一个。HC-SR04模块有四个引脚VCC、GND、Trig、Echo。工作原理是给Trig引脚一个至少10微秒的高电平脉冲模块发射超声波然后Echo引脚会输出一个高电平高电平的持续时间就是超声波从发射到遇到障碍物反射回来的时间。距离 高电平时间 * 声速 / 2。用F103实现超声波测距的步骤配置一个GPIO为推挽输出Trig配置另一个GPIO为输入Echo或者用定时器的输入捕获功能来测量Echo的高电平时间。如果用GPIO轮询的方式精度受限于程序循环的速度用输入捕获则精度高很多可以精确到微秒级。实际使用中要注意几个问题第一超声波模块的测量角度比较窄大约15度所以安装时要注意方向。第二测量距离受温度和湿度影响声速在空气中大约是340m/s但温度每升高1度声速增加约0.6m/s。如果要做高精度测量需要加温度补偿。第三超声波模块之间会互相干扰如果同时用多个模块需要分时触发。4. 常见问题排查与避坑经验实录4.1 程序烧录失败与Flash报错热词里有一条“load d:\stm32 prohect\2-1 stm32工程模板\objects\project.axf error: fla”这是Keil编译后烧录时常见的Flash下载失败错误。这个错误的原因通常有几种第一Flash算法选择错误。在Keil的Options for Target - Debug - Settings - Flash Download里需要选择正确的Flash算法。STM32F103C8T6的Flash大小是64KB应该选STM32F10x Med-density Flash。如果选成了High-density或者Low-density就会报错。第二芯片被写保护了。用ST-Link Utility连接芯片查看Option Bytes里的写保护状态如果被保护了先解除保护再烧录。第三ST-Link连接不稳定。检查SWDIO和SWCLK的接线是否牢固线缆是否过长。SWD接口的线最好不要超过20厘米否则信号质量会下降。第四芯片供电不足。有些开发板通过ST-Link供电如果板子上外设比较多电流可能不够。可以尝试给开发板单独供电。4.2 delay函数卡死的几种原因delay函数卡死是新手经常遇到的问题。F103的delay函数一般有两种实现方式一种是基于SysTick的阻塞式延时另一种是基于定时器中断的非阻塞式延时。卡死的原因通常有第一SysTick配置错误。SysTick的时钟源可以是HCLK的1/8或者HCLK如果配置错了延时时间会不对甚至看起来像卡死。检查SysTick-CTRL寄存器的CLKSOURCE位。第二中断优先级冲突。如果delay函数依赖中断而中断被更高优先级的任务屏蔽了delay就会一直等下去。检查NVIC的优先级配置。第三在中断服务函数里调用了delay。如果在中断里调用阻塞式delay会导致中断响应时间变长严重时会影响其他中断的处理。中断服务函数里应该尽量避免使用阻塞式延时。4.3 JTAG禁用与引脚复用冲突STM32F103的JTAG引脚PA13、PA14、PA15、PB3、PB4在默认状态下是作为调试接口使用的。如果你想把它们当作普通GPIO来用需要先禁用JTAG功能。禁用JTAG的代码是RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);这样禁用了JTAG但保留了SWD你仍然可以用SWD来调试和烧录。如果连SWD也禁用了那就只能用串口或者USB来烧录了比较麻烦。注意禁用JTAG之后PA15、PB3、PB4就可以当作普通GPIO使用了。但PA13和PA14是SWD的引脚如果连SWD都禁用了这两个引脚才能当普通GPIO用。一般情况下不建议禁用SWD否则调试会很不方便。4.4 常见问题速查表问题现象可能原因排查方法烧录时报Flash下载失败Flash算法选错、芯片写保护、ST-Link连接不稳检查Flash算法、解除写保护、检查接线程序运行后没反应BOOT引脚状态不对、复位电路问题、时钟配置错误检查BOOT0/BOOT1、检查复位电路、检查时钟配置delay函数卡死SysTick配置错误、中断优先级冲突、中断里调用了delay检查SysTick配置、检查NVIC优先级、避免中断里用阻塞延时串口收不到数据波特率不匹配、TX/RX接反、串口助手配置错误检查波特率、交换TX/RX、检查串口助手设置USB虚拟串口识别不了驱动没装、USB时钟配置错误、描述符有问题安装VCP驱动、检查USB时钟是否为48MHz、检查描述符按键触发不稳定没有消抖、GPIO模式配置错误加消抖处理、检查GPIO输入模式PWM没有输出定时器配置错误、GPIO复用没使能、通道配置错误检查定时器配置、检查GPIO复用配置、检查通道配置5. 从入门到进阶项目实战与学习路线5.1 适合练手的STM32项目推荐学STM32最好的方式就是做项目。光看教程不动手看完就忘。我推荐几个难度递进的项目每个都能覆盖不同的知识点第一个项目是“按键控制LED”。这个项目虽然简单但涵盖了GPIO输入输出、按键消抖、中断处理等基础知识点。做完这个项目你对GPIO的理解就差不多了。第二个项目是“串口命令行”。通过串口接收电脑发来的命令控制LED的开关、读取按键状态、输出系统信息。这个项目涵盖了串口通信、字符串处理、命令解析等知识点。第三个项目是“超声波测距仪”。用HC-SR04模块测量距离通过串口或者OLED屏幕显示结果。这个项目涵盖了定时器输入捕获、GPIO控制、数据显示等知识点。第四个项目是“PWM调光台灯”。用PWM控制LED的亮度通过按键或者电位器调节亮度。这个项目涵盖了定时器PWM输出、ADC采样、按键处理等知识点。第五个项目是“基于STM32的毕业设计”。如果你是在校学生毕业设计是一个很好的练手机会。常见的题目有“基于STM32的智能家居控制系统”、“基于STM32的环境监测系统”、“基于STM32的电机控制系统”等。这些项目综合性强能锻炼完整的系统设计能力。5.2 标准库还是HAL库选型建议STM32的软件开发库主要有两种标准外设库Standard Peripheral Library和HAL库Hardware Abstraction Layer。标准库是ST早期推出的代码简洁、执行效率高、寄存器操作直观但ST已经不再维护了。HAL库是ST现在主推的跨系列兼容性好、代码可移植性强但代码量大、执行效率相对低一些。对于F103来说我建议新手先用标准库。原因有三第一标准库的代码更接近寄存器操作有助于理解STM32的底层原理第二标准库的例程和教程非常多遇到问题容易找到答案第三标准库编译出来的代码体积小对于Flash只有64KB的C8T6来说比较友好。如果你以后要迁移到F4、F7或者H7系列那HAL库是更好的选择因为ST的新芯片只支持HAL库和LL库。但就F103入门而言标准库足够了。5.3 新建标准库工程的完整步骤新建一个标准库工程是每个STM32开发者都要掌握的技能。虽然网上有很多现成的工程模板可以下载但我建议你自己从头建一次这样能理解工程的结构和各个文件的作用。第一步下载STM32F10x标准外设库。ST的官网可以下载到文件名一般是STM32F10x_StdPeriph_Lib_V3.5.0.zip。解压后里面有Libraries、Project、Utilities等文件夹。第二步在Keil里新建工程选择芯片型号STM32F103C8或者你实际使用的型号。第三步把标准库的文件复制到工程目录下。需要复制的文件包括启动文件startup_stm32f10x_md.smd代表中等容量、系统文件system_stm32f10x.c、内核文件core_cm3.c和core_cm3.h、外设驱动文件stm32f10x_gpio.c、stm32f10x_rcc.c等按需添加、以及对应的头文件。第四步在Keil里添加文件分组把复制过来的文件添加到对应的分组里。第五步配置头文件包含路径。在Options for Target - C/C - Include Paths里添加标准库的头文件路径。第六步在stm32f10x.h里配置芯片型号。找到“#define STM32F10X_MD”这一行取消注释如果是C8T6的话。第七步编写main函数编译下载。提示新建工程时最容易出错的地方是启动文件选错。STM32F103C8T6的Flash是64KB属于中等容量应该选startup_stm32f10x_md.s。如果选成了hd高容量或者ld低容量编译会报错。5.4 后续学习路线建议学完F103的基础外设之后下一步可以往几个方向发展如果你对实时操作系统感兴趣可以学FreeRTOS或者RT-Thread。F103的RAM虽然不大但跑一个精简的RTOS还是没问题的。RTOS能帮你更好地组织多任务程序提高代码的可维护性。如果你对通信协议感兴趣可以学Modbus、CAN、Ethernet等。F103自带CAN控制器配合CAN收发器就能实现CAN通信。Modbus可以通过串口或者以太网实现是工业控制领域常用的协议。如果你对电机控制感兴趣可以学BLDC电机控制、步进电机控制等。F103的高级定时器TIM1支持互补PWM输出和死区控制非常适合驱动电机。如果你对USB感兴趣可以学USB HID、USB MSC、USB CDC等。F103的USB功能比较基础但实现键盘、鼠标、虚拟串口、U盘这些设备还是绰绰有余的。6. 硬件选型与开发板购买建议6.1 最小系统板还是学习板市面上的F103开发板大致分两类最小系统板和综合学习板。最小系统板一般只有芯片、晶振、复位电路、BOOT跳线和几个LED价格在10到20元之间。综合学习板则集成了各种外设比如OLED屏幕、按键、蜂鸣器、温湿度传感器、红外接收头、SD卡座等价格在50到150元之间。我的建议是如果你完全零基础买一块综合学习板因为上面集成的外设可以让你快速做各种实验不用额外接线。如果你已经有一定基础或者想自己搭配外设那最小系统板加面包板加杜邦线的组合更灵活也更接近实际项目的开发方式。6.2 ST-Link还是J-Link调试器方面ST-Link是性价比最高的选择。原厂的ST-Link V2价格在100元左右淘宝上的兼容版只要20到30元功能基本一样。J-Link功能更强支持更多的芯片和更快的下载速度但价格也贵不少。对于F103来说ST-Link完全够用了。买ST-Link的时候要注意有些便宜的兼容版用的是国产芯片固件升级可能会出问题。如果预算允许建议买原厂或者口碑好的兼容版。6.3 常见开发板对比开发板类型代表型号优点缺点适合人群最小系统板F103C8T6核心板便宜、小巧、灵活外设少、需要自己接线有基础的开发者综合学习板正点原子Mini板外设丰富、教程完善价格较高、体积较大零基础初学者综合学习板野火指南者资料丰富、社区活跃价格较高零基础初学者综合学习板普中A2价格便宜、外设多教程质量参差不齐预算有限的初学者我个人用过的板子里正点原子和野火的教程质量是最好的代码风格也比较规范。普中的板子便宜但教程和代码质量一般适合预算有限的情况。7. 调试技巧与效率提升工具7.1 ST-Link Utility的妙用ST-Link Utility是ST官方提供的一个工具功能比Keil自带的下载功能强大很多。它可以用来烧录Hex文件、读取芯片内存、擦除Flash、配置Option Bytes、升级ST-Link固件。当你遇到Keil烧录失败的时候用ST-Link Utility往往能解决问题。比如芯片被写保护了Keil烧录会报错但用ST-Link Utility连接芯片后在Target - Option Bytes里把Read Out Protection改成Disabled就能解除保护。再比如你想把程序发给别人但对方没有源码你可以用ST-Link Utility把芯片里的程序读出来保存成Hex文件。7.2 串口调试助手的选用串口调试助手是调试STM32的必备工具。常用的有SSCOM、XCOM、友善串口调试助手等。我一般用SSCOM功能全、稳定性好。选择串口助手时要注意几点支持HEX显示和发送、支持定时发送、支持多条发送指令、支持数据保存。调试串口时最常见的坑是波特率不匹配。STM32的串口波特率是由时钟频率和分频系数计算出来的如果时钟配置不对实际波特率会和设定值有偏差。比如你设了115200但实际波特率是9600那收到的就是乱码。用示波器或者逻辑分析仪测一下TX引脚上的波形就能算出实际波特率。7.3 逻辑分析仪排查时序问题的利器逻辑分析仪是调试数字通信协议的利器。几十块钱的USB逻辑分析仪配合开源软件PulseView就能抓取SPI、I2C、UART、CAN等协议的数据。当你遇到通信不通的问题时用逻辑分析仪抓一下波形看看时钟、数据、片选信号是否正常往往能快速定位问题。比如你调试SPI通信时发现数据不对用逻辑分析仪抓一下可能发现是时钟极性或者相位配置错了或者片选信号没有正确拉低。这些问题用万用表是看不出来的只有逻辑分析仪才能直观地展示时序关系。8. 个人实操体会与建议我在用F103做项目的过程中最大的体会是不要怕犯错但要学会从错误中总结。嵌入式开发是一个实践性极强的领域很多问题只有亲手做过才会遇到也只有亲手解决过才能真正掌握。另一个体会是文档和手册是最好的老师。STM32F103的参考手册Reference Manual和的数据手册Datasheet加起来有一千多页虽然不可能全部看完但遇到问题时去查对应的章节往往能找到最权威的答案。网上的教程和博客质量参差不齐有些甚至是错的但官方手册不会骗你。还有一点代码规范很重要。我见过很多初学者的代码所有逻辑都写在main函数里变量命名随意没有注释。这样的代码在项目小的时候还能维护一旦项目变大就完全失控了。建议从开始就养成好习惯模块化编程、变量命名有意义、关键逻辑加注释、使用版本控制工具管理代码。最后分享一个小技巧如果你在调试时怀疑是硬件问题先写一个最简单的程序来验证硬件是否正常。比如怀疑LED不亮是GPIO配置问题那就写一个只控制LED闪烁的程序排除其他代码的干扰。这种“最小化验证”的思路能帮你快速定位问题避免在复杂的代码里大海捞针。