☰
STM32开发板入门:开箱点灯到串口调试的实战与避坑指南
2026/10/1 15:04:12 网站建设 项目流程

板子到手那天晚上,我把防静电袋拆开,一块蓝色的STM32F103C8T6最小系统板躺在里面。说实话,这种几十块钱的蓝色小板子在网上已经被拍烂了,但当你真把它拿在手里,准备从零开始学嵌入式时,第一反应往往是:"然后呢?芯片上这么多引脚都是干嘛的?要不要先写个点灯?"

如果你也刚把STM32开发板买回来,或者正在51单片机和STM32之间犹豫,这篇就从我的真实经历出发,讲讲拿到板子第一周应该做什么、怎么做,以及几个我踩得最深的坑。全文不绕弯子,直接给路线和代码,新手照做就能跑起来。

1. 为什么兜了一圈还是选了STM32F103这个"老家伙"

1.1 芯片便宜、资料多,生态成熟是最大理由

STM32F103C8T6这颗芯片到今天已经不是什么新鲜东西了,但它在嵌入式学习圈里的地位依然稳得很。原因很简单:一是便宜,单颗芯片几块钱,一块带下载器的最小系统板也就几十块,学习成本极低;二是资料多,十多年前的帖子、教程、开源项目几乎都用它做例子,你遇到百分之九十的问题,网上都有人问过、回答过、踩过坑。

我大学那会儿用过STC89C52,后来直接跳到STM32时,最直观的感受就是外设丰富:GPIO、定时器、ADC、USART、SPI、I2C、CAN、USB都集成在一块芯片里。你做超声波测距、OLED显示、电机控制、CAN总线通信,全都能在一块F103上搞定,不用频繁换芯片。对想通过做项目提升动手能力的人来说,这就是一台"嵌入式瑞士军刀"。

1.2 开发板怎么挑:我踩过的一次对比

刚买板子时,我在网上看到两大类,一类是蓝板子风格的最小系统板(通常叫Blue Pill风格),主控是STM32F103C8T6,48个引脚,板载晶振、LDO稳压、USB座,卖家通常附送ST-Link下载器;另一类是正经的入门级开发板,比如正点原子mini板、野火指南者、普中板子等,主控是STM32F103RCT6或ZET6,引脚更多,板载LED、按键、USB转串口,甚至板载ST-Link。

我的建议是:预算紧就买C8T6最小系统板加ST-Link V2,总共最多四五十块,够你把GPIO、串口、定时器、I2C全部学完。但如果你知道自己后面要做稍微复杂的项目,比如带TFT屏幕、文件系统、网络协议栈,那直接上一块ZET6的板子会省心很多。C8T6只有64KB Flash、20KB RAM,跑个小型操作系统勉强,做复杂应用容易撞到容量天花板。

另外提醒一句,不要被"板载资源多"迷惑。开发板好不好,核心看主控芯片和下载器是否可靠,板载LED多了几个对学习没有本质帮助。

板型常见主控引脚数Flash/RAM适合人群
最小系统板C8T648脚64KB / 20KB想低成本入门、喜欢自己搭电路
入门开发板RCT6 / ZET664~144脚256KB~512KB / 48~64KB想省事、跟教程、做毕业设计
核心板+扩展板视厂家不等不等进阶项目、验证原型

1.3 拿到板子前,建议顺手备齐这些小工具

买板子时我顺手拿了这些:ST-Link V2下载器、USB转TTL串口模块、杜邦线一捆(公对公和公对母都要)、面包板、几颗LED灯珠、按键、电阻,还有一块万用表。后面做按键输入、串口通信、外设驱动时,这些都是每天要用的东西。

万用表容易被忽略,但它能救你很多次。板子没反应时,先量一下3.3V和GND之间是不是有短路,再量一下USB口的5V是否正常。很多"板子坏了"的案例,最后查出来都是杜邦线插错了、地线没接、灯珠插反之类的小问题。

2. 开箱先别急着通电:硬件细节与引脚识别

2.1 确认芯片第一脚:新手最容易忽略的事

