ARTICLE DETAIL

资讯详情

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

STM32F103RCT6最小系统板搭建与Keil工程配置实战

STM32F103RCT6最小系统板搭建与Keil工程配置实战 第三个作品我不打算再跟 C8T6 死磕了。前两个作品都是在这家店里买的现成最小系统板上跑的功能一多64KB 的 Flash、20KB 的 RAM 就开始吃紧加个协议栈还得抠着删代码。所以这次换主控我直接上了 STM32F103RCT6做了一块属于自己焊接和配置的最小系统板从芯片选型到 Keil 工程配置再到第一次烧录整个流程走下来踩了不少坑也把一些原本模棱两可的细节彻底搞明白了。这篇就把整个“MCU 配置”过程完整记录下来重点集中在硬件层面的最小系统搭建以及软件工程里那些不对齐就编译报错、不识别芯片的细节。如果你也是第一次做 F103 系列的最小系统板或者正准备从 C8T6 换到资源更充足的 RCT6这篇应该能帮你少走不少弯路。1. 为什么最终选了 RCT6而不是继续用 C8T61.1 从“资源焦虑”开始的换芯决定很多教程喜欢把 C8T6 称为“入门神芯”这个说法本身没错但“入门”意味着你迟早会碰天花板。我前两个作品都是基于 C8T6 做的第一个是简单的温湿度采集第二个做了一个带 OLED 菜单的小工具到第二个作品后期Flash 就已经快写满了。C8T6 的资源就摆在那里64KB Flash、20KB SRAM、48 个引脚。对一个只用串口和 I2C 的小项目来说绰绰有余可一旦想加图形库、加 FatFS、加一份完整的日志缓冲立刻就会意识到“啊该换芯片了”。换芯片的时候我首先考虑过 ZET6但它的 LQFP144 封装对焊接和布线要求都高不少对于一个第三个小作品来说有点过度。F407 我之前也心动过但 F1 和 F4 的外设库体系完全不同为了一个作品全家老小迁库成本太高。最后落在 RCT6 上原因很直接芯片型号FlashSRAM封装定位STM32F103C8T664KB20KBLQFP48入门、小体积STM32F103RCT6256KB48KBLQFP64资源充足、全能型STM32F103ZET6512KB64KBLQFP144大资源、复杂项目RCT6 在中间这个档位Flash 是 C8T6 的四倍、SRAM 是两倍多引脚也多了 16 个。LQFP64 封装虽然比 LQFP48 大一圈但手工焊接完全可控热风枪手推焊或者拖焊都行。换言之它是“一次性把入门级痛点全部解决”的一颗芯片既不夸张也不憋屈。1.2 F103 家族里的“密度”概念选错直接编译报错在配置工程之前必须先搞清楚一个概念ST 把 F103 系列按 Flash 容量分成了 low-density、medium-density、high-density 三档。这个密度是软件工程配置的关键依据不是随便填的。C8T6 属于 medium-density对应启动文件startup_stm32f10x_md.s宏定义用STM32F10X_MD。RCT6 属于 high-density因为它有 256KB Flash对应启动文件startup_stm32f10x_hd.s宏定义用STM32F10X_HD。这两个文件虽然名字就一个词之差但里面的中断向量表排列完全不同——高密度芯片多了更多中断源Flash 容量相关的寄存器布局也不一样。选错之后最常见的症状是程序编译通过但烧进去后一进中断就跑飞或者干脆No target connected。很多人在这一步栽跟头就是因为把 C8T6 的工程直接改了 Device 型号其他没动。一个更反直觉的点F103ZET6 也是 high-density虽然它有 512KB Flash但它和 RCT6 用的启动文件是同一个hd.s。ST 把 256KB 到 512KB 都划进高密度档所以工程配置逻辑其实相当“宽”。这点在后续换成 ZET6 时也能沿用。2. 最小系统板搭建每一颗电容都有它的位置所谓最小系统板说穿了就是让芯片能跑起来的最少外围电路。但“最少”不等于“随便”每个元件的位置、容值、走线方向都直接影响稳定性。我在做这块 RCT6 最小系统板时主要拆成四个部分供电、时钟、复位/启动、调试接口。2.1 供电链路5V 进3.3V 出去耦电容要贴近引脚STM32F103 的额定供电电压是 2.0V 到 3.6V最常用的做法是 3.3V。从 USB 的 5V 转 3.3V我用的是 AMS1117-3.3这是国产开发板用得最多的线性稳压器极限压差小、输出纹波在轻载下可以接受。VIN接 5VVOUT出 3.3V输入和输出各并一个 10μF 钽电容和 100nF 陶瓷电容。钽电容负责低频储能陶瓷电容负责滤高频噪声两者不是冗余关系而是互补作用。真正的关键动作是去耦电容的布局。一些刚学画板的朋友喜欢把 100nF 电容统一摆成一排这在低速电路里问题不大但在 72MHz 主频下芯片每个电源引脚瞬间抽取电流的路径如果太长容易在 VDD 上产生毛刺。我的做法是每个 VDD 引脚旁边 3mm 之内放一个 100nF 的 0402/0603 电容电容地端就近打过孔到地平面。RCT6 是 LQFP64VDD 引脚有多个别图省事只放一个总电容。另外有一个经验最小系统板上最好留一个 3.3V 的测试点一上电先用万用表量一下这个点对地电压是否稳定在 3.28V 到 3.32V 之间。如果电压偏低先不要怀疑芯片多半是 AMS1117 虚焊或者输入电压不够。AMS1117 手册上写的压差约 1V如果 USB 线质量差5V 到板端只剩 4.3V输出 3.3V 就会很勉强。2.2 外部时钟8MHz 晶振与负载电容的计算逻辑STM32F103 内部有一个 8MHz 的 HSI 振荡器但它的精度只有约 ±1%而且温漂明显。实际项目要想让串口波特率不出现超过 1% 的偏差、让 USB 正常枚举必须用外部晶振做 HSE。RCT6 的 OSC_IN/OSC_OUT 是 PD0 和 PD1 的复用功能不能接反否则完全起振不了。晶振选型时8MHz 这个频率几乎是 F103 的“黄金频率”因为 72MHz 系统时钟正好是 8 的 9 倍PLL 倍频系数为 9 时刚好落在最高主频上。晶振的负载电容需要匹配不是随便装两个 20pF 就完事。负载电容的等效公式是CL (C1 × C2) / (C1 C2) Cstray其中Cstray是 PCB 走线和引脚之间的杂散电容经验值大概在 3pF 到 7pF 之间。我使用的是一款负载电容为 10pF 的 8MHz 晶振代入上面公式如果 C1C210pF(10×10)/(1010)5pF再加约 5pF 杂散电容正好等于 10pF。所以两个匹配电容各取 10pF 是最贴近晶振规格的。如果手里只有 20pF 电容也可以用但起振后的频率会略偏低几个 ppm串口在 115200 波特率下误差通常还在可接受范围内只是不建议长期这样用。晶振下方的铺铜要挖空避免寄生电容把振荡频率拉偏两边的匹配电容地端要直接回主地。这些都是影响稳定性的细节虽然最小系统板不是高频板但晶振这个位置值得多花半分钟处理。2.3 复位电路、BOOT 模式与调试接口三个细节一次说清复位电路是最容易“照抄但不知道为什么”的部分。NRST 引脚内部有上拉但内部上拉阻值较大、抗干扰能力有限外部再加一个 10kΩ 上拉到 3.3V并一个 100nF 电容到地。这个电容的作用是让复位引脚在按键按下时产生一段低电平时间确保 MCU 完整复位。轻触开关一头接 NRST、一头接地按下时拉低复位松开后经上拉电阻恢复高电平。BOOT0 和 BOOT1 的配置逻辑是三个启动模式的关键。STM32 上电后会采样 BOOT0 引脚电平并从 BOOT1实际引脚为 PB2采样另一个电平二者组合决定从哪里取指令执行BOOT0BOOT1启动来源典型用途0任意主 Flash正常运行程序10系统存储器进入串口 ISP Bootloader11SRAM调试/下载到 RAM我在板上把 BOOT0 做成一个 2 脚跳线默认跳到 GND需要串口下载时跳到 3.3V。BOOT1 则直接拉低因为实际项目中从 SRAM 启动的场景很少。这个设计可以避免每次手动去拨开关同时也不会影响 SWD 下载。调试接口我选了 SWD 而不是 JTAG理由是 SWD 只需要 4 根线SWDIO、SWCLK、GND、3.3V。RCT6 的 SWDIO 和 SWCLK 是 PA13、PA14这个接口哪怕板子上还有别的电路也不占用额外引脚。第一次画板时我在这个地方踩过坑SWDIO 和 SWCLK 焊反了结果 ST-Link 死活识别不到芯片后来用万用表一根一根对才发现是交叉接错。所以接线时一定要照着数据手册的引脚图慢慢来别凭印象。3. 硬件焊好后的软件配置芯片型号、启动文件、宏定义三者必须对齐3.1 标准外设库还是 HAL 库F1 老将的配置逻辑F103 系列现在有两种主流软件体系ST 早期的标准外设库Standard Peripheral Library和后来的 HAL 库。我在这个作品里选择的是标准外设库原因有几个一是 F1 系列的标准库在 STM32CubeMX 出现之前就已经非常成熟网上大量现成代码都是基于标准库写的二是标准库的代码层级更薄读起来可以直接看到寄存器的操作逻辑对“配置最小系统”这件事帮助很大三是这个项目规模不大不需要 CubeMX 的图形化初始化来管理复杂外设树。准备好标准外设库之后第一件事是解压库的压缩包找到Libraries和Project两个目录。工程模板只需要Libraries/STM32F10x_StdPeriph_Driver里的src和inc文件夹再加上CMSIS文件夹下的启动文件、系统文件和内核文件。整体结构类似这样Project/ ├── User/ │ ├── main.c │ ├── stm32f10x_it.c │ └── stm32f10x_conf.h ├── Libraries/ │ ├── CMSIS/ │ │ ├── startup_stm32f10x_hd.s │ │ ├── core_cm3.c │ │ └── system_stm32f10x.c │ └── STM32F10x_StdPeriph_Driver/ │ ├── src/ │ └── inc/ └── Keil/标准外设库里有些 .c 文件是每个工程都必须包含的比如stm32f10x_gpio.c、stm32f10x_rcc.c、stm32f10x_flash.c没用到某些外设时可以通过stm32f10x_conf.h里的条件宏减掉对应源码缩小编译体积。这个“配置只看需求、不盲目全选”的习惯能让你在以后的大项目里少很多麻烦。3.2 Keil 工程配置中三个必须对齐的地方用 Keil 打开工程后最容易出问题的就是三处器件型号、启动文件、预处理宏定义。这三者必须严格匹配芯片的真实资源少一个对不齐程序就可能编不过或者烧进去后表现诡异。首先是在 Options for Target 的 Device 选项卡里选择 STMicroelectronics 下的 STM32F103RC。注意不要选成 STM32F103RBRB 是 128KB Flash 的版本虽然启动文件同为hd.s但标准库的某些 Flash 大小判断宏可能会对不上。器件型号直接决定了 Keil 传给编译器的.S启动文件模板和链接脚本参数所以这里不能随手点。其次是启动文件。从库的CMSIS/Device/ST/STM32F10x/startup目录中选择startup_stm32f10x_hd.s把这个文件加入工程并在 Keil 工程管理器的对应分组里确认它已经存在于Startup分组。启动文件负责在进入main()之前完成栈指针、中断向量表、时钟初始化等动作。如果选成了md.s中断向量表会短一截RCT6 上某些中断触发时 PC 会直接跳飞。最后是 C/C 选项卡里的 Define。RCT6 需要填STM32F10X_HD, USE_STDPERIPH_DEVICE这里STM32F10X_HD告诉标准外设库当前芯片是高密度型号库里面的外设寄存器使能代码会按高密度芯片的内存和外设布局去做USE_STDPERIPH_DEVICE则确定库函数使用的标准命名。很多编译错误比如stm32f10x.h里报出莫名其妙的 undefined identifier十有八九是这两个宏没写全。3.3 从 8MHz 到 72MHz时钟树到底是怎么被配置起来的芯片上电的一瞬间系统默认使用的是 HSI内部 8MHz RC也就是说如果什么都不配置F103 默认主频其实是 8MHz而不是 72MHz。所有“最小系统板”工程的核心动作之一就是把时钟从 HSI 切换并倍频到 72MHz。在标准外设库里这个工作由system_stm32f10x.c里的SystemInit()完成。Keil 的启动文件会在调用main()之前先执行SystemInit()它通过读取SystemCoreClock相关的宏来决定最终配置。如果想确认系统的配置逻辑可以直接看这个文件的头部// system_stm32f10x.c 中定义的目标频率 #define SYSCLK_FREQ_72MHz 72000000SystemInit()内部的执行流程大致是先把 HSI 打开并等待就绪然后配置 Flash 的预取缓冲和等待周期72MHz 主频下 Flash 等待周期需要设为 2接着把 PLL 倍频设为 9、PLL 时钟源切到 HSE外部 8MHz 晶振等待 PLL 锁定后把系统时钟切换到 PLL最后更新SystemCoreClock。如果用寄存器来理解这一步实际上就是在操作RCC_CR和RCC_CFGR两个寄存器。RCC_CR里的HSEON位打开外部晶振PLLON位打开锁相环RCC_CFGR里的PLLMULL字段设为 9SW字段设为 PLL 作为系统时钟源。整个过程不是“点一下就 72MHz”而是“先让晶振稳定再倍频再切换”这也是为什么代码里到处都是while等待标志位就绪的原因。理解了这条链路以后换晶振频率或者改主频时就不会只会抄代码了。4. 第一次烧录的全过程从 ST-Link 接线到 “No target connected” 排查4.1 SWD 四线接法以及 Keil 调试器设置焊接、工程配置都完成之后就要面对最激动也最容易翻车的一步烧录。我用的是 ST-Link V2 烧录器接法非常简单ST-Link SWDIO → 板子 PA13ST-Link SWCLK → 板子 PA14ST-Link GND → 板子 GNDST-Link 3.3V → 板子 3.3V注意 3.3V 这根线ST-Link 上有两种输出一种是 3.3V一种是 5V板上如果已经用 USB 或者外部电源供电了就不要再接 ST-Link 的 3.3V否则两个电源并在一起轻则供电异常重则把 AMS1117 反向灌坏。我调试最小系统板时先只接 SWDIO、SWCLK、GND单独用 USB 给板子供电烧录器只负责传数据这样电源关系最简单也最不容易出问题。如果板子还没接通电源可以临时让 ST-Link 供电但仅限于调试阶段。Keil 里的设置是这样的点击 Options for Target → Debug 选项卡右侧选择 ST-Link Debugger然后点 Settings。在 Debug 子页面里Port 选择 SWMax Clock 可以先选 4MHz如果下载失败再往下降。Reset 选项在第一次下载时可以保持 Normal但芯片锁死后需要切到 Connect under Reset。这些设置保存后再打开 Download 选项勾选 Program 和 Reset and Run这样烧录完毕芯片会自动复位开始跑程序。4.2 “No target connected”典型排查链路我在第一次给 RCT6 烧录时就遇到了 Keil 报No target connected而且是那种不管点多少次都连不上的情况。这种问题通常不是 ST-Link 坏了而是板子上的某个小环节没到位。我的排查顺序是这样的第一步先量板的 3.3V。万用表探针放在 3.3V 测试点和 GND 之间如果电压没有或者只有零点几伏供电链路肯定有问题比如 AMS1117 焊反、USB 线是纯充电线没有数据脚、某个电容短路。先把电修好再谈烧录。第二步确认 ST-Link 指示灯状态。ST-Link V2 正常上电后 LED 会亮起插到电脑上设备管理器里应当出现STLink dongle。如果没有换一根数据线试试很多 ST-Link 是山寨克隆版驱动和固件容易出奇葩问题但 99% 还是 USB 线的问题。第三步用万用表蜂鸣档检查 SWDIO 和 SWCLK 之间是否有短路。这个很值得留意我之前有次焊最小系统板焊锡拖多了把 PA13、PA14 两个引脚之间桥接在了一起结果 ST-Link 怎么都连不上。刮开短路点之后瞬间恢复正常。第四步检查 BOOT0 跳线。如果 BOOT0 被跳到了 3.3V芯片上电后进入系统存储器模式虽然这种情况下 SWD 理论上仍能连上但也有部分山寨 ST-Link 在系统存储器模式下连接不稳定。直接把 BOOT0 跳到 GND保证从 Flash 启动再试一次。第五步如果以上都正常还是连不上就把 Keil 的 Reset 选项改成 Connect under Reset并且把 Max Clock 降为 1MHz。这个操作对芯片锁死、引脚复用冲突导致的无法访问特别有效。SWD 接口在芯片执行用户程序时如果被配置成了普通 GPIO默认状态下调试器是没法连接的而 Connect under Reset 会在复位期间抢占调试接口强制连上后清掉那些错误配置。这套排查链路走下来绝大多数“连不上”都能解决。如果还不行那就要怀疑芯片体质或者 ST-Link 克隆版兼容性可以换一个烧录器交叉验证也可以用串口 ISP 的方式先烧一个简单的跳灯程序确认芯片本身是活的。4.3 备用方案串口 ISP 下载与 BOOT0 跳线没有 ST-Link 或者 ST-Link 彻底罢工时F103 还有一个保底方案串口 ISP。原理是芯片出厂时在系统存储器里预置了一段 Bootloader当 BOOT01、BOOT10 上电时芯片会运行这段引导代码通过 USART1 接收外部发来的程序并写入 Flash。操作步骤不复杂先把 BOOT0 跳到 3.3VBOOT1 保持低电平按一下复位键让芯片进入系统存储器模式。然后找一个 USB 转 TTL 模块TXD 接板子的 PA10USART1 的 RXRXD 接板子的 PA9USART1 的 TXGND 共地。注意是交叉接不是同名相连。波特率选 115200打开 PC 上的上位机软件比如老牌的 FlyMcu 或 ST 官方的 Flash Loader Demonstrator选择生成的.hex文件擦除并烧录。烧完后把 BOOT0 跳回 GND复位即可运行。ISP 下载最大的优点是几乎不会失败——因为系统存储器里的 Bootloader 不受用户程序影响。缺点是速度慢而且需要手动跳线。但它作为 SWD 失联时的“救生通道”非常实用。我建议每个做过最小系统板的人都把这个流程走一遍不需要很熟练至少知道它有这个能力关键时刻能省一天时间。5. 配置验证与往后的扩展空间5.1 第一个程序的正确验证方式别只看灯亮烧录成功之后很多人的第一个程序是点灯但点灯其实只能说明芯片在工作不能证明时钟配置正确、Flash 等待周期没问题。我的做法是先用一个带延时误差标志的测试程序验证系统时钟。比如写一个 GPIO 翻转程序让 PA0 每隔 500ms 翻转一次然后用示波器或者逻辑分析仪去看实际波形周期。如果SystemInit()配置正确、外部 8MHz 晶振起振稳定方波周期应该非常接近 500ms。如果测出来明显偏快或偏慢很可能是系统时钟没有切到 PLL而是停留在了 HSI 8MHz那方波周期会偏到 4.5ms 左右如果频率偏高可能 PLL 倍频设错了。利用这种“带基准的验证”比单纯看见灯亮有意义得多。#include stm32f10x.h #include stm32f10x_gpio.h #include stm32f10x_rcc.h void Delay(void) { // 简单软件延时用于测试时钟频率 volatile uint32_t i; for (i 0; i 720000; i); } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); while (1) { GPIO_SetBits(GPIOA, GPIO_Pin_0); Delay(); GPIO_ResetBits(GPIOA, GPIO_Pin_0); Delay(); } }这个程序编译后正常的方波翻转周期在几百毫秒这个量级。如果项目里接入了 SysTick 或者 TIM2 做标准延时也可以把手动延时换成定时器再通过波形周期反推主频这一步对确认最小系统硬件是否可靠非常关键。5.2 用 MCO 引脚验证系统时钟比逻辑分析仪更直接的方式是使用芯片自带的 MCO 引脚。F103 的 PA8 可以复用为 MCO把内部时钟信号直接输出到引脚上这样只要一个示波器或者频率计就能看到时钟频率的真实值。我的做法是把 PA8 配置为复用推挽输出再用标准外设库的时钟输出函数选择时钟源。如果选择输出 HSE 8MHz示波器上能看到一个稳定的 8MHz 方波说明外部晶振起振正常、HSE 稳定就绪如果选择输出 SYSCLK72MHz那需要示波器带宽足够普通 100MHz 示波器测 72MHz 方波已经接近极限但至少能看到信号轨道存在。更稳妥的是输出 PLL 二分频后的 36MHz既能验证 PLL 锁定也不会超出常规示波器带宽太多。这一步在排查“程序烧进去了但串口乱码”的问题时特别有用。乱码很多时候不是波特率写错而是外部晶振根本没工作芯片跑在内部 8MHz 上串口实际波特率跟配置值差了一大截。先用 MCO 确认时钟源头没问题再排查波特率配置这是符合逻辑的排查顺序。5.3 从 RCT6 迁移到 C8T6/ZET6 时的资源对照RCT6 配置好之后往后可能会遇到两种迁移换到更小的 C8T6 做低成本批量或者换到更大的 ZET6 做复杂项目。这个迁移过程不只是换颗芯片那么简单主要差异在三个维度。芯片型号FlashSRAM引脚数启动文件宏定义STM32F103C8T664KB20KB48md.sSTM32F10X_MDSTM32F103RCT6256KB48KB64hd.sSTM32F10X_HDSTM32F103ZET6512KB64KB144hd.sSTM32F10X_HD引脚数不同意味着之前用的引脚定义大概率对不上比如 C8T6 的 PC13 上接了 LED换到 RCT6 后 PC13 还存在但可能同时多了 PC14、PC15引脚复用关系就要重新理一遍。启动文件和宏定义按上面表格做相应切换这是代码层面最核心的改动。外设方面RCT6 到 ZET6 是“只多不少”的方向所以从 RCT6 往 ZET6 迁反而简单只要把引脚数和更多外设用起来就行从 RCT6 往 C8T6 迁则要严格检查资源占用Flash、SRAM、外设中断向量都要重新算一遍预算。另外RCT6 和同封装的 RBT6128KB Flash是 pin-to-pin 兼容的PCB 不用改版只换芯片和启动宏。这个“同封装跨型号”的操作在库存紧张时非常实用——我曾经为了等一颗 RCT6 等了两周临时拿 RBT6 顶上去跑同一个程序只是因为 Flash 占用已经超过 128KB最后被迫精简了代码。从此我养成了一个习惯画板之前先把目标工程的全部代码编一遍看 Flash 和 SRAM 的最终占用率再决定用什么型号而不是先定型号再往里硬塞代码。这次做完 RCT6 最小系统板的配置我最深的体会是最小系统板虽然叫“最小”但每个环节的知识密度一点都不小。供电去耦、晶振负载电容、BOOT 模式、启动文件、时钟树、调试接口任何一个细节没搞清楚都会在某个时刻冒出来卡你一手。如果你也准备做自己的第一块 F103 最小系统板我的建议是别一口气把所有外设都画上去先按“电源 晶振 复位 SWD 一颗 LED”这个最小集合做一版跑通之后再逐步扩展。这样万一出了问题排查范围非常小一次就能定位到根因。
返回列表