STM32F103+FreeRTOS排查实录:从“没反应”到揪出假芯片 插上电灯不亮仿真器连不上代码烧不进去这是玩 STM32F103 FreeRTOS 时最让人血压飙升的场景。更离谱的是有时板子明明能下载程序但上电后就是没反应连个 LED 都不闪。很多朋友第一反应是“我代码写错了”然后翻来覆去查 FreeRTOS 移植步骤改配置、换启动文件折腾几天一无所获。最后换一块新的 STM32F103C8T6焊上去一切正常。这时候才意识到问题很可能出在芯片本身——你手里那片极可能是“假芯片”。这里的“假”不是指那种完全不能用的废片而是一类很特殊的存在外观、丝印、引脚定义都和正品 STM32F103 一致但内部 Flash 时序、ID 读取、低功耗行为、甚至内核版本却和正品有细微差异。这些差异平时也许感觉不到一旦配合 FreeRTOS 这种依赖精确时钟和中断优先级控制的系统就会暴露成各种“没反应”“进不了任务”“偶尔死机”的玄学问题。这篇博文我会按一个完整的排查链路来写从“怎么判断芯片没反应是什么原因”到最小系统硬件检查再到下载环节、FreeRTOS 跑不起来的“伪死机”最后回到芯片真伪分辨和采购避坑。每一步都会解释为什么这样做也会把我在实际项目里踩过的坑和总结的经验放进去。适合刚入门 STM32FreeRTOS 的初学者也适合那些被奇怪问题折磨到怀疑人生的老手。1. 先别急着怀疑芯片给“没反应”分个类遇到问题第一件事不是换芯片而是把“没反应”这个模糊的症状拆开。芯片没反应和芯片没反应之间差别可能很大。我在排查这类故障时习惯先回答三个问题能不能下载上电后复位引脚/时钟引脚有没有波形代码是不是真的跑起来了1.1 用“能否下载程序”作为第一道分水岭如果 J-Link、ST-Link 或者 DAP-Link 能正常识别到芯片并且程序能烧进去说明芯片内核基本是好的电源和时钟树的核心部分大概率也没问题。这种情况下“没反应”通常是软件层面的问题或者某个外设初始化失败导致程序卡死在某个地方。这种问题聚焦 FreeRTOS 配置、启动文件、外设时钟即可。反过来如果下载器连芯片都识别不到报的是“No target connected”“Cannot connect to target”之类的错误那就要从硬件连接、供电、复位电路、启动模式这几个方向查。很多所谓的“假芯片”翻车点恰恰发生在这条链路上因为打磨片、翻新片的引脚氧化、内部 ESD 损伤会导致 SWD 信号质量差下载器时能连上时连不上。1.2 用可控现象区分“硬件死”和“软件死”还有一个实用的方法在代码最开头也就是进入 FreeRTOS 调度器之前驱动一个 LED 或者翻转一个 GPIO。如果上电后 LED 不亮甚至连复位瞬间的一点动静都没有问题大概率在硬件或者芯片本身。如果 LED 能亮但进入调度器后任务不执行、LED 不闪烁那问题就在 FreeRTOS 的任务创建、调度器启动、中断优先级配置上。这两种情况我都在实际项目中遇到过。有一次客户反馈“板子没反应”我远程让他用万用表量 3.3V 对地阻值结果发现只有十几欧是电源短路。另一次是 LED 能亮但任务永远不运行查到最后是中断优先级分组设置和 FreeRTOS 的要求冲突PendSV 和 SysTick 的优先级没设成最低导致调度器根本没法触发上下文切换。所以“没反应”三个字背后可能藏着完全不同的病因第一步永远是定性而不是盲目排查。1.3 建立自己的“最小可运行基准”排查 STM32F103 这类问题手边最好有一个“最小可运行基准”一块确定是正品、能稳定跑 FreeRTOS 的板子或者一个最精简的点灯程序只初始化时钟和 GPIO不进 RTOS。遇到客户或自己板子出问题时先用这个基准程序烧进去通过是否点灯来判断问题层级。这比任何高深理论都管用。我的习惯是任何一块新板子到手第一件事不是移植 FreeRTOS而是先烧一个不带 RTOS 的 GPIO 翻转程序确认下载链路、时钟、基本 IO 都正常。只有这一步通过了才会开始往里加 FreeRTOS。很多人一上来就同时引入新板子、新芯片、新 RTOS出了问题根本无法定位变量这是排障的大忌。2. 最小系统的“静默杀手”电源、时钟、复位、BOOT如果确认程序下载链路没问题但上电后依然没反应就得回到最小系统本身来查。STM32F103 的最小系统并不复杂电源、时钟、复位、BOOT 引脚四样东西缺一不可。但恰恰是这几个“最基础”的环节藏着大量容易被忽略的故障点。2.1 电源部分VDD/VDDA 和去耦电容的“反直觉”故障STM32F103 的电源引脚包括多个 VDD 和 VSS以及 VDDA/VSSA模拟电源、VREF如果有的话。很多人只接了 VDD 和 VSSVDDA 直接悬空或者没接好结果芯片工作异常、ADC 乱跳、内部复位不稳定CPU 跑飞表现就像“没反应”。另外一个大坑是去耦电容的位置和容值。STM32F103 的每个电源引脚旁边都应放一个 100nF 的陶瓷电容且要尽量靠近引脚放置。我见过不少板子为了布线方便把所有去耦电容集中放在电源入口处距离芯片引脚几厘米远高频噪声滤不掉芯片偶尔上电启动不起来但有时又正常——这种随机性故障最容易让人误以为是“芯片坏了”或者“买到假货”。还有一个容易忽略的点BOOT0 引脚内部有下拉电阻但外部如果接了电容或者走线过长可能导致上电时 BOOT0 电平不稳定。BOOT0 一旦上电瞬间被拉到高电平哪怕只有几毫秒芯片就会从系统存储器启动用户 Flash 里的程序根本不会运行表现同样是“没反应”。排查时用万用表量 BOOT0 的对地电压正常应为 0V跳线帽接 GND 时如果量出来有 1V 以上就要怀疑外部电路了。2.2 时钟8MHz 晶振不起振系统卡死在启动阶段STM32F103 上电默认使用内部 HSI8MHz RC 振荡器如果代码里配置了外部 HSE 晶振且配置了“HSE 启动超时等待”而外部晶振根本没起振程序就会卡在等待 HSE 就绪的 while 循环里表现出来就是“上电后完全没反应”。这在带 FreeRTOS 的项目里更隐蔽因为很多人用 CubeMX 生成工程时RCC 设置为 Crystal/Ceramic Resonator默认就启用 HSE如果晶振电路有问题程序根本走不到 main 函数后面的任何逻辑。排查方法不复杂用示波器或逻辑分析仪量 8MHz 晶振的 OSC_IN/OSC_OUT 引脚起振后应能看到类似正弦波的时钟信号幅值通常在几百毫伏到 1V 以上。如果没有示波器可以临时把代码里的时钟源切换到 HSI或者把 Keil、IAR 里的调试配置改成 low-power 模式观察是否能跳过 HSE 等待。如果切换到 HSI 后系统正常运行问题几乎可以锁定在晶振电路。晶振电路的电容负载也很关键。我的经验是 8MHz 晶振通常配 10~22pF 的负载电容但这和晶振本身的 CL 值有关。如果负载电容和晶振不匹配可能出现两种情况要么完全不起振要么起振但频率偏差大导致串口波特率错乱、FreeRTOS 的时基不准表现为任务调度异常、delay 超时甚至看起来像死机。2.3 复位电路NRST 引脚的外部电容和干扰STM32F103 的 NRST 引脚是低电平复位内部有上拉电阻和复位电路外部通常再接一个 100nF 电容到地以增强抗干扰能力。但有些板子为了省成本把这个电容省了或者用了 1uF 的大电容结果会导致上电复位时间异常芯片偶尔无法正常启动。还有一个不常见但很实际的问题有些翻新片因为内部复位电路老化或损伤对 NRST 的上电时序非常敏感。表现为第一次上电没反应按一下复位按钮之后又能正常运行。这种“按复位才能启动”的现象在很多旧料、拆机片中比较常见遇到这种情况基本可以断定不是正品新片。3. 下载环节的“至暗时刻”SWD 连接失败与芯片锁死在 STM32F103 的调试环节最让人抓狂的报错就是“Cannot connect to target”和“RDDI-DAP Error”。这两个错误我以前遇到时总以为是板子坏了后来才总结出它们各自的常见原因和针对性解法。3.1 目标板供电与接线方式SWD 不等于免供电很多人第一次用 ST-Link 或 J-Link 调试 STM32F103 时以为只要接 SWDIO、SWCLK、GND 三根线就能下载完全没考虑目标板供电问题。SWD 接口本身虽然有电源引脚但 ST-Link 的 3.3V 输出能力有限如果你的目标板上有其他外设比如传感器模块、OLED 屏、Wi-Fi 模块下载器根本带不动电压跌落会导致芯片上电不完全表现为“能识别但下载超时”或“时好时坏”。SWD 接线的抗干扰能力也经常被低估。SWCLK 和 SWDIO 这两根线的走线如果过长或者和小电压、大电流的电源线平行高速 SWD 通信就会被干扰。解决方法是降低 SWD 通信速率在 Keil 的 Settings 里把 Max Clock 从 4MHz 降到 1MHz 或更低或者把线束改成双绞线/屏蔽线。我调试飞线板时习惯把 SWD 速率直接设到 1MHz稳定性和速度的性价比最好。3.2 SWD 引脚被代码占用最经典的“自己锁死自己”这种情况在跑了 FreeRTOS 之后更容易出现初始化代码把 PA13/PA14 重映射成了普通 GPIO或者 JTAG/SWD 引脚被设置为复用功能导致调试器下一次无法连接。很多人以为“芯片坏了”其实是 SWD 功能被禁用了。解决方法是用 J-Link 的“Connect under Reset”功能或者把 BOOT0 拉高强制从系统存储器启动然后在复位期间连接调试器擦除 Flash。在 Keil 里如果遇到无法连接可以尝试以下几个步骤组合操作按住目标板复位键不放点击 Keil 的 Download 按钮在下载开始的瞬间松开复位键如果时序正确下载器会趁芯片运行在复位状态时建立连接然后擦除 Flash这个方法成功率很高但需要点手速。更稳妥的办法是使用 J-Link 的“Reset”模式配置选择“Hardware Reset”或“Connect under Reset”让调试器在复位期间抢占 SWD 接口控制权。3.3 读保护RDP与 Option Bytes被忽略的“假死”原因还有一类问题是读保护开启导致的。如果之前在调试选项里误开了读保护或者芯片是二手料比如从某工业设备上拆机下来的Flash 可能处于 RDP Level 1 保护状态此时 SWD 只能做全片擦除不能读写 Flash。表现是下载时提示“Flash Download failed - Target DLL has been cancelled”或“Cannot access memory”。处理方法是执行整片擦除Full Chip Erase这会解除读保护但也会清空所有用户数据和 Option Bytes 设置。使用 ST-Link Utility 或 J-Link Commander 都能操作。这里有个常见的坑某些 ST-Link 克隆版在解除读保护时不稳定擦除到一半失败导致芯片进入更深的保护状态需要先用串口 ISP 方式BOOT0 拉高接 USART1连接再执行擦除操作。如果你手里的“假芯片”是翻新料它的 Option Bytes 区域很可能是混乱的这种情况下用串口 ISP 也比 SWD 更可靠。4. FreeRTOS 跑不起来的“伪死机”启动文件、中断优先级与堆栈好如果硬件链路都查过了芯片也确定能下载、能点灯但一旦加上 FreeRTOS 就“死机”那就进入了我最想聊的环节。这个环节里很多问题的根源不是芯片本身而是 FreeRTOS 在 STM32F103 上的移植细节没到位。但注意兼容芯片和正品芯片在这些细节上的“容错能力”差异巨大正品可能勉强能跑兼容片直接挂这也是很多人误判“芯片是假的”的原因。4.1 启动文件选错等于系统时钟根本没配置STM32F103 有多个型号例如 C8T664KB Flash20KB RAM、RCT6256KB Flash48KB RAM、ZET6512KB Flash64KB RAM。不同容量的芯片对应的启动文件不同小容量用 startup_stm32f10x_ld.s中容量用 startup_stm32f10x_md.s大容量用 startup_stm32f10x_hd.s。如果用错了比如在 C8T6 上放了 hd 启动文件运行时中断向量表会错位程序一旦触发中断就会跑飞表现就是“点灯正常但 FreeRTOS 一跑就死”。而且启动文件里定义了初始堆栈大小Stack_Size和堆大小Heap_Size。如果 Stack_Size 设置太小比如默认 0x400而 FreeRTOS 的任务栈又比较大系统一旦进入更深层次的函数调用栈溢出会把相邻内存区域的数据踩掉导致虚拟的“随机死机”。这在调试时比“完全没反应”更隐蔽因为它是概率性发生的。4.2 中断优先级FreeRTOS 对 PendSV 和 SysTick 的硬性要求这是 STM32F103 移植 FreeRTOS 时最“反直觉”的一个点。ARM Cortex-M3 内核里FreeRTOS 要求 PendSV 和 SysTick 的中断优先级必须设置为最低数值最大同时还要把所有其他中断的优先级设置为数值小于等于某个值通常是对应 PORT 宏定义里的 configMAX_SYSCALL_INTERRUPT_PRIORITY。原因在于FreeRTOS 的上下文切换是借助 PendSV 完成的而 PendSV 是一个可挂起的中断它会在所有高优先级中断处理后在最低优先级线程模式下完成切换。如果 PendSV 的优先级不是最低那么在某个中断处理过程中触发了 PendSV而 PendSV 优先级高于当前中断就会出现“中断嵌套中的上下文切换”导致堆栈错乱系统跑飞。STM32F103 上还有个特殊之处它只使用了 NVIC 的 4 位优先级中的高 4 位且它的优先级分组通常设置为 NVIC_PriorityGroup_4全抢占优先级无子优先级。如果用 CubeMX 生成代码默认就是这样。但如果你从网上下载了一个别人移植的工程它的优先级分组可能被改成了 Group_2 或 Group_3这会导致 FreeRTOS 内部的临界区逻辑失效最终表现为随机死机。我的建