
板子到手那天晚上我把防静电袋拆开一块蓝色的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风格主控是STM32F103C8T648个引脚板载晶振、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接到PA9TXTX接到PA10RXGND共地。串口助手里选好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先把那个板载灯点亮再说吧。