拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32F103入门实战:从开箱到点亮LED的完整避坑指南

STM32F103入门实战:从开箱到点亮LED的完整避坑指南

STM32 开发板到手之后,真正让人卡住的往往不是写代码,而是从拆快递到点亮第一颗 LED 之间的那段空白。我见过太多人兴冲冲买回一块 STM32F103 板子,结果在装软件、找驱动、配环境这几步上耗掉一整个周末,最后板子吃灰。这篇内容就是写给刚拿到板子、或者正准备下单的朋友,把 STM32F103 入门这条路上该踩的、不该踩的坑一次讲清楚。不管你是电子专业的学生、想转嵌入式的程序员,还是纯粹想折腾硬件的爱好者,只要跟着走一遍,至少能少走两三个晚上的弯路。

1. 拿到 STM32F103 开发板先别急着上电

1.1 开箱之后第一件事是核对板子版本和核心芯片

很多人拆开包装就直接插 USB 线,这个习惯不太好。STM32F103 这个系列本身有很多细分型号,最常见的几种是 C8T6、RCT6、ZET6,区别主要在 Flash 容量、RAM 大小和引脚数量上。C8T6 是 64KB Flash、20KB RAM、48 引脚,RCT6 是 256KB Flash、48KB RAM、64 引脚,ZET6 则是 512KB Flash、64KB RAM、144 引脚。你买到的板子上焊的是哪一颗,直接决定了后面能跑多复杂的程序。

我一般拿到板子会先做三件事:第一,用放大镜或者手机微距拍一下芯片表面的丝印,确认型号;第二,翻一下板子背面,看看有没有标注版本号,比如 V3.0、V3.1 这种,不同版本的外设布局可能不一样;第三,对照卖家给的资料包,确认板载的晶振频率是 8MHz 还是 12MHz,这个直接影响后面时钟树的配置。

注意:有些低价板子会用 STM32F103C8T6 打磨后冒充 RCT6 卖,丝印模糊或者字体不对就要警惕。正规渠道买的板子一般不会有这个问题,但二手或者来路不明的要留个心眼。

1.2 上电前必须确认的供电和跳线状态

STM32F103 的供电范围是 2.0V 到 3.6V,大部分开发板通过 USB 的 5V 经过板载 LDO 降压到 3.3V 给芯片供电。上电之前要检查几个地方:BOOT0 和 BOOT1 跳线帽的位置,这决定了芯片从哪里启动。正常跑程序的时候 BOOT0 接 GND、BOOT1 随便,也就是从主 Flash 启动。如果要通过串口下载程序,需要把 BOOT0 接到 VCC,下载完再拨回去。

还有一个容易被忽略的点是 USB 接口的类型。有些板子用的是 Micro USB,有些是 Type-C,还有些老板子是 Mini USB。线材不对的话,插上去没反应,新手很容易以为是板子坏了。我建议拿到板子先用手电筒照一下接口,确认类型再找线。

另外,如果你买的板子带 ST-Link 或者 DAP-Link 调试器,要确认调试器的排线有没有插反。排线上一般有防呆凸起,但有些廉价排线没有,插反了轻则识别不到芯片,重则烧调试器。我自己的习惯是插之前先看板子上的丝印,SWDIO、SWCLK、GND、3V3 这四个脚的位置记清楚。

1.3 板载外设的初步检查清单

一块典型的 STM32F103 开发板通常会带这些外设:LED 指示灯(至少一个)、按键(复位键加用户键)、USB 转串口芯片(CH340 或者 CP2102)、SWD 调试接口、以及一些扩展排针。上电之后,如果板子出厂烧了测试程序,LED 应该会闪烁或者呼吸。如果没有反应,先别慌,可能只是出厂没烧程序。

我习惯用万用表蜂鸣档快速测一下几个关键点:3.3V 和 GND 之间有没有短路,USB 的 5V 有没有到 LDO 输入端,LDO 输出是不是 3.3V。这几个点正常,板子基本就没大问题。这一步花不了两分钟,但能避免很多"板子是不是坏了"的焦虑。

2. 开发环境搭建:Keil、CubeMX 和 VSCode 怎么选

2.1 Keil MDK 依然是 STM32F103 最稳的选择

