
开发板到手的那一刻才是最考验耐心的时候。STM32F103开发板买回来了这意味着你正式踏上嵌入式这条又苦又爽的路。作为常年跟单片机打交道的嵌入式工程师见过太多人买完开发板就吃灰也见过不少人靠一块三十块钱的板子学会了整个裸机开发。这篇文字就写给刚入手STM32开发板、想认真学STM32的读者我从板子选型讲到环境搭建从点灯讲到串口、定时器、ADC、USB和CAN顺便把那些年踩过的坑一次性讲清楚。本文适合零基础初学者也适合已经会点灯但卡在外设调试阶段的朋友。1. 拿到STM32F103开发板之后先搞清楚你要学什么1.1 为什么第一块开发板通常是STM32F103STM32F103这个型号在嵌入式圈子里几乎成了“入门代名词”。原因很简单它便宜、资料多、外设全而且整个F1系列引脚兼容性极强。无论是淘宝上二十多块的“最小系统板”还是带屏幕带传感器的一两百块“旗舰学习板”核心都是同一颗芯片——ARM Cortex-M3内核主频72MHzFlash从16KB到512KB不等RAM从4KB到64KB不等。很多人会纠结要不要直接上F4或者H7我的建议是如果你是零基础F103就是最优解。因为网上关于F103的教程量级是F4的几倍不止从寄存器到标准库到HAL库任何你卡住的地方几乎都能搜到现成答案。学习嵌入式最怕的不是芯片性能不够而是遇到问题没人能问、没资料可查F103恰好把这个问题规避掉了。还有一点容易被忽略F103的功耗、引脚定义、外设逻辑和更高端的F4乃至H7是高度相似的。你花两星期学会了F103的GPIO和USART之后换F4甚至换国产的GD32、AT32过渡成本非常低。芯片只是工具掌握的是思路F103就是那个最不容易劝退新手的工具。1.2 开箱检查芯片型号、丝印、第一脚确认拿到板子别急着插线先做三件事看芯片丝印、找第一脚、确认下载器接口。芯片丝印通常在MCU正面像“STM32F103C8T6”这样的字符串。C8T6表示C是48引脚T是LQFP封装8是64KB Flash6是工作温度范围。这块芯片是F103系列里最经典的型号淘宝最常见的“蓝色药丸板”就是它。别小看这串字符后面写程序选芯片型号、选启动文件、配置Flash大小全都靠它。第一脚确认是新手最容易忽略但踩坑最多的环节。LQFP48封装的C8T6芯片左上角有一个圆形凹坑凹坑左下角那个引脚就是1脚然后逆时针数。用杜邦线或者排针接外设时接反一个引脚轻则程序跑飞重则烧外设所以拿到板子第一件事就是对着数据手册把引脚图画出来标好VCC、GND、BOOT0、3.3V这几个关键位置。开发板上的下载器接口一般有三种ST-Link的SWD四线接口3.3V、SWDIO、SWCLK、GND、J-Link的20针JTAG接口、以及串口下载的BOOT接口。新手最容易买到的板子通常带一个板载ST-Link或者DAP-Link直接USB线插上就能识别。如果是纯最小系统板就需要外接一个ST-Link或者DAP-Link下载器十几块钱别买太廉价的容易烧到一半就掉线。1.3 开发板的三种程序下载方式STM32F103支持三种典型的下载方式JTAG/SWD、串口ISP、以及DFUUSB固件升级。对初学者来说最常用的是SWD因为它只占两根数据线加电源地速度也够用而且ST-Link默认就是SWD模式。串口ISP是很多老教程里的玩法把BOOT0跳线跳到1BOOT1跳到0然后通过USART1的PA9、PA10用串口工具把hex文件烧进Flash。这种下载方式不用下载器但每次都得手动拨跳线、上电、复位极其折磨人建议只当了解别作为日常开发流。DFU方式需要芯片的USB引脚下拉同时BOOT0置高在电脑上装DFU工具完成烧录。这种方式在F103上用得不多因为直接用SWD比它香太多。至于用J-Link还是ST-Link看预算功能上ST-Link V2就完全够用淘宝十几块钱的“盗版”也能稳定调试记得装好驱动就行。提示买板子的时候顺手问卖家要一下原理图PDF这玩意儿比你买十本书都值。后面接任何外设都得靠它确认引脚映射和跳线帽位置。2. 开发环境搭建Keil、芯片包、ST-Link一条龙2.1 安装Keil MDK与STM32F1芯片包开发环境这一步能劝退一大半新手。这里给一套我自己反复用过的安装顺序先装Keil MDKARM版再装STM32F1xx的Device Family Pack芯片包最后装ST-Link驱动顺序反了也不影响但容易出一些莫名其妙的问题。Keil MDK装完之后打开Pack Installer它通常随MDK一起安装。在搜索框输入“STM32F1”找到Keil.STM32F1xx_DFP这个芯片支持包选一个稳定版本安装。注意有的新手是从网上下载的绿色版MDKPack Installer无法在线更新这时候就需要手动下载DFP安装包然后双击导入。安装完芯片包之后新建工程时Device栏里才能看到“STM32F103C8”这个芯片选项否则你会卡在“找不到芯片型号”这步。ST-Link驱动方面Win10/Win11系统通常插上就能识别如果设备管理器里显示“未知USB设备”去ST官网下STSW-LINK009驱动包手动安装。装完之后插上板子右键“我的电脑”-“管理”-“设备管理器”看到“STMicroelectronics STLink dongle”字样就说明成功了。这里有个小技巧插USB线之前先把开发板上的供电跳线帽放对位置避免电脑USB口给板子供电时电流不足导致反复识别失败。2.2 工程模板与代码结构解析STM32F103的开发方式目前主要三条路寄存器直接操作、标准外设库StdPeriph、HAL库。寄存器操作适合彻底搞懂原理但写起来又长又难Debug标准库是十年前的主流中文教程最多但官方已停止维护HAL库配合STM32CubeMX图形界面配置是目前ST主推的方案也是我个人推荐新手走的路线。用CubeMX建工程不需要手写启动文件和系统时钟初始化。打开STM32CubeMX选择芯片型号“STM32F103C8Tx”在Pinout界面点一下PA5引脚设为GPIO_Output在Clock Configuration里把HSE设为晶振源如果是板载8MHz晶振HCLK设为72MHz然后Project Manager里选Toolchain为MDK-ARM生成代码。CubeMX生成出来的工程自带SystemInit和时钟树初始化省掉了一大半新手容易出错的地方。工程里核心文件要认识几个main.c是入口stm32f1xx_hal_msp.c负责外设底层引脚映射stm32f1xx_it.c放中断服务函数system_stm32f1xx.c管时钟初始化。别一上来就试图每行都看懂先能改main.c让灯亮起来再说。初次建议只勾选HAL库不要勾选FreeRTOS等中间件学裸机先把基础打牢。2.3 第一次点灯GPIO输出模式的选择点灯是嵌入式的Hello World但GPIO模式的选择这里值得多说两句。CubeMX里GPIO输出模式常见有两种Push-Pull推挽和Open-Drain开漏。推挽模式能直接输出高电平和低电平驱动能力强LED负极接地、正极串电阻接引脚这种方式就用推挽。开漏模式下引脚只能主动拉低或者输出高阻要靠外部上拉电阻才能输出高电平主要用于电平转换、多设备共享总线等场景。点灯代码本身很简单CubeMX生成之后在main的while循环里加上要翻转的引脚电平。也可以用HAL函数判断当前电平来翻转。如果板子上的LED是低电平点亮推挽输出下要点亮就要写GPIO_PIN_RESET很多新手栽在这里——明明配置正确灯却不亮一查是极性反了。GPIO速度选择这里也容易困惑。CubeMX里GPIO Output speed有Low/Medium/High三档LED点灯Low都够但如果后面用GPIO模拟时序比如DS18B20、DHT11建议选High否则边沿太慢可能导致数据读错。同样一个引脚配置不同速度档位对信号质量的影响在示波器上非常明显新手养成习惯高频场合开High低频场合开Low省电又稳定。2.4 点灯之外的板级验证点灯成功之后别急着上复杂外设建议再做两个验证按键输入和串口输出。按键输入涉及GPIO输入模式配置和上下拉设置按键接法通常一端接引脚、一端接GND内部上拉开启后按下就是读到低电平松开是高电平。这比LED输出多了一个“读取外部状态”的概念是理解中断和轮询的基础。串口输出验证则要配置USART1PA9-TX、PA10-RX波特率115200在CubeMX里把USART1打开Mode设为Asynchronous。然后接到电脑USB转TTL工具上用串口助手看能不能收到数据。到这里你的板子才算真正跑通了从“编译-下载-执行-反馈”的完整链路。3. 从点灯走向实用外设串口、定时器我都要3.1 串口USART配置与printf重定向USART是学习STM32外设的第二个分水岭。点灯只是单向输出串口则给了程序一双“眼睛”。配置USART的核心参数就三个波特率、数据位、停止位。默认经常用115200-8-N-1即115200波特率、8位数据、无校验、1位停止位收发双方必须完全一致否则收到的全是乱码。printf重定向是串口调试的经典需求。在Keil中勾选“Use MicroLIB”后重写fputc函数在fputc里调用HAL_UART_Transmit发送一个字节就能直接用printf格式化输出。很多新手在勾选MicroLIB之前printf根本不会从串口出来这个问题我也踩过。另外一定记得在MDK的Target选项卡里勾选MicroLIB不勾选则默认使用标准C库重定向方式不一样。串口接收方面新手先学最简单的方式轮询接收。HAL_UART_Receive在超时时间内等待接收一个字节。这种方式写入while循环接收速度慢但逻辑简单。进阶之后再学HAL_UART_Receive_IT中断接收以及DMA接收尤其是接收不定长数据时要用到空闲中断加DMA这部分水比较深等串口收发闭环了再研究不迟。3.2 定时器捕获测频率与超声波测距定时器是STM32里最灵活、也最容易让人头大的外设。TIM1和TIM8是高级定时器带互补PWM输出和刹车功能适合电机控制TIM2-TIM5是通用定时器能做时基、PWM、输入捕获TIM6和TIM7是基本定时器只能做最简单的定时。“定时器捕获测频率”是经典的入门综合实验。原理是通过TIM输入捕获通道检测外部信号上升沿两次上升沿之间的时间差取倒数就是频率。比如PA0引脚复用为TIM2_CH1在CubeMX中开启Input Capture direct mode在中断回调里记录CNT值两次记录的差值乘以定时器时钟周期就是周期。超声波测距HC-SR04也是输入捕获的典型应用。Trig引脚发一个10us以上的高电平脉冲模块会自动发8个40kHz方波Echo引脚返回一个高电平高电平持续时间和距离成正比。用输入捕获测出Echo高电平时间t距离t×340m/s÷2换算成厘米通常用t除以58。这个公式很多教程直接给但没有解释声音在空气中的速度约340米每秒往返距离除以2才是单程。理解了误差和干扰后面的避障小车才能走得稳。3.3 PWM输出与电机控制舵机、步进电机、伺服电机PWM脉宽调制的本质是通过占空比控制平均电压或脉冲宽度。LED亮度调节和舵机角度控制是同一个底层原理。STM32F103的TIM2-TIM5都有4个独立通道每个通道可以独立输出不同占空比的PWM频率由定时器预分频和自动重载值决定。舵机控制是最直观的PWM应用周期20ms脉冲宽度1ms对应0度1.5ms对应90度2ms对应180度。使用TIM2_CH1输出PWM设置周期50Hz20ms占空比根据目标角度换算。这里有个细节舵机的信号线要直接接PA0这种PWM输出引脚同时舵机供电最好单独接5V不要和STM32共用3.3V否则启动瞬间电流太大容易把板子复位。“五线四相步进电机”和“伺服电机485控制”是热搜里的两个高频关键词。四相步进电机通常用ULN2003驱动板通过GPIO按顺序给四个相线通电步进角就是节拍。关键是控制换相顺序比如半步模式A-AB-B-BC-C-CD-D-DA这样八个节拍。而伺服电机用RS485控制时需要配置USART的DE方向引脚发送前拉高DE进入发送模式发完再拉低切回接收。很多新手在485通信上翻车根因就是DE引脚没有正确切换方向。4. 进阶玩法USB、显示、通信与物联网4.1 把STM32做成USB设备“stm32 如何做usb设备”被很多人搜索这其实是个典型的需求想用STM32当键盘、鼠标、游戏手柄或者虚拟串口。F103的USB是USB 2.0 FS设备控制器不能直接当USB Host但做Device没问题。前提是芯片型号带USB功能C8T6就有两个USB引脚PA11和PA12主频必须配置为48MHz否则USB枚举会失败。最稳妥的入门方式是使用STM32CubeMX配置USB功能。选择USB设备模式中间件里选Communication Device Class虚拟串口生成代码之后插上USB线电脑就能识别出一个COM口。这种虚拟串口方案在物联网调试里特别实用一根线同时解决供电和串口通信。如果想自己做HID键盘之类的设备需要在USB配置里选Human Interface Device类配置报告描述符Report Descriptor。这部分的坑集中在描述符配置错误导致的设备无法识别建议先用现成模板改别从零手写。USB相关的调试比较痛苦没有示波器时只能靠电脑设备管理器里的反馈来判断把状态枚举、复位、地址设置等步骤理清才能下手调。4.2 SPI屏幕ILI9341读不到正确ID的教训“stm32使用ili9341读id是a1a1”这个问题我能感同身受因为我当年调试TFT屏幕时也卡过整整两天。ILI9341是一款2.8寸TFT LCD的经典驱动芯片读ID寄存器0xD3时正常返回0x9341。如果你读回来是0xA1A1说明通讯数据线有问题最常见的原因是SPI的MOSI和MISO接反了或者SDI/SDO两根线连错。ILI9341有两种接线模式四线SPI和五线SPI。四线SPI中SCL接时钟、SDA接MOSI、DC接命令/数据选择、CS片选、RST复位五线SPI多了一条MISO用于读寄存器。很多卖家给的例程是四线模式但板子上的接口把MISO也引出来了线序稍有不慎就会读ID异常。排查思路是先测试复位时序RST拉低再拉高延时120ms再确认SPI时钟极性CPOL和相位CPHA最后检查DC引脚在发命令和发数据时是否正确切换。ILI9341这块屏还有一层坑很多所谓“SPI屏”实际上支持SPI读回但初始化时序必须先用SPI写入0x28等关闭显示命令再读ID顺序乱了也很容易得到0xA1A1。实在读不到ID可以先不管ID直接跑一个固定分辨率240x320的初始化序列再填充纯色看颜色输出是否正常。调试显示外设时别死磕一个点先确认屏能亮、能显示颜色再去追寄存器细节。4.3 ADC多通道切换连续采样与通道选择STM32F103的ADC是12位逐次逼近型ADC最多支持16个外部通道。ADC多通道切换的难点不在配置单个通道而在于多个通道之间的切换方式和数据对应关系。CubeMX中启用ADC1、开启扫描模式Scan Mode、连续转换模式Continuous Conversion Mode设置通道数量为你要采样的通道数然后按顺序把PA0-PA3等引脚勾选为ADC1_IN0等模拟输入。生成的HAL代码里会启动DMA循环搬运转换结果。但Scan Mode下ADC的转换顺序取决于Rank配置读出来的DMA数据顺序一定要和Rank顺序对上否则会出现通道数据错位的诡异问题。我个人的实践是不用连续转换模式改成软件触发单次转换每次转换前用HAL_ADC_ConfigChannel选择目标通道转换完读取结果再切换。这种方式的优势是逻辑直观而且不会出现通道串扰的问题。缺点是CPU占用高但对大多数传感器轮询场景完全够用。ADC还有一个频繁踩坑点引用引脚电平范围。STM32的ADC输入范围是0到VREF如果VREF接的是3.3V那内部参考电压就是3.3V。想要采集任意模拟电压值还必须确保被测信号的GND和STM32的GND是共地的否则读数完全没意义。4.4 CAN通信连不上是怎么回事“stm32 can通信突然连不上”这个问题解决起来需要系统的排查方法。STM32F103的CAN控制器内置在芯片内但板上必须外接CAN收发器最常见的是TJA1050或MCP2551。接线是CANH对CANH、CANL对CANL并在CANH和CANL之间跨接120欧终端电阻这个电阻在只有两个节点的网络中尤其重要缺少它信号反射会导致通信不稳定甚至完全不通信。CAN通信突然连不上的排查顺序应该是先看硬件——CAN收发器的VCC是否正常、CANH/CANL是否接反、终端电阻是否在位再看软件——波特率是否匹配。F103的CAN外设时钟来自APB1通常36MHz波特率由预分频器和Bit Segment配置决定。常见的500Kbps配置是Prescaler4BS19BS22同步跳转宽度1。这里有个规律通信双方只要波特率稍有偏差就会导致总线错误计数器快速飙升最终进入Bus-off状态。“突然连不上”还有一个隐蔽原因CAN总线上电时所有节点的TJA1050同时上电总线上至少要有两个节点才能正常通信如果只有单节点回环模式能收到自己的报文那就没问题。用CAN分析仪抓帧把所有节点断开只留一个发数据看波形和ACK回执是判断物理层问题还是协议层问题的分界线。5. 常见问题与排查技巧实录速查表5.1 烧录失败类症状可能原因解决办法烧录时报“No ST-Link detected”USB驱动未装/线材质量差重装驱动换短数据线直连主板USB口烧录时报“Target not found”芯片被锁死或供电异常按住复位键开始烧录点击下载瞬间松开复位烧录成功但程序不运行BOOT0跳线帽在1位置将BOOT0跳线帽拨回0重新上电复位Keil检测不到芯片ID芯片型号选错如C8选成了C6在Device中重新选择STM32F103C8烧录失败时最常见的操作顺序是先检查驱动、再检查接线、最后考虑芯片锁死。F103芯片锁死的典型原因是把SWD引脚PA13、PA14配置成了普通IO或者开启了低功耗模式。解决办法有两种一是按住复位键不放点击Keil的下载按钮在“Connecting...”提示出现时瞬间松开复位键二是在芯片解锁模式下用ST-Link的“Connect under Reset”选项重新连接。5.2 调试异常类串口收不到数据是仅次于点灯不亮的第二大问题。首先确认串口调试助手的波特率和程序里完全一致其次检查USB转TTL工具的TX是否接了STM32的RXPA10工具的RX接STM32的TXPA9TX和RX交叉连接是最容易犯的错误最后确认共地——USB转TTL工具和开发板必须共地否则串口电平参考点不同数据必然乱码或收不到。定时器中断不进、PWM输出频率不对这一类问题大多出在时钟树配置上。F103的TIM2-TIM5挂在APB1上如果APB1分频系数不是1定时器时钟会是APB1的两倍。很多人用CubeMX自动生成的时钟树没问题但手动改代码时就会把预分频算错。建议新手在改时钟前先把CubeMX生成的SystemClock_Config函数完全看懂尤其是PLL倍频和APB1/APB2分频关系。5.3 工程配置类“stm32 ld文件”是热搜里的词这通常出现在从STM32G0或H7转向F103或者使用GCC工具链的场景。F103的GCC工程里ld文件定义了Flash和RAM的起始地址及大小。C8T6的Flash从0x08000000开始大小为64KBRAM从0x20000000开始大小为20KB。改动ld文件要注意Flash地址0x08000000、长度64KRAM地址0x20000000、长度20K。写错会导致链接后代码超过Flash空间或者运行时数据跑到非法地址表现就是程序上电飞了。“stm32 gbk转utf8”这个搜索词其实是工程里遇到中文编码问题的场景。Keil默认使用ANSI编码Windows下即GBK而Git/GitHub上的源码常用UTF-8。混用编码会导致注释乱码、字符串常量输出乱码甚至编译警告。解决办法是统一文件编码要么全部文件按GBK保存要么全部按UTF-8保存并在Keil的Edit-Configuration-Editor里设置Encoding为对应编码。从来没有“完美自动转换”保持编码一致才是根治方案。5.4 有关联的外设联动方案如果不想停留在点灯阶段这里给几条我自己觉得比较有代表性的外设联动路线路线涉及外设综合技能两轮差速小车控制GPIO、PWM、编码器接口、串口遥控定时器多通道、PID调节、中断优先级多功能鱼缸温度传感器DS18B20、水位检测、OLED显示、继电器控制单总线时序、ADC采集、I2C显示STM32物联网网关ESP8266/ESP32、巴法云、LWIP协议栈、FreeRTOS透传AT指令、嵌入式网络、任务调度步进电机定位平台五线四相步进电机、ULN2003、限位开关状态机、非阻塞逻辑、硬件时序6. 最后分享几个我自己的实操心得踩过几次坑之后我现在拿到任何一块新开发板都会先做四件事看原理图把每个引脚功能标出来点灯确认工具链跑通串口确认可以看到调试信息跑一个外设Demo确认板子硬件没暗病。STM32F103的学习路径上最容易被忽略的是“阅读数据手册”的能力。不要被上千页的英文手册吓住现在芯片手册都有中文版本而且你只需按需查阅。看GPIO章节时只看你要用的部分重点看引脚定义、复用功能表、电气特性三个小节就够了。手册不是用来通读的是用来当字典查的。另外一个小技巧工程从最开始就养成好习惯。每个外设单独一个源文件、宏定义常量、中断服务函数尽量精简。很多写过三年代码的老手看到自己第一年的项目也会脸红但早期好的工程习惯能让你少走很多弯路。学习STM32这件事最大的瓶颈从来不是智商而是“持续动手调试”的耐心。一块F103开发板能学到的内容足够撑起整个裸机开发的基础从GPIO到串口、从定时器到ADC、从USB到CAN每过一个坎都会发现自己的能力圈扩大一圈。这块板子买回来了接下来要做的就是一件一件把它点亮。