我第一次拿到芯片时,盯着丝印上的小圆点愣了半天。STM32F103C8T6是LQFP48封装,芯片正面左下角会有一个圆形凹坑或者斜切角,这个标记下方对应的位置就是第一脚,然后按逆时针方向数下去,依次是1、2、3……直到第48脚。

这个知识看起来基础,但真出事也就出在这儿。插面包板时如果把芯片方向搞反,通电瞬间就可能冒烟。我的习惯是:上电之前先用手电筒照着芯片,找到圆点,再确认该脚对应的电路是不是和原理图一致。哪怕只是插个排针,也要确认"脚1"对的是丝印上的哪个标号。

顺便提一个网上问得很多的问题:怎么确认芯片某个具体引脚是干什么的?最靠谱的办法是打开STM32F103C8T6的数据手册(Datasheet),找到"Pinouts"章节,它会列出每个引脚的序号、名称、复用功能。不要只靠记忆,因为同一个引脚在不同封装和不同复用配置下,功能差异很大。

2.2 供电、晶振和BOOT跳线的门道

C8T6最小系统板上有三个供电来源:USB口的5V、3.3V排针、外部电源接口。板载稳压芯片一般把5V转成3.3V供给MCU。用USB供电拉几个LED没问题,但如果外接舵机、WiFi模块这类电流大户,务必用外部电源,别指望USB口扛得住,否则会出现复位、乱码、花屏。

晶振方面,F103核心板通常焊了一个8MHz的外部晶振,作为HSE时钟源。系统内部还有一个8MHz的HSI,如果外部晶振没焊或烧录配置出了问题,默认可以切换到内部振荡器,但USB通信、CAN通信等外设对时钟精度有要求,最好还是把外部晶振搞定。

BOOT0和BOOT1两个跳线帽也要搞清楚。BOOT0拉高时,芯片从系统存储器启动,这时候配合串口可以烧录ISP程序;BOOT0拉低时,从FLASH启动,正常跑你的程序;BOOT1拉高且BOOT0拉高时,从RAM启动,一般调试用。平时使用,BOOT0默认拉低就行。

2.3 上电前先过我自己的检查清单

我不太建议拿到板子先急着插USB。先做三件事:

  • 检查跳线帽位置,确认BOOT0在低电平、BOOT1在低电平。
  • 检查排针焊接面有没有焊桥、有没有杜邦线残留。
  • 用万用表蜂鸣档量一下3.3V和GND之间有没有短路。

确认没问题后再上电。上电后留意板载电源指示灯是否稳定亮起,如果一闪一闪或者亮度忽高忽低,大概率是USB供电不稳,换根线试试。这个简单习惯帮我规避了很多不必要的返工。

3. 开发环境搭建记录与芯片包安装的那些坑

3.1 Keil MDK安装与芯片包下载

我用的第一套环境是Keil MDK5加ST-Link,也是目前资料最多的组合。到Keil官网下载安装包后,安装时尽量不要把路径放在C盘系统目录里,更不要有中文路径,比如"C:\Program Files (x86)\Keil_v5"这种默认路径没问题,但"D:\工程\xxx"里的中文路径在编译时可能闹脾气。

MDK装好后只是个空壳,还需要安装STM32F1系列的芯片支持包。打开Pack Installer,搜索"STM32F1",找到"STM32F1xx Series"点击Install即可。如果在线安装慢或者失败,可以从官网下载对应的.pack文件,双击后等待MDK自动导入,效果一样。

我遇到过一个典型问题:装了MDK但新建工程时芯片列表里找不到STM32F103C8,就是因为支持包没装好。先去Pack Installer左上角确认"Installed"里有没有STM32F1系列,没有就重新安装一次。芯片包文件版本最好和MDK版本兼容,不用追新,稳定够用就好。

3.2 芯片包安装失败怎么办

在线安装失败的原因很多,常见的是网络不通或杀毒软件拦截。我的处理办法是手动下载.pack文件,然后放在一个临时目录,双击安装。如果双击没反应,检查是不是文件没有下载完整,再看MDK是否在管理员权限下运行。

