1. 拿到板子先别急着点灯STM32F103开发板入门全景拆解STM32F103开发板到手的那一刻很多人第一反应是找根USB线插上电脑看看能不能跑起来。这个冲动我太理解了当年我也是这么干的。但如果你真想把这块板子学明白而不是停留在“烧个例程看个亮”的阶段那第一件事其实是把心态从“玩”调整到“建体系”。STM32F103这块芯片放在2024年来看确实不算新但它依然是嵌入式入门最扎实的跳板之一——资料多、坑多、能踩的地方多踩完了你就真会了。这篇文章面向的是刚拿到STM32F103开发板、想系统入门的朋友也适合那些之前断断续续学过、但一直没串起来的半新手。我会从拿到板子后的第一轮硬件检查开始一路讲到工程模板搭建、时钟配置、GPIO操作、定时器使用、串口通信再到调试烧录中那些让人抓狂的问题排查。核心关键词就三个STM32、STM32F103、开发板。你手里那块板子不管是普中、正点原子、野火还是其他家的底层逻辑都一样区别只在引脚分配和外设布局。先说一个很多人忽略的事实STM32F103开发板买回来之后真正决定你学习效率的不是板子本身多高级而是你有没有一套稳定的工程模板和清晰的调试链路。我见过太多人卡在“VS Code里编译成功却烧录不进去”这种问题上一卡就是两三天热情直接磨没了。所以这篇文章的重点是把入门路径上的关键节点全部拆开让你少走弯路。2. 硬件到手后的第一轮检查与工具链搭建2.1 开发板开箱检查别让硬件问题浪费你三天时间拿到STM32F103开发板之后先别插电。我习惯先做一轮纯物理检查这一步花不了五分钟但能帮你排除掉很多低级问题。首先看板子上的电源指示灯和用户LED位置确认跳线帽是否按厂家默认状态插好。很多开发板出厂时BOOT0和BOOT1的跳线帽是悬空的或者插在错误的位置导致你后面烧录时死活连不上。然后检查USB接口类型。STM32F103开发板常见的有两种USB口一种是直接给MCU供电和通信的USB Device口另一种是给板载USB转串口芯片用的。你要分清楚哪个口是烧录用的哪个口是供电用的。有些板子两个口都能供电但只有一个能烧录。我建议你先把板子上的丝印拍张照对照厂家提供的原理图确认一遍。接下来是晶振。STM32F103通常有两个晶振一个8MHz的高速晶振HSE一个32.768kHz的低速晶振LSE。你不需要用示波器去测但可以用肉眼看一下晶振是否焊接牢固有没有明显虚焊。我遇到过一块板子8MHz晶振的一个引脚虚焊结果程序跑起来时钟不对串口波特率全是乱码查了半天才定位到硬件问题。最后检查跳线帽和排针。如果你买的是带扩展排针的版本确认排针没有弯针或短路。有些廉价开发板的排针焊接质量堪忧相邻引脚之间可能有锡渣残留上电后直接短路。用放大镜或者手机微距镜头扫一遍心里有底。提示开箱检查阶段如果发现任何硬件异常第一时间联系卖家换货不要试图自己修。STM32F103开发板的价格通常不高但自己修板子浪费的时间成本远超换货成本。2.2 工具链选型Keil、STM32CubeIDE还是VS Code工具链的选择直接决定你后续的学习体验。目前STM32F103开发的主流工具链有三条路线Keil MDK、STM32CubeIDE、VS Code 开源工具链。我分别说一下各自的适用场景和坑点。Keil MDK是国内最流行的STM32开发环境资料最多教程最全。它的优势在于调试器集成度高ST-Link、J-Link都能直接识别断点调试体验流畅。但Keil有两个问题一是需要安装器件包Device Family PackSTM32F1系列的包大概几百MB下载速度取决于你的网络环境二是Keil5默认不兼容C51如果你之前装过Keil C51需要额外配置才能共存。我个人的做法是Keil5和Keil C51装在不同目录通过注册表工具切换但更推荐直接用Keil5的STM32版本C51用单独的安装包。STM32CubeIDE是ST官方推出的免费IDE基于Eclipse。它的最大优势是集成了STM32CubeMX配置工具图形化配置引脚、时钟、外设自动生成初始化代码。对于新手来说CubeIDE能帮你快速理解STM32的时钟树和外设配置逻辑。但CubeIDE的代码编辑体验不如VS Code编译速度也偏慢而且生成的代码框架比较臃肿不适合追求极致精简的场合。VS Code 开源工具链是进阶玩家的选择。你需要自己安装arm-none-eabi-gcc、OpenOCD、Make或CMake配置tasks.json和launch.json。这套方案灵活度最高代码编辑体验最好但配置门槛也最高。我见过太多人卡在“VS Code里编译成功却烧录不进去”这个问题上根本原因是OpenOCD的配置文件没写对或者ST-Link的驱动被其他软件占用了。我的建议是新手先用Keil MDK把基础外设跑通同时用STM32CubeMX辅助理解时钟和外设配置等你有了一定基础之后再迁移到VS Code 开源工具链。这样你既不会一上来就被环境配置劝退又能在后期获得更好的开发体验。2.3 ST-Link驱动与烧录环境配置不管你用哪个IDE烧录环节都绕不开ST-Link。STM32F103开发板通常板载ST-Link或者预留了SWD接口。板载ST-Link的板子最省事插上USB就能识别预留SWD接口的板子需要你外接一个ST-Link模块。ST-Link驱动安装有个坑Windows 10和Windows 11会自动安装一个通用驱动但这个驱动有时候和Keil或CubeIDE不兼容表现为设备管理器里能看到ST-Link但IDE里就是连不上。解决办法是手动安装ST官方提供的驱动或者用Zadig工具把ST-Link的USB驱动替换成WinUSB。我实测下来ST官方驱动在大多数情况下更稳定。烧录配置方面Keil里需要在Options for Target - Debug里选择ST-Link Debugger然后在Settings里确认SWD模式、时钟频率。时钟频率不要设太高1.8MHz或4MHz就够了设太高反而容易连接失败。CubeIDE里类似在Run - Debug Configurations里选择ST-Link确认接口是SWD。注意如果你同时装了Keil和CubeIDE两个软件可能会争抢ST-Link的控制权。烧录时确保另一个软件已经关闭否则会出现“No ST-Link detected”的错误。3. 工程模板搭建从零开始建一个能跑的STM32F103工程3.1 标准库还是HAL库新手该怎么选STM32F103的代码库主要有两种标准外设库Standard Peripheral Library和HAL库Hardware Abstraction Layer。标准库是ST早期推出的代码精简、执行效率高、寄存器操作直观但ST已经停止维护。HAL库是ST现在主推的跨系列兼容性好配合CubeMX使用方便但代码体积大、执行效率略低。对于STM32F103这块芯片我的建议是先学标准库再了解HAL库。原因很简单标准库能让你更清楚地看到寄存器的操作逻辑理解STM32的底层工作原理。比如配置一个GPIO标准库的代码是直接操作CRL、CRH、ODR寄存器你能看到每一位的作用HAL库则封装成了HAL_GPIO_Init()函数你只知道传了个结构体进去具体怎么操作寄存器的不清楚。当然如果你后续要迁移到STM32F4、F7或者H7系列HAL库是必经之路。所以我的学习路径建议是用标准库把F103的基础外设全部跑一遍理解时钟树、中断向量表、外设寄存器映射这些核心概念然后再用CubeMX HAL库做一两个项目体会一下图形化配置的便利。3.2 标准库工程模板的目录结构与关键文件一个标准的STM32F103标准库工程目录结构通常是这样Project/ ├── CMSIS/ │ ├── core_cm3.h │ ├── core_cm3.c │ └── stm32f10x.h ├── FWLIB/ │ ├── inc/ │ └── src/ ├── USER/ │ ├── main.c │ ├── stm32f10x_it.c │ └── stm32f10x_conf.h ├── SYSTEM/ │ ├── delay/ │ ├── sys/ │ └── usart/ └── OBJ/CMSIS目录放的是ARM Cortex-M3内核相关的头文件和STM32F103的寄存器定义。FWLIB是标准外设库的源文件和头文件。USER目录放你的应用代码main.c、中断服务函数、库配置文件都在这里。SYSTEM目录通常是正点原子或野火提供的系统级驱动包括延时函数、系统时钟配置、串口调试等。OBJ是编译输出目录。关键文件是stm32f10x_conf.h这个文件决定了你启用哪些外设的库文件。默认情况下很多外设的宏定义是注释掉的你需要手动取消注释。比如你要用GPIO和USART就要确保对应的宏是打开的。这个文件如果配错了编译时会报一堆“undefined reference”错误。另一个关键文件是system_stm32f10x.c它负责系统时钟的初始化。STM32F103默认使用8MHz的HSE通过PLL倍频到72MHz。如果你用的是内部时钟HSI需要修改这个文件里的相关配置。我建议新手直接用外部晶振72MHz主频这是F103最经典的配置。3.3 时钟树配置72MHz是怎么来的STM32F103的时钟树是入门阶段最容易被忽略、但最重要的知识点之一。很多人写代码时直接用SystemInit()然后默认主频就是72MHz但从来没想过这72MHz是怎么来的。简单来说STM32F103的时钟来源有三个HSI内部8MHz RC振荡器、HSE外部4-16MHz晶振通常用8MHz、PLL锁相环倍频输出。系统时钟SYSCLK可以选择HSI、HSE或者PLL输出。我们通常的配置是HSE 8MHz - PLL 9倍频 - 72MHz - SYSCLK。但这里有个关键限制APB1总线的最高频率是36MHzAPB2总线最高是72MHz。所以如果你把SYSCLK设成72MHzAPB1的分频系数必须至少是2否则APB1就超频了。APB2可以不分频直接72MHz。这个细节在配置定时器、串口波特率的时候非常重要因为外设的时钟频率取决于它挂在哪条总线上。我见过有人配置串口波特率时明明公式算对了但实际波特率就是不对最后发现是APB1的分频系数设错了导致USART2的时钟频率不是36MHz而是72MHz。所以你在用CubeMX配置时钟树的时候一定要看一眼Clock Configuration那个界面确认每条总线的频率。4. GPIO与定时器从点灯到PWM输出的完整实操4.1 GPIO输出点亮第一颗LED的正确姿势点灯是嵌入式的“Hello World”但很多人点灯只是复制粘贴代码没搞懂GPIO的配置逻辑。STM32F103的GPIO有8种模式输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、开漏复用、推挽复用。点灯用的是推挽输出模式。配置一个GPIO输出的标准流程是使能GPIO时钟 - 配置GPIO模式 - 设置输出速率 - 操作ODR寄存器或使用GPIO_SetBits/GPIO_ResetBits。以PA5为例代码大概是这样RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_5); // 输出高电平 GPIO_ResetBits(GPIOA, GPIO_Pin_5); // 输出低电平这里有个细节GPIO_Speed这个参数很多人随便填其实它影响的是GPIO翻转速度。50MHz适合大多数场合但如果你的LED是接在长排线上速度设太高可能导致信号振铃。一般点灯用2MHz或10MHz就够了。另一个坑是LED的驱动方式。有些开发板的LED是阳极接VCC阴极接GPIO输出低电平点亮有些是阳极接GPIO阴极接GND输出高电平点亮。你写代码之前一定要看原理图否则会出现“代码逻辑反了”的情况。4.2 定时器配置从延时到PWM的进阶STM32F103的定时器资源很丰富有高级定时器TIM1、TIM8通用定时器TIM2-TIM5基本定时器TIM6、TIM7。入门阶段最常用的是通用定时器用来做延时、PWM输出、输入捕获。定时器的核心参数是预分频系数PSC和自动重装载值ARR。定时器的计数频率 时钟频率 / (PSC 1)溢出时间 (ARR 1) / 计数频率。比如你要做一个1ms的定时中断定时器时钟是72MHzPSC设为71ARR设为999那么计数频率 72MHz / 72 1MHz溢出时间 1000 / 1MHz 1ms。PWM输出的配置稍微复杂一点需要设置PWM模式、占空比、输出极性。以TIM3_CH1输出PWM为例配置步骤是使能TIM3和GPIO时钟 - 配置GPIO为复用推挽输出 - 配置TIM3的时基结构体 - 配置PWM输出通道 - 使能TIM3。占空比通过TIM_SetCompare1()函数调整。我实测下来STM32F103的PWM输出在频率低于100kHz时非常稳定占空比调节线性度也很好。但如果你要做超声波测距或者红外解码就需要用到输入捕获模式这个比PWM输出要复杂一些涉及到中断和捕获寄存器的读取。4.3 串口通信USART1的配置与调试技巧串口是嵌入式开发中最重要的调试手段。STM32F103的USART1挂在APB2总线上时钟频率72MHzUSART2、USART3挂在APB1总线上时钟频率36MHz。配置串口时波特率计算公式是波特率 时钟频率 / (16 * USARTDIV)以USART1、115200波特率为例USARTDIV 72MHz / (16 * 115200) 39.0625。整数部分是39小数部分是0.0625对应BRR寄存器的值是0x271。标准库的USART_Init()函数会自动帮你算这个值但你要知道背后的逻辑。串口调试的常见问题是乱码。乱码的原因通常有三个波特率不匹配、时钟配置错误、串口线接触不良。我排查乱码问题的顺序是先用示波器或逻辑分析仪看TX线上的波形确认波特率是否准确然后检查代码里的时钟配置确认APB总线频率最后换一根USB转串口线试试。提示如果你用的是板载USB转串口芯片比如CH340、CP2102需要先安装对应的驱动。CH340在Windows 10上有时需要手动安装驱动否则设备管理器里会显示黄色感叹号。5. 烧录与调试那些让你抓狂的问题排查实录5.1 编译成功却烧录不进去常见原因速查“VS Code里编译成功却怎么也烧录不进开发板”——这个问题在热词里出现了说明很多人遇到过。我总结了一下原因通常有这几类问题现象可能原因排查方法ST-Link连接失败驱动未安装或冲突设备管理器检查驱动重装ST官方驱动烧录时提示“Flash Download failed”芯片被读保护用ST-Link Utility解除读保护烧录成功但程序不跑BOOT0跳线错误确认BOOT0接地BOOT1任意烧录后程序跑飞时钟配置错误检查HSE是否起振PLL配置是否正确调试器识别不到芯片SWD引脚被占用检查代码中是否禁用了SWD或JTAG其中芯片读保护是最容易被忽略的问题。有些开发板出厂时开启了读保护或者你之前烧录的程序里误操作开启了读保护导致ST-Link无法写入Flash。解决办法是用ST-Link Utility连接芯片在Target - Option Bytes里把Read Out Protection设为Disabled然后重新烧录。另一个常见问题是BOOT0跳线。STM32F103的启动模式由BOOT0和BOOT1决定BOOT00时从主Flash启动BOOT01时从系统存储器启动。如果你把BOOT0接高电平芯片会进入Bootloader模式你烧录的程序不会运行。所以烧录完成后一定要确认BOOT0接地。5.2 ST-Link Utility与STM32CubeProgrammer的使用ST-Link Utility是ST早期的烧录工具界面简洁功能实用。STM32CubeProgrammer是ST现在主推的烧录工具支持更多芯片系列和更多功能。两者都可以用来烧录hex、bin文件也可以用来解除读保护、修改选项字节。我个人的习惯是日常烧录用IDE自带的下载功能批量生产或修复芯片用STM32CubeProgrammer。CubeProgrammer有一个好处是支持命令行模式可以写脚本自动化烧录。比如你要给一批板子烧录同一个固件可以用CubeProgrammer的命令行工具配合批处理脚本效率比手动点按钮高得多。使用CubeProgrammer时需要注意接口选择。STM32F103支持SWD和JTAG两种调试接口CubeProgrammer默认可能是JTAG模式你需要手动切换到SWD。切换方法是在CubeProgrammer的连接设置里把Interface从JTAG改成SWD然后重新连接。5.3 调试技巧用串口打印和断点定位问题调试STM32F103程序最有效的手段是串口打印和断点调试。串口打印适合看程序运行流程和变量值断点调试适合看寄存器和内存状态。串口打印的经典做法是重定向printf函数。标准库中你需要重写fputc()函数把字符输出到USART。代码大概是这样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”否则printf会卡死。这个坑我踩过当时printf死活没输出查了半天才发现是没勾MicroLIB。断点调试方面Keil和CubeIDE都支持硬件断点和软件断点。STM32F103的硬件断点数量有限通常4-6个如果你设了太多断点IDE会提示“Cannot set breakpoint”。这时候要么减少断点数量要么用软件断点替代。软件断点会修改Flash内容调试完成后需要重新烧录。6. 进阶方向从入门到能做项目的学习路径6.1 外设进阶ADC、DMA、SPI、I2C的学习顺序把GPIO、定时器、串口跑通之后下一步是学ADC、DMA、SPI、I2C这四个外设。我建议的学习顺序是ADC - DMA - I2C - SPI。ADC用来采集模拟信号比如电位器电压、光敏电阻阻值。STM32F103的ADC是12位逐次逼近型最高采样率1MHz。学ADC的重点是理解采样时间、转换模式、校准流程。DMA用来在内存和外设之间直接搬数据不占用CPU。ADC配合DMA可以实现多通道自动采集串口配合DMA可以实现高速收发。DMA的难点是通道分配和优先级配置STM32F103的DMA1有7个通道每个通道对应固定的外设请求。I2C用来连接EEPROM、传感器、OLED屏幕。STM32F103的硬件I2C有已知的bug在某些情况下会死锁。很多人的做法是用软件模拟I2C虽然速度慢一点但稳定性好。我实测下来软件I2C在400kHz速率下跑OLED屏幕完全没问题。SPI用来连接Flash、SD卡、无线模块。SPI的速度比I2C快得多STM32F103的SPI最高18MHz。学SPI的重点是理解时钟极性CPOL和时钟相位CPHA的组合这四个组合决定了数据在时钟的哪个边沿采样。6.2 项目实战从超声波测距到USB虚拟串口学完基础外设之后你需要做几个小项目来巩固。热词里提到的STM32超声波测距和STM32 USB虚拟串口是两个很好的练手项目。超声波测距用HC-SR04模块原理是Trig引脚发一个10us的高电平脉冲模块自动发8个40kHz的超声波Echo引脚输出高电平高电平持续时间就是超声波往返时间。距离 时间 * 340m/s / 2。这个项目的关键是输入捕获用定时器捕获Echo引脚的高电平时间。USB虚拟串口稍微复杂一点需要用到STM32F103的USB外设。STM32F103的USB是Full Speed12Mbps支持Device模式。实现虚拟串口需要移植ST的USB库配置描述符、端点、中断处理。这个项目的坑比较多比如USB时钟必须精确到48MHz时钟配置错了USB就枚举失败。6.3 学习资源与社区哪些资料值得看STM32F103的学习资料非常多我推荐几个我觉得质量高的ST官方参考手册RM0008这是最权威的资料虽然厚但遇到问题查它准没错。ST官方数据手册Datasheet查引脚定义、电气特性、封装信息。正点原子/野火的教程国内最流行的STM32教程适合入门。STM32中文社区遇到问题可以搜索很多坑别人已经踩过了。我个人的经验是不要只看视频教程一定要动手写代码。看视频的时候觉得都懂了自己写的时候才发现到处都是问题。每学一个外设就自己从零建一个工程不看教程写一遍写不出来再回去查。这样学下来的东西才是你自己的。最后分享一个小技巧建一个自己的代码仓库把每个外设的配置代码整理成模板。比如GPIO初始化、定时器初始化、串口初始化都写成独立的函数放在自己的库里。下次做新项目的时候直接复制粘贴效率会高很多。这个习惯我从学STM32第一年就开始保持到现在积累了几十个常用模块做新项目基本不用从头写底层驱动。