网上关于 STM32 开发环境的讨论很多,有人推 Keil,有人推 IAR,还有人用 VSCode 加插件。我的建议是,如果你是零基础入门 STM32F103,先用 Keil MDK。原因很简单:资料最多、教程最全、遇到问题最容易搜到答案。Keil 的安装包不大,装完之后需要单独装 STM32F1 的器件支持包,也就是常说的 Pack。

安装顺序是这样的:先装 Keil MDK 主程序,然后去 Keil 官网下载 STM32F1 系列的 Device Family Pack,双击安装。装完之后打开 Keil,在 Project 菜单里新建工程,选择芯片型号的时候能看到 STM32F103 系列就说明装好了。这里有个细节,Keil 5 和 Keil 4 的 Pack 不通用,现在新装的都是 Keil 5,别下错版本。

提示:Keil 的编译速度在 STM32F103 这种小芯片上完全够用,没必要为了追求编译速度去折腾其他工具链。等你把 F103 玩熟了,再考虑换 VSCode 加 GCC 的方案。

2.2 STM32CubeMX 要不要学,什么时候学

CubeMX 是 ST 官方出的图形化配置工具,可以自动生成初始化代码。对于 STM32F103 来说,CubeMX 支持得非常好,基本上点几下鼠标就能把时钟、GPIO、串口、定时器都配好。但我不建议一上来就用 CubeMX,原因是你会对底层寄存器没有概念,后面遇到问题不知道怎么排查。

我的建议是分两步走:第一周先用标准库手写代码,把 GPIO 输出、GPIO 输入、串口收发这几个基础实验做一遍,理解一下寄存器是怎么操作的。第二周开始用 CubeMX 生成代码,对比一下它生成的初始化和你自己写的有什么区别。这样既懂了原理,又享受了工具的效率。

CubeMX 生成代码的时候要注意,它默认用的是 HAL 库,代码量比标准库大不少。STM32F103C8T6 只有 64KB Flash,用 HAL 库写复杂一点的项目可能会不够用。如果 Flash 吃紧,可以在 CubeMX 里选择用 LL 库,代码会精简很多。

2.3 VSCode 方案适合什么人

VSCode 加 STM32 的方案这两年越来越流行,核心是装 Cortex-Debug 插件,配合 OpenOCD 或者 ST-Link GDB Server 来调试。这套方案的好处是编辑器体验好、免费、跨平台,缺点是配置起来比 Keil 麻烦不少,尤其是 launch.json 和 tasks.json 这两个文件,新手很容易配错。

如果你已经有一定编程基础,平时就用 VSCode 写代码,那可以试试这套方案。配置的关键是这几个文件:c_cpp_properties.json 里要指定 ARM 的头文件路径,tasks.json 里要写编译命令,launch.json 里要配调试器路径和芯片型号。网上有现成的模板可以抄,但芯片型号和路径要改成你自己的。

我自己的主力环境是 Keil 加 VSCode 双开:Keil 用来编译和下载,VSCode 用来看代码和写代码。Keil 的编辑器确实不太好用,但它的编译器和调试器对 STM32F103 的支持是最成熟的,没必要为了编辑器体验去折腾整个工具链。

3. 第一个工程:从新建到点亮 LED 的完整链路

3.1 用标准库新建工程的目录结构

标准库新建工程是 STM32F103 入门最经典的方式,虽然现在 ST 主推 HAL 库,但标准库的代码结构清晰,适合理解底层。新建工程的步骤大致是:在 Keil 里新建 Project,选好芯片型号,然后把 ST 官方标准库里的启动文件、内核文件、外设驱动文件复制到工程目录里。

目录结构我一般这样组织:根目录下建四个文件夹,分别是 Start、Library、User、Output。Start 放启动文件和内核文件,Library 放标准库的外设驱动,User 放 main.c 和用户自己写的代码,Output 放编译生成的 hex 和 axf 文件。这样分的好处是结构清晰,后面加外设的时候不会乱。

启动文件的选择要注意,STM32F103C8T6 用的是 startup_stm32f10x_md.s,md 代表 Medium Density。如果是 RCT6 或者 ZET6,要用 hd 版本。选错了启动文件,编译能过但程序跑不起来,这个坑我踩过。

3.2 时钟配置:为什么是 72MHz

STM32F103 的最高主频是 72MHz,这个频率不是随便定的,而是通过外部 8MHz 晶振经过 PLL 倍频得到的。具体路径是:8MHz 晶振输入,经过 PLL 预分频器除以 1,然后 PLL 倍频 9 倍,得到 72MHz,再经过 AHB 预分频器除以 1,作为系统时钟。这个配置在标准库的 system_stm32f10x.c 文件里已经写好了,默认就是 72MHz。

