STMF103 FreeRTOS 板子“死机”了一周最后发现是芯片本身有问题。这事说出来挺丢人但我觉得很值得分享因为最近在好几个技术群里都看到有人问类似的问题代码看了无数遍逻辑没问题硬件连接也检查过了可芯片就是不跑。如果你也遇到这种“玄学故障”建议先别急着怀疑自己移植 FreeRTOS 的步骤花几分钟看看手里的芯片是不是“正经货”。这个事情是这样的我手里有一批 STM32F103C8T6 的小板子之前用裸机程序跑得挺好最近在移植 FreeRTOS前后折腾了好几天现象很统一——下载程序时提示成功但一按复位芯片就跟睡着了一样什么反应都没有。用 Keil 在线调试能连接上也能设置断点但一运行就死在 HardFault_Handler 里或者直接跑飞。后来换了一块从代理商正规渠道拿的芯片同样的代码、同样的工程一次性就跑起来了。到这里我才确定问题出在芯片本身而不是 FreeRTOS 的移植步骤。1. 芯片没反应先别怪 FreeRTOS这几个现象要先对号入座FreeRTOS 移植本身确实容易出问题但很多“芯片没反应”的情况根子不在系统移植而在芯片或硬件层面。我这里总结了几种常见现象你可以对照看看自己属于哪一种。现象一程序能下载但板子没有任何输出。LED 不亮、串口没数据显示无论按复位还是重新上电都一样。这种“下载成功但运行失败”的情况最先怀疑的应该是芯片的启动配置和复位电路。现象二在线仿真可以暂停但一全速运行就死在 HardFault。这种情况通常说明芯片的 Flash 里确实写入了程序但程序一跑起来就遇到非法指令或非法内存访问。常见的诱因有芯片 Flash 容量不匹配、Option Bytes 配置错误、堆栈指针 SP 初始值不对。现象三改用串口 ISP 下载发现读出的芯片 ID 是 0x00000000 或者是很奇怪的值。这说明 MCU 与调试器或串口工具之间的通信是通的但芯片内部的身份信息读不出来。正规的 STM32 芯片读出来的 Device ID 应该是一串有意义的十六进制数。现象四用万用表量 VDDA、VDD 引脚电压正常但 NRST 复位引脚一直是低电平。如果复位引脚被外部电路强制拉低芯片永远处于复位状态自然跑不了程序。现象五同一个程序在这个板子上不行换到另一块板子上就好了。这个现象最迷惑人因为很容易让人误以为是“静态干扰”“接触不良”之类的硬件问题但实际情况往往就是芯片体质差异过大正品芯片和仿品芯片在电气特性上差距非常明显。我之前遇到的坑就是现象一和现象二同时出现。刚开始怀疑是 FreeRTOS 配置文件写错了把configMINIMAL_STACK_SIZE从 128 改成 256没用怀疑是启动文件选错了换成 HD 版本还是不行甚至怀疑是 Keil 的 Flash Download 配置有问题重新配置了 Reset and Run依然没反应。最后换了块板子同一份代码直接跑通才意识到不是软件问题。这里要特别提醒一下芯片“没反应”和“程序跑飞”是两回事。芯片没反应意味着连最基本的启动过程都没完成程序跑飞说明 CPU 的取指和执行已经乱了。排查优先级上先确认芯片能不能正常启动再考虑 FreeRTOS 的任务调度是否正常这个顺序一定不能反。2. 被忽视的芯片“身份”问题如何识别翻新或仿冒的 STM32F103聊到“假芯片”这个事得先简单说一下 STM32F103 这个型号为什么会成为重灾区。F103 系列是意法半导体出货量极大的产品线尤其 C8T6、CBT6 这些型号在工业控制、消费电子、DIY 圈子里用得太多了。需求大了市场上就出现了一批把旧芯片打磨掉丝印再重新印字、或者直接用低规格芯片冒充高规格芯片的“套片”和“翻新片”。那么怎么识别手里的芯片是不是正品我这里分享几个实操方法不需要高端仪器有电烙铁、万用表和调试器基本就够了。2.1 看丝印与外观是最直观的第一道检查正品 STM32F103C8T6 的表面丝印字体清晰、笔画均匀芯片顶部的“STM32F103”字样和下面的“C8T6”字样位置和大小都有固定规格。翻新片最常见的特征就是丝印模糊、位置偏移或者字体与官方产品不一致。另外正品芯片的引脚光亮、整齐没有明显的焊接残留翻新片因为是拆机后重新处理过的引脚往往会有发暗或者重新镀锡的痕迹。更直接的办法是看芯片底部的批号编码Date Code。正品 STM32 芯片的底部会有一串激光刻印的编码不同批次的芯片编码不同。如果你发现手头一批芯片底部的批号编码完全一样那就要打一个大大的问号——正规渠道的原厂芯片几乎不可能出现同批次统一编码的情况。用酒精擦拭芯片表面也是一个简单有效的办法。正品芯片的丝印是激光打上去的用酒精擦不会掉色翻新片如果表面重新喷过漆酒精一擦容易露出下面的旧字或颜色变浅。当然这个方法只能作为辅助判断不能作为唯一依据。2.2 用调试器读芯片 ID最可靠的识别方式外观检查只能筛掉一部分比较粗糙的假片要真正确定芯片身份还是要通过调试接口去读芯片内部的 ID 寄存器。STM32F103 系列通过 SWD 接口连上 ST-Link 或 J-Link在 Keil 的 Flash Download 配置界面里可以看到 Device ID。具体操作步骤是这样的用 ST-Link 连接目标板的 SWDIO、SWCLK、GND如果有必要再连上 3.3V 给板子供电。打开 Keil进入“Options for Target” - “Debug”页面确认调试器选择的是 ST-Link Debugger。在“Flash Download”标签页里查看右下角的“ID CODE”信息。正品 STM32F103C8T6 的 Device ID 通常是 0x410中容量产品或 0x411大容量产品。如果你用 STM32CubeProgrammer 或者 ST-Link Utility可以直接连接后读取芯片的 UID。STM32 每颗芯片都有一个 96 位的唯一 ID放在 0x1FFFF7E8 地址开始的三个 32 位寄存器里。三次读取把结果记下来。如果你发现两片“不同批次”的芯片 UID 完全一样那基本可以断定是假货。这里插一句网上有一些人用“读出的芯片 Flash 容量”来判断真假这个方法有局限性。因为有些翻新片是拿高容量型号打磨后冒充低容量型号这类芯片读出的容量匹配不上型号能测出来但也有些仿冒片在内部 ID 和容量寄存器上做了手脚测出来跟正品一模一样。所以最保险的做法是把丝印、底部编码、Device ID、UID 这几项综合起来看。正品芯片这几项都经得起推敲假芯片总有一项会对不上。提示读 ID 是排查“假芯片”最有力的武器。只要调试器能连上优先做这一步。如果连调试器都连不上重点查一下 SWDIO 和 SWCLK 两个引脚是否被程序复用为普通 GPIO 了这个是另一个常见问题我在后文会专门讲。2.3 实际测试中的“芯片没反应”与仿冒品的关联我换掉“假芯片”之后用同一块板子、同一个工程、同一个 ST-Link重新烧录了一次代码立刻就跑通了。后来我仔细对比了一下两块芯片假芯片读出来的 UID 是 0xFFFFFFFF 或全 0正品芯片读出来是一连串有规律的十六进制数。假芯片在空板状态下上电后 3.3V 电源引脚的电流在 5mA 左右浮动正品芯片通常在 3mA 以下。假芯片在下载程序时Keil 的 Flash Download 提示“Erase Failed”需要把 Erase Full Chip 手动勾选上才能擦除正品芯片按默认设置就能正常擦写。这几点差异在刚开始排查的时候很容易被忽略因为“程序下载成功”这个反馈太容易迷惑人了。下载成功只能说明 Flash 擦写操作完成了不能代表芯片内部逻辑正常。如果你遇到反复擦写失败、下载偶发失败、或者下载后运行行为诡异的情况优先级最高的排查项就是芯片的可信度。3. 排查流程的正确顺序从电源到时钟再到 FreeRTOS 移植假设你手里的芯片是正品但板子依然没反应这时候就要老老实实走一遍硬件到软件的排查流程。我这里给出一个我自己惯用的顺序每一步都有明确的验证方法可以帮你快速缩小问题范围。3.1 第一步给板子“验电”确认电源和复位都健康STM32F103 的正常工作条件是VDD 和 VDDA 引脚有 2.0V 到 3.6V 的电压常规设计用 3.3V。NRST 引脚在正常工作时是高电平按下复位按键时短暂拉低。BOOT0 引脚的电平状态决定了启动方式拉低接 GND从主 Flash 启动拉高从系统存储器内置 Bootloader启动。实际操作中用万用表量这几个点的电压基本能排查掉 80% 的“没反应”问题。在这里我遇到过一个很典型的坑板子上用的 LDO低压差线性稳压器输出不是标准的 3.3V而是只有 2.8V芯片手册上写的电压范围是 2.0-3.6V按道理 2.8V 也能跑但因为负载较重电压被拉得更低芯片进入了欠压复位BOR状态程序自然跑不起来。还要强调一点NRST 引脚如果被外接一个大电容复位时间会被拉长导致上电后要等很久才能运行。我曾经见过一块板子NRST 引脚上焊了一个 10uF 的电容结果每次上电都要等两三秒才有反应第一次遇到时还以为是程序初始化太慢查了半天才发现是复位电路的 RC 时间常数太大。3.2 第二步确认调试接口和目标配置防止程序把调试引脚“关掉”很多时候芯片“没反应”是 SWD 调试引脚被程序复用导致的。STM32F103 的 PA13SWDIO和 PA14SWCLK默认是调试功能但如果你在程序里调用了类似下面的代码来重映射 GPIOGPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);或者使用了__HAL_RCC_AFIO_CLK_ENABLE()之后把 SWJ 全部禁用芯片的调试接口就会被关闭之后你就再也连不上调试器了即使程序本身写得没问题也感觉像“芯片挂了”。官方文档里提到SWJ 引脚有三种模式全功能JTAG SWD、只保留 SWD、完全禁用。前两种模式下 SWD 调试功能都可用但如果你不小心选成了完全禁用那就只能通过复位瞬间抢连调试器或者用串口 ISP 把 Flash 擦掉来恢复。所以排查顺序很重要先用串口 ISP 连接测试确定芯片内部 Bootloader 还能不能工作。STM32F103 内置的 Bootloader 出厂时就存在不会因为用户程序的写入而被覆盖。把 BOOT0 拉高复位后 STM32 会进入系统存储器模式此时通过 USART1PA9 和 PA10可以用串口工具发送指令读取芯片信息。如果能读到说明芯片本身活着问题在用户程序侧。3.3 第三步用一个最简单的裸机程序验证芯片基本功能在跑 FreeRTOS 之前先烧一个不需要任何外设的裸机程序比如让 PA1 引脚输出一个高电平用万用表量电压。如果这个程序都无法正常运行那问题大概率在芯片或硬件电路而不是操作系统移植。我一般会用这样的代码测试#include stm32f10x.h int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_1); while(1); }这里用 PA1 是因为很多 STM32F103 最小系统板上都引出了 PA1并且板载 LED 可能就接在附近测试起来方便。如果这段代码烧进去后 PA1 的电平没有变化再从电源和芯片层面仔细排查。注意先跑裸机程序再引入 FreeRTOS这是嵌入式开发里我一直坚持的原则。FreeRTOS 不是排查“芯片没反应”的起点它应该在你确认芯片“会动”之后再参与进来。3.4 第四步再谈 FreeRTOS 移植常见坑当你确认芯片和硬件没问题之后FreeRTOS 本身的移植问题才会浮出水面。以我的经验以下三个坑最值得先自查。第一个坑sysTick被 FreeRTOS 和用户代码同时占用。FreeRTOS 默认使用 SysTick 作为系统时基裸机代码里如果还用 SysTick 做延时两个功能互相踩踏程序会随机死机。解决办法是用户代码改用xTaskGetTickCount()来获取系统时间或者用vTaskDelay()做延时。第二个坑堆栈大小设置得太小。F103 的 RAM 才 20KBC8T6如果任务里定义了较大的局部数组任务栈很容易溢出。FreeRTOS 提供了栈溢出检测机制在FreeRTOSConfig.h里把configCHECK_FOR_STACK_OVERFLOW设置为 1 或 2然后实现vApplicationStackOverflowHook回调函数就可以在栈溢出时得到提示。我之前就遇到过任务里定义了一个 512 字节的数组把默认 128 字节的任务栈直接撑爆表现为任务跑一段时间后就不再执行了。第三个坑中断优先级配置和 FreeRTOS 的要求冲突。Cortex-M3 内核用 4 位来表示中断优先级但 STM32 只用了高 4 位中的一部分。FreeRTOS 要求所有中断优先级数值不能大于configMAX_SYSCALL_INTERRUPT_PRIORITY用标准库移植时一般是 5否则不能在中断服务函数里调用 FreeRTOS API。如果把某个外设中断的抢占优先级设成了 0最高优先级而configMAX_SYSCALL_INTERRUPT_PRIORITY是 5那这个中断里调用的xQueueSendFromISR就会触发断言失败程序直接卡死在断言里。4. 采购建议与实操心得避开“假芯片”的三个方法虽然本文的重点是“芯片没反应”的排查但既然问题源于假芯片我顺便分享一些个人的采购经验希望你能从源头上减少踩坑概率。尽量避开来源不明的散新片。所谓散新片就是没有完整包装、渠道不明的芯片外观可能是新的但来源不明有可能被翻新过。我常用的渠道一般是有正规授权的代理商或者官网可查的目录分销商。虽然价格高一些但省心省事。不要只看“能下载程序”就放心。很多翻新片也能正常下载程序只是某些内部模块有问题比如 Flash 的擦写次数接近耗尽或者内部 LDO 电压不稳。这类芯片用一阵子才会出问题最坑人。用起来之前先把 UID 和 Device ID 读一遍。不要嫌麻烦这个工作在正式开发前做一次能筛掉大部分假芯片。如果项目已经进入量产阶段建议在产测程序里加一段读取 UID 并和预置白名单比对的功能直接把假芯片挡在生产线上。5. 常见问题速查表50 秒定位“芯片没反应”为了方便你排查我把文中的关键问题、排查方法和解决思路整理成一个速查表现象优先嫌疑快速验证方法解决思路程序下载成功但复位后无任何反应芯片质量、电源、复位电路读取 Device ID 和 UID更换正品芯片检查电源与 NRST 引脚在线仿真一运行就死机芯片 Flash 异常、栈指针错误、FreeRTOS 任务栈过小单步执行看 PC 指针位置检查 map 文件调整启动文件增大任务栈开启栈溢出检测调试器连接不上SWJ 引脚被禁用、芯片进入低功耗模式BOOT0 拉高用串口 ISP 连接通过串口擦除用户程序检查用户代码是否禁用调试引脚烧录时偶发擦写失败Flash 寿命耗尽、芯片品质差多次擦写测试观察 Fail 概率更换芯片检查电源稳定性某一块板子正常另一块板子不正常个体芯片差异、焊接不良交换芯片测试观察引脚焊接重新焊接改用正品芯片排查时可以按这个顺序走先量电源再试串口 ISP最后用调试器读 ID。这三步做完基本能把问题定位到“芯片问题”还是“程序问题”。不要在一开始就陷入 FreeRTOS 的配置细节里出不来那会让你浪费很多时间。6. 跳过假芯片之后我给 FreeRTOS 移植顺序的建议既然你已经确认了芯片可靠FreeRTOS 移植这件事本身也需要有个清晰的推进顺序。我多次移植过 F103 FreeRTOS标准库和 HAL 库都试过大概的流程是这样准备一个能点灯、能串口输出的裸机工程确保芯片和工具链可靠这条算前面说过的地基。把 FreeRTOS 源码的tasks.c、list.c、queue.c、croutine.c、event_groups.c、stream_buffer.c、timers.c全部加进工程对应portable目录下选RVDS/ARM_CM3的移植层文件heap部分选heap_4.c。配置FreeRTOSConfig.h重点检查configCPU_CLOCK_HZF103 一般配 72MHz、configTICK_RATE_HZ常用 1000、configMINIMAL_STACK_SIZE建议至少 128、configTOTAL_HEAP_SIZE根据 RAM 调整C8T6 建议 8KB 左右起步。写两个最简单任务一个负责点灯一个负责串口打印先验证系统跑起来了。再逐步加队列、信号量、软件定时器这些进阶组件。从第 4 步再往后如果任务调度不稳定常见的原因就是内存不足、栈溢出、以及中断里调用了不安全的 API。我在第 3 节已经列了三个典型场景可以对着查。我个人在实际操作中的体会是提高问题的可排查性比提高代码的“先进性”更重要。移植系统时多花一点时间把栈溢出检测打开调试时勤看map文件、留意任务栈的使用量比出了问题之后盲猜要高效得多。另外把“更换芯片”这个动作也纳入你的常规排查手段里不要因为它看起来“太基础”就不去试。嵌入式开发里最诡异的问题往往就藏在最普通的角落。