还有一类报错很迷惑:提示缺少某某组件,但实际是生成的工程文件里包含了旧版本的库文件。解决思路一般是:重新解压一个干净的标准库/官方例程,对应好自己的芯片型号,再把源文件添加进去,不用迷信"全能模板"。模板越花哨,出问题时越难排查,新手阶段用最简单的工程模板最省心。

3.3 更现代的路线:VSCode + EIDE + ARM GCC

如果你之前用VS Code写代码,受不了Keil的老界面,也可以直接用VSCode搭一套免费的STM32开发环境。思路是这样的:VSCode安装EIDE插件,用于管理工程结构;ARM GCC工具链负责编译;OpenOCD加ST-Link负责调试下载。

这套组合的优势是界面现代、代码提示好、开源免费,缺点是配置工作量明显更大。EIDE里要新建的是"Keil MDK v5"工程还是"CMake"工程,刚开始容易搞混。我的建议是:新手先乖乖用Keil MDK跑通第一个点灯程序,等对工程结构、链接脚本、启动文件都有概念了,再迁移到VSCode。不要一开始就两头折腾,容易把自己劝退。

方案上手难度调试体验适用场景
Keil MDK + ST-Link低集成调试,界面老但稳定新手入门、看教程跟做
VSCode + EIDE + OpenOCD中需要配置launch.json习惯VS Code、想用Git管理
STM32CubeMX + Keil低CubeMX生成初始化代码快速起工程、转HAL库

4. 从点灯到串口:第一个工程的完整路线

4.1 新建工程模板的两种思路

STM32编程有两条常见路线:标准外设库和寄存器操作。初学时我建议先用标准库,因为函数封装清晰,看名字就知道在干什么;但至少要写一遍寄存器版点灯,知道这些函数背后到底操作了哪些寄存器,不然以后遇到库函数不解决的问题,会手忙脚乱。

标准库新建工程的大致步骤:新建目录,把标准库的CMSIS和固件库源码复制进去;在MDK里新建工程,选择对应芯片;添加启动文件startup_stm32f10x_md.s;添加main.c和固件库源文件;在C/C++选项卡里添加头文件路径,并定义STM32F10X_MD宏。核心就这几步,网上教程很多,照着做就行。

我自己常用的目录结构是:

project/ ├── User/ // main.c、stm32f10x_it.c ├── Core/ // 启动文件、核心寄存器定义 ├── Periph/ // 标准外设库源码 ├── Hardware/ // 自己写的LED、UART、传感器驱动 └── Doc/ // 笔记和接线图

这种分法在项目变大时特别有用,每类文件都有固定位置,找问题快。

4.2 点灯代码实战(标准库)

下面这段是我第一个点亮LED的完整代码,以最小系统板PC13口为例。很多板子把LED接在PC13,正极通过限流电阻连3.3V,负极接引脚,所以拉低电平点亮。

#include "stm32f10x.h" void Delay_Init(void); int main(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIOC时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 配置PC13为推挽输出,最大速度50MHz GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); while (1) { GPIO_SetBits(GPIOC, GPIO_Pin_13); // 熄灭 Delay_Init(); GPIO_ResetBits(GPIOC, GPIO_Pin_13); // 点亮 Delay_Init(); } }

这里最容易被忽略的是第一步:使能时钟。STM32的所有外设默认都不上电,你要用哪个外设,先打开它的时钟开关。很多新手写代码不查时钟,结果代码看起来没错,芯片就是没反应,十有八九是这里漏了。

Delay_Init的具体实现可以用SysTick定时器做,下面会专门提到。写好后编译,再通过ST-Link连接板子,点击Flash Download即可。如果一切正常,你会看到板载LED开始闪烁,那一刻的成就感比看一百段教程都强。

4.3 串口输出:让开发板"开口说话"

点灯之后,第二件事就是把串口打通。串口是你观察程序运行状态最直接的工具,后面排错都要靠它。