但如果你用的是 12MHz 晶振的板子,这个默认配置就不对了,需要手动改 PLL 的倍频系数。12MHz 的话,倍频 6 倍得到 72MHz。改的地方在 SystemInit 函数里,找到 RCC_PLLConfig 那一行,把参数改成对应的值。这个细节很多教程不讲,但买板子的时候如果没注意晶振频率,后面串口波特率会全乱。

3.3 GPIO 输出实验:点亮第一颗 LED

点亮 LED 是嵌入式界的 Hello World,但这里面也有讲究。STM32F103 的 GPIO 有八种模式,输出模式下常用的是推挽输出和开漏输出。推挽输出可以输出高电平和低电平,驱动能力强;开漏输出只能输出低电平,高电平需要外部上拉。驱动 LED 一般用推挽输出。

配置 GPIO 的步骤是:先开 GPIO 时钟,然后设置引脚的模式、速度,最后用 GPIO_SetBits 或者 GPIO_ResetBits 控制电平。这里有个容易搞混的地方,STM32 的 GPIO 时钟是挂在 APB2 总线上的,开时钟要用 RCC_APB2PeriphClockCmd,别用成 APB1 的。

LED 的接法有两种:共阳极和共阴极。共阳极是 LED 正极接 3.3V,负极接 GPIO,GPIO 输出低电平点亮;共阴极是负极接 GND,正极接 GPIO,输出高电平点亮。大部分开发板用的是共阳极接法,所以 GPIO_ResetBits 是点亮,GPIO_SetBits 是熄灭。写代码之前先看一眼原理图,确认接法。

3.4 下载程序:SWD 和串口两种方式

下载程序有两种常用方式:SWD 和串口。SWD 需要 ST-Link 或者 DAP-Link 调试器,接线是 SWDIO、SWCLK、GND、3V3 四根线。串口下载需要 USB 转串口模块,接线是 TX、RX、GND 三根线,还要把 BOOT0 拨到 VCC。

SWD 的好处是可以在线调试,打断点、看变量都方便,推荐作为主要下载方式。串口下载的好处是不需要额外买调试器,板载的 USB 转串口芯片就能用,但只能下载不能调试。我一般两个都留着,SWD 用来日常开发,串口用来救急,比如不小心把 SWD 引脚复用了导致连不上芯片的时候。

注意:如果 SWD 连不上芯片,先检查 BOOT0 是不是被拨到了 VCC,再检查调试器驱动有没有装好。还不行的话,把板子断电,按住复位键,点下载,再松开复位键,这叫"复位下载法",能解决大部分连不上的问题。

4. 入门阶段最容易卡住的几个问题

4.1 串口乱码:波特率、时钟、线序三查

串口乱码是新手遇到最多的问题,没有之一。排查思路按这个顺序来:第一,确认波特率是不是两边一致,常用的有 9600、115200,板子和串口助手要设成一样的;第二,确认系统时钟是不是 72MHz,如果晶振频率不对导致时钟错了,波特率也会跟着错;第三,确认 TX 和 RX 有没有接反,板子的 TX 要接串口模块的 RX,板子的 RX 要接串口模块的 TX。

还有一个隐蔽的坑是串口助手的编码格式。有些串口助手默认用 GBK,有些用 UTF-8,如果程序里发的是中文,编码不对就会乱码。发英文和数字一般不会有这个问题。我习惯先用英文测试,确认通路正常再折腾中文。

4.2 程序下载后不运行:BOOT 引脚和复位电路

程序下载成功但板子没反应,先检查 BOOT0 是不是还在 VCC。下载完之后要把 BOOT0 拨回 GND,否则芯片会一直停在 Bootloader 里,不执行你烧的程序。这个坑几乎每个新手都会踩一次。

如果 BOOT0 没问题,检查复位电路。有些板子的复位按键质量不好,按下去弹不起来,芯片一直处于复位状态。用万用表测一下复位引脚的电平,正常应该是 3.3V,按下按键变成 0V。如果一直是 0V,那就是复位电路有问题。

还有一种情况是程序里把 SWD 引脚复用了,导致下载一次之后就连不上了。解决办法是用串口下载一个空程序,或者用复位下载法。预防措施是在程序开头加一段延时,给调试器留出连接时间。