用标准库初始化USART1,通常就是配置PA9为复用推挽输出、PA10为浮空输入,然后设置波特率、数据位、停止位,最后使能。如果你想把printf重定向到串口,还需要实现一个fputc函数。

#include <stdio.h> int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, (uint8_t)ch); return ch; } void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_RX | USART_Mode_TX; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); }

测试的时候,把USB转TTL模块的RX接到PA9(TX),TX接到PA10(RX),GND共地。串口助手里选好COM口,波特率设为115200,就能看到输出。如果出现乱码,先查波特率是不是一致,再查接线,最后查系统时钟频率是否正确。这三个原因占了乱码问题的九成。

5. 烧录不进、延时卡死、JTAG失效:新手最容易踩的三个坑

5.1 编译成功却烧录不进:一次完整排查链路

网上最经典的一个现象是:VSCode或Keil里编译一点问题没有,一烧录就报错,类似"Flash Download failed - Cortex-M3"或者"Error: Flash Download failed - Target DLL has been cancelled"。这种问题我遇到过多次,每次都在同一套链路里排查。

第一步,确认下载器有没有被识别。打开Keil的Options for Target -> Debug,选ST-Link后点击Settings,如果看到IDCODE和芯片型号,说明接线和驱动没问题;如果提示找不到设备,那问题在前端连接。

第二步,检查四根线。SWD只需要SWDIO、SWCLK、GND三根就能烧录,加上NRST复位更稳。杜邦线接触不良很常见,尤其是面包板上的线,松一下就会出现偶发识别失败。

第三步,检查芯片供电。如果板子只接了ST-Link没接VCC,芯片没电,当然烧不进去。ST-Link有时能提供3.3V输出,但输出能力弱,接了外设后电压被拉低,也会出现连接不稳定。

第四步,检查Flash下载算法。在Keil的Utilities -> Settings里,Flash Download列表要包含对应芯片的算法,比如C8T6用STM32F10x Med-density 64K Flash。选错算法,烧录会在擦除阶段直接失败。

我见过有人卡了一天,最后发现是ST-Link的USB线有问题,换一根带屏蔽的短线就好了。所以遇到烧录不进,先从最简单的线材开始排除,别一上来就怀疑芯片坏了。

5.2 禁用JTAG后程序刷不进的教训

这个坑相信很多老玩家都踩过。为了把JTAG引脚解放出来做普通GPIO,我在程序里写了类似这样的代码:

GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);

结果就是,程序下载进去之后第二次再也连不上ST-Link。因为SWJ引脚被完全禁用了,下载器也没法强制接管,板子变成了"砖头"。这个坑的根源在于,SWJ_JTAG和SWJ_SWD是两个概念,如果只是想让PB3、PB4等引脚做GPIO,尽量保留SWD功能。用GPIO_Remap_SWJ_JTAGDisable而不是GPIO_Remap_SWJ_Disable,就能把SWD留下来。

真遇到程序里写了禁用SWJ导致连不上,我最后是用这个办法救回来的:把BOOT0拉高,通过串口ISP方式连接芯片,用串口下载一个空程序或正常程序,然后恢复BOOT0,再用ST-Link连接。或者用STM32CubeProgrammer连接时选择"Connect under reset",配合硬件复位脚。

这个教训让我明白,玩嵌入式一定要留后路。别在项目初期就激进地禁用调试接口,尤其是在还没跑通稳定烧录流程的时候。

5.3 延时函数卡死与时钟配置的关系

另一个高频问题是延时函数卡在while循环里出不来。我一次写delay_ms函数时,照着网上模板抄,结果调用后程序完全卡死。排查发现原因在SysTick的配置上:SysTick_Config(SystemCoreClock / 1000)里的SystemCoreClock值,和实际运行时钟对不上。

如果外部晶振没有起振,或者系统时钟用的是内部HSI,但代码里还按外部8MHz的倍数计算,那SystemCoreClock就和实际时钟差出好几倍,导致SysTick中断等待时间异常。

正确的做法是,先确认RCC_ClockSecuritySystem的状态,再看SystemCoreClock宏由哪个时钟源决定。最省心的是直接把延时函数写成基于SysTick的标准模板:

static __IO uint32_t TimingDelay; void Delay_Init(void) { if (SysTick_Config(SystemCoreClock / 1000)) // 1ms 中断一次 { while (1); } } void SysTick_Handler(void) { if (TimingDelay != 0) TimingDelay--; } void delay_ms(uint32_t ms) { TimingDelay = ms; while (TimingDelay != 0); }

这套流程的关键是SysTick_Handler要存在,而且标准库里还要有对应的中断响应。如果没有把SysTick_IRQHandler做好,延时同样会卡死。以后遇到"程序卡在delay里",先查时钟、再查中断,基本都是这两件事。

6. 进阶路线:从会点灯到能做完一个毕业设计

6.1 学习顺序建议:GPIO、定时器、中断、通信

不少新手学完点灯就不知道下一步干什么,我的建议是按这个顺序走:

  • 第一阶段:GPIO输出输入,LED、按键、蜂鸣器。
  • 第二阶段:定时器,先学定时中断做精准延时,再学PWM输出控制LED亮度、舵机角度。
  • 第三阶段:外部中断和NVIC,理解中断优先级、抢占优先级和子优先级。
  • 第四阶段:串口收发,把这套能力和调试习惯固化下来。
  • 第五阶段:ADC采集、I2C和SPI通信,接温湿度传感器、OLED屏。
  • 第六阶段:把CAN总线、USB协议这些复杂外设加入,做点有含金量的项目。

这个顺序的本质是"从单点控制到系统交互"。GPIO只能控制单个引脚的高低电平,定时器让你明白时间维度怎么处理,中断让系统能响应突发事件,通信则把MCU和外部世界连接起来。每一步都为下一步做铺垫。

6.2 值得复现的经典小项目

网上搜"stm32超声波测距",大概率会看到HC-SR04加OLED的组合。HC-SR04触发后发送超声波,接收回波后返回一个高电平,脉宽对应距离。用定时器输入捕获测量高电平时间,再换算成距离,逻辑清晰,是学习定时器捕获的绝佳项目。

另一个我很推荐的是DS3231时钟模块,通过I2C读时间,再用OLED显示时分秒。这个项目能把I2C时序、数据帧解析、显示驱动全串起来,做完后你对"通信协议"的理解会完全不一样。

做电机控制的话,五线四相步进电机也很有意思。一个电机有四相绕组,按固定顺序给脉冲就能转,代码里本质就是一个相位切换数组加定时器中断频率控制。别看它简单,对理解时序、状态机、中断协调帮助很大。

想挑战CAN通信的朋友,注意两个坑:CAN总线两端必须各加一个120欧姆终端电阻,否则信号反射会导致通信不稳定;另一个是波特率配置要确保所有节点一致,哪怕误差几个百分点,数据量大了也会出现帧错误。

6.3 我个人的几个习惯与建议

最后分享几个我烧过几次板子、熬过几个夜后总结出来的习惯。

第一,每个新外设先写一个最小测试程序,验证通过再并到主工程里。不要一次把所有代码全写完,再来统一验证,那样子系统一多,定位问题难度指数上升。

第二,工程文件定期备份,保存一份能编译通过的原始模板。我电脑里始终放着一个"00_Template"工程,里面只有LED和串口最小配置,做新项目直接复制,几十秒就能开始写业务代码,不用每次重新配环境。

第三,多看数据手册和参考手册,少看玄学博客。网上教程偶尔会抄错、漏配置,最终权威还是ST官方手册。遇到库函数报错,先在头文件里找函数原型,再回手册看寄存器意义。

第四,别囤开发板。一块STM32F103加一块ESP32基本覆盖大学阶段绝大多数项目需求。板子买太多反而会分散精力,今天摸这个、明天摸那个,最后哪个都不熟。

这块蓝色小板的潜力,比大多数人想象的大得多。从点亮一个LED开始,到以后能独立做完毕业设计、参加电子设计竞赛,中间每一步都能让人成长很多。现在接好ST-Link,打开Keil,先把那个板载灯点亮再说吧。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询