4.3 Keil 编译报错:常见错误代码对照

Keil 编译报错信息有时候不太直观,我整理了几个常见的错误和解决办法:

错误提示可能原因解决办法
cannot open source input file头文件路径没加在 Options for Target 的 C/C++ 里添加 Include Paths
undefined symbol源文件没加到工程把对应的 .c 文件添加到工程目录树
flash download failed调试器配置不对检查 Debug 选项卡里的调试器型号和 Flash 算法
no target connected调试器没识别到芯片检查接线、驱动、BOOT 引脚
section .text overflowFlash 空间不够优化代码或者换 Flash 更大的芯片

这些错误看着吓人,其实原因都很简单。我建议每次报错先看第一行,Keil 的报错信息第一行通常是最关键的,后面的都是连锁反应。

4.4 延时函数 delay 卡死的原因

delay 函数卡死是 STM32F103 新手常见问题,尤其是用 SysTick 做延时时。卡死的原因通常是中断优先级配置不对,或者 SysTick 的时钟源没选对。STM32F103 的 SysTick 可以选择 HCLK 的 1/8 或者 HCLK,如果选错了,延时时间会差 8 倍,但一般不会卡死。

真正会卡死的情况是:在中断服务函数里调用了 delay,而 delay 又依赖中断,形成了死锁。解决办法是不要在中断里用延时,或者用不依赖中断的延时方式,比如简单的 for 循环空转。虽然 for 循环延时不准,但在中断里用足够了。

还有一种情况是 delay 函数里用了 while 等待标志位,但标志位一直不置位。这通常是初始化没做对,比如 SysTick 没使能,或者重装载值设成了 0。检查一下 SysTick_Config 的返回值,如果是 1 说明重装载值太大了,超出范围。

5. 从点亮 LED 到做出一个小项目

5.1 按键输入:消抖和中断两种方式

按键是继 LED 之后第二个要掌握的外设。STM32F103 的按键处理有两种方式:轮询和中断。轮询是在主循环里不断检测引脚电平,中断是配置外部中断,按键按下时触发中断服务函数。

轮询方式简单,但占用 CPU 时间,而且需要消抖。消抖有硬件消抖和软件消抖两种,硬件消抖是在按键两端并一个电容,软件消抖是检测到按下后延时 10ms 再检测一次。我一般用软件消抖,加个 10ms 的延时,简单有效。

中断方式响应快,但配置麻烦一点,要开 AFIO 时钟、配置 EXTI、配置 NVIC。这里有个坑,STM32F103 的 EXTI 线是复用的,PA0 和 PB0 共用 EXTI0,不能同时用。配置的时候要注意引脚编号,别冲突了。

5.2 串口通信:收发数据和 printf 重定向

串口是 STM32F103 最常用的通信接口,调试、通信、下载都靠它。配置串口的步骤是:开 GPIO 时钟和 USART 时钟,配置 GPIO 为复用推挽输出和浮空输入,配置 USART 的波特率、数据位、停止位、校验位,最后使能 USART。

printf 重定向是串口调试的利器,可以把 printf 的输出重定向到串口。具体做法是重写 fputc 函数,在里面调用 USART_SendData。这样就能用 printf 打印变量值,比用串口助手手动发数据方便多了。注意要在 Keil 的 Target 选项里勾选 Use MicroLIB,否则 printf 可能不工作。

串口接收数据一般用中断方式,每收到一个字节触发一次中断,把数据存到缓冲区里。如果数据量大,可以用 DMA 加空闲中断的方式,效率更高。STM32F103 的 USART1 支持 DMA,配置好之后 CPU 几乎不占用。

5.3 定时器:PWM 输出和定时中断

定时器是 STM32F103 最强大的外设之一,通用定时器 TIM2 到 TIM5 都是 16 位的,支持向上、向下、中央对齐三种计数模式。定时器最常用的两个功能是定时中断和 PWM 输出。

定时中断的配置步骤是:开定时器时钟,设置预分频器和自动重装载值,使能更新中断,配置 NVIC。定时时间的计算公式是:定时时间 = (预分频器 + 1) × (自动重装载值 + 1) / 时钟频率。比如 72MHz 时钟,预分频 7199,重装载 9999,定时时间就是 1 秒。

PWM 输出的配置稍微复杂一点,要设置 PWM 模式、输出比较极性、使能输出。PWM 频率的计算公式是:频率 = 时钟频率 / ((预分频器 + 1) × (自动重装载值 + 1))。占空比 = 比较值 / (自动重装载值 + 1)。用 PWM 控制 LED 亮度或者电机速度都很方便。

5.4 做一个超声波测距的小项目

把前面几个外设串起来,可以做一个超声波测距的小项目。超声波模块常用的是 HC-SR04,工作原理是发一个 10us 的高电平触发信号,模块发出 8 个 40kHz 的脉冲,然后等待回波。通过测量回波高电平的时间,乘以声速再除以 2,就是距离。

实现思路是用定时器输出一个 10us 的高电平触发信号,然后用定时器输入捕获功能测量回波的高电平时间。输入捕获的配置比 PWM 输出复杂,要设置捕获边沿、捕获通道、中断。测到时间后,用公式 距离 = 时间 × 340 / 2 / 10000 计算,单位是厘米。

这个项目涉及 GPIO 输出、定时器输出、定时器输入捕获、串口打印,是一个很好的综合练习。做完这个,STM32F103 的基础外设基本就摸清楚了。

6. 入门之后往哪个方向走

6.1 标准库转 HAL 库的过渡

标准库玩熟之后,建议花点时间了解一下 HAL 库。现在 ST 的新芯片基本只支持 HAL 库,标准库已经停止更新了。HAL 库的代码风格和标准库差别挺大,但核心概念是一样的,都是配置寄存器,只是封装方式不同。

过渡的时候重点看这几个文件:stm32f1xx_hal_gpio.c、stm32f1xx_hal_uart.c、stm32f1xx_hal_tim.c。HAL 库的函数名比较长,但命名有规律,比如 HAL_GPIO_WritePin、HAL_UART_Transmit。用 CubeMX 生成一个工程,对照着看,很快就能上手。

6.2 从 F103 到其他 STM32 系列的迁移

STM32F103 是入门的好选择,但实际项目中可能会用到其他系列,比如 F4 系列性能更强,G0 系列性价比更高,H7 系列适合高性能场景。从 F103 迁移到其他系列,核心概念是通的,主要差异在时钟树、外设数量和寄存器地址上。

迁移的时候建议先看芯片的参考手册和数据手册,重点看时钟树和外设框图。然后用 CubeMX 生成一个工程,对比一下和 F103 的差异。有 F103 的基础,迁移到其他系列一般一两周就能上手。

6.3 进阶方向:RTOS、通信协议、电机控制

STM32F103 的资源有限,跑 RTOS 有点吃力,但可以跑 FreeRTOS 的裁剪版。学 RTOS 的好处是理解任务调度、信号量、消息队列这些概念,对后面做复杂项目有帮助。

通信协议方面,CAN、485、Modbus 都是工业上常用的,STM32F103 自带 CAN 控制器,做 CAN 通信很方便。电机控制方面,F103 可以驱动步进电机和伺服电机,用 PWM 加定时器就能实现。

我自己的路径是:先做几个传感器的小项目,把 GPIO、串口、定时器、ADC 都摸一遍,然后学 FreeRTOS,再做 CAN 通信的项目。每一步都不用急,把当前的东西吃透再往下走。

6.4 一些实用的学习资源和习惯

最后分享几个我自己的学习习惯。第一,每学一个外设,先看参考手册对应的章节,再看例程,最后自己写一遍。第二,遇到问题先搜错误信息,大部分问题别人都遇到过。第三,准备一个笔记本,记录踩过的坑和解决办法,下次遇到直接翻。

资源方面,ST 官方的参考手册和数据手册是最权威的,虽然厚但值得翻。正点原子和野火的教程适合入门,讲得比较细。B 站上也有很多免费的 STM32 教程,选一个播放量高的跟着做就行。

提示:不要贪多,STM32F103 的外设很多,但常用的就那几个。把 GPIO、串口、定时器、中断、ADC 这五个玩熟,就能做大部分入门项目了。剩下的用到再学,不用一次全啃完。

板子买回来只是开始,真正有价值的是动手做。哪怕只是点亮一颗 LED,也比看十篇教程强。我见过太多人收藏了一堆资料,板子却一直在盒子里躺着。STM32F103 这块板子不贵,但能学到的东西很多,从寄存器操作到 RTOS,从裸机到通信协议,足够玩上大半年。别想着一口吃成胖子,先把第一个 LED 点亮,后面的路自然就清晰了。

返回列表