STM32F103假芯片导致FreeRTOS无反应:从裸机点灯到芯片身份识别的排查指南
前几年做 STM32F103C8T6 FreeRTOS 的项目遇到过一次让我差点怀疑人生的“灵异事件”。板子焊好三片第一片上电正常第二片和第三片上电后纹丝不动串口没有任何输出LED 也不亮电流倒是正常。我第一反应是 FreeRTOS 没移植好或者启动文件被 Keil 吃掉了花了两天时间检查任务优先级、堆栈大小、SysTick 配置甚至把工程里所有 .s 启动文件轮着换了一遍毫无进展。最后还是抱着试试看的心态用 ST-Link Utility 读了一下芯片信息才明白问题根本不在软件——这几片芯片的 ID 和正品对不上是典型的“身份造假”。这篇文章把那次排查过程完整还原顺便把如何识别 STM32F103“假芯片”、假芯片为什么会让 FreeRTOS 整个挂掉、以及买到兼容片之后怎么办这些事一次性讲透。如果你正在做 F103 FreeRTOS 的板子或者刚收到一批来历不明的芯片这篇值得看完。1. 上电后“装死”的板子一次刻骨铭心的定位经历1.1 当时的现象与软件排查误区项目是一款小型采集设备主控是 STM32F103C8T6跑 FreeRTOS内部建了三个任务一个负责 AD 采样一个负责通过串口输出数据一个负责 LED 闪烁。程序在第一批板子上跑得好好的第二批板子换上后就出现“部分板子没反应”的情况。没反应的板子具体表现是这样USB 转串口插上电脑收到不到任何数据板载 3.3V 电源指示灯亮说明供电基本正常量了一下电流大概 6~8mA不像短路更像芯片处于复位或停滞状态ST-Link 可以正常连接也能读到芯片型号但烧录后全速运行就是进不了 main 函数用 Keil 的 Debug 模式单步结果 PC 指针停在一个奇怪的地址看不出任何逻辑。我当时第一反应就是 FreeRTOS 配置出问题了于是做了一堆“标准动作”把任务栈调大、把 configMINIMAL_STACK_SIZE 调大、打开堆栈溢出检测钩子、关闭时间片轮转调度甚至把 main 函数简化成只创建任务不启动调度器。结果都一样——芯片依旧不鸟你。后来想想这其实是大多数人调 FreeRTOS 时最容易踩的思维陷阱总以为系统没跑起来是 RTOS 的問題但实际上 FreeRTOS 只是一个线程调度器它对底层的要求无非是一个能用的 SysTick、正确的中断向量表和可读写的 RAM。如果底层芯片本身就有问题RTOS 层面是不可能跑起来的查一千遍配置文件都没用。1.2 为什么 FreeRTOS 会让问题变得更隐蔽有人可能会问FreeRTOS 的出现是不是让假芯片问题更容易被观察到确实如此。裸机程序里如果只是循环点灯即使芯片时钟偏差很大LED 也可能照样闪只是频率不准确你未必能一眼看出来。但 FreeRTOS 对时间基准很敏感——任务调度靠滴答定时器SysTick任务延时靠 tick 计数信号量和队列的超时等待也依赖 tick。一旦芯片的内部 RC 振荡器频率偏差超标或者 SysTick 提供的中断时序不正常就会出现“任务不切换”“vTaskDelay 时间严重不准”“看门狗误复位”这些现象。更麻烦的地方在于这些现象的表现非常像代码缺陷。比如一个任务卡死了你会怀疑是不是进入临界区没退出两个任务无法同步你会怀疑信号量初始化错误系统每隔几秒自动复位你会怀疑是看门狗喂狗位置放错了。这导致我后来的排查思路彻底反转与其在软件里猜不如先把芯片本身的身分辨清楚。2. 按顺序自我排查先硬件后软件先裸机后 RTOS2.1 最小系统五项检查在怀疑假芯片之前我一直认为最小系统没有问题。为什么因为第一片板子能正常工作且三块板子来自同一个批次我下意识认为硬件绝对不会出问题。这是第二个容易犯的错误——同一批次不代表每片都可靠尤其当货源不正规时。如果你也遇到“芯片没反应”先别急着开软件调试工具老老实实按下面这个顺序量一遍电源用万用表测量每个 VDD 引脚的对地电压应为 3.3V ±0.1V同时确认每个电源引脚附近都有 0.1uF 高频去耦电容而不是只放一颗大电解电容地确认 LQFP48 封装的焊盘已经可靠接地很多手工焊接的板子底部焊盘没焊好看似贴上了实际地平面悬空复位NRST 引脚在正常工作时应为高电平按下复位按键后能拉到低松开后能恢复到高。如果 NRST 一直为低芯片会一直停在复位状态BOOT0必须为低电平一般接一个 10k 下拉电阻到地不能悬空。BOOT0 悬空在高阻状态下容易受干扰导致芯片进入系统存储器引导模式程序自然跑不起来晶振F103 标准库默认使用外部 8MHz HSE如果外部晶振没焊好系统时钟初始化会失败程序虽然不会死但内部时钟可能回退到 HSI 或者干脆停在等待 HSE 就绪的死循环里。这几项检查是基本功但非常多人会跳过。我那次就是因为第一片板子没问题所以没有重新做这一步结果导致后面两天的排查全被带偏。2.2 用 PA8 引时钟比示波器探针到处戳更高效F103 早期一些批次或国产兼容片外部晶振引脚特性差异比较大。为了快速确认时钟链路是否正常有个非常实用的技巧把 PA8 引脚配置为 MCO 输出输出系统时钟 SYSCLK 或外部 HSE 时钟然后用示波器测 PA8 的频率。用标准库只需要几行代码void MCO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); RCC_MCOConfig(RCC_MCO_SYSCLK); }如果此时 PA8 能测到稳定的 72MHz 波形说明从 HSE 到 PLL 到 SYSCLK 这条链路是通的。如果测到的是 8MHz说明 PLL 没有锁定通常是外部晶振没起振如果完全没有波形那就非常可疑要么芯片 RCC 逻辑损坏要么芯片供电有问题。这个方法比拿示波器敲晶振引脚两端的 “疑似小信号” 靠谱得多。晶振脚信号通常振幅很小探棒一上去反而导致停振容易误判。2.3 裸机点灯证明 CPU 本身能执行代码接下来最重要的一步剥离 FreeRTOS烧一个完全裸机的 GPIO 翻转程序。如果这个程序能让 LED 正常闪烁说明芯片内核、Flash、GPIO、时钟这些最基本的东西都是好的FreeRTOS 跑不起来大概率是软件层面的问题如果连这个都不行那你基本可以确定问题出在硅片本身。当时我写了一个最简版的点灯程序直接操作寄存器#define RCC_APB2ENR (*(volatile unsigned long *)0x40021018) #define GPIOC_CRH (*(volatile unsigned long *)0x40011004) #define GPIOC_ODR (*(volatile unsigned long *)0x4001100C) void SimpleDelay(void) { volatile unsigned int i; for (i 0; i 1000000; i) { ; } } int main(void) { RCC_APB2ENR | (1 4); /* 使能 GPIOC 时钟 */ GPIOC_CRH 0xFF0FFFFF; /* 清空 PC13 相关位 */ GPIOC_CRH | 0x00300000; /* PC13 配置为推挽输出50MHz */ while (1) { GPIOC_ODR ^ (1 13); /* 翻转 PC13 */ SimpleDelay(); } }当时的心情是如果能闪灯我就回去继续调 FreeRTOS如果不能闪灯那问题肯定不在 FreeRTOS。答案是没有闪灯。这一刻我反而踏实了因为问题成功地从“FreeRTOS 玄学”转移到了“芯片硬件”这个可验证的方向。接下来要做的就是把假芯片的真面目标出来。3. 假芯片的“身份指纹”到底假在哪里3.1 三种最常见的假货类型市面上常见的 STM32F103“假芯片”可以分成三类每一类对项目的影响完全不同类型实际本质主要风险打磨重刻片用小容量 ST 芯片打磨后重新印字冒充大容量型号Flash/RAM 容量不足程序烧到越界地址运行时随机崩溃国产兼容片GD32、APM32、MM32 等非 ST 厂商芯片打着 ST 型号卖或被打磨为 ST 丝印外设寄存器兼容度不够时钟树、Flash 控制器、USB 等外设行为有差异废品弃片原厂检测不合格的芯片被非法渠道流出重新印字各种随机硬件故障有的引脚短路有的 Flash 坏块有的内核访问偶发错误我买到的这批更像是第一类或第二类的混合体外观上看丝印有打磨痕迹内部 ID 读出来又不像标准 STM32F103C8T6。3.2 读内部寄存器Flash 容量、UID 与设备 ID判断芯片真假不能只看外观必须读芯片内部的“户口信息”。STM32F1 系列有几个关键地址可以读取0x1FFFF7E0Flash 大小寄存器单位是 KB。比如 F103C8T6 烧写这段地址会读到 0x40也就是 64KBF103CBT6 读到 0x80128KB0x1FFFF7E8起三个 32 位字芯片唯一 IDUID一共 96 位0xE0042000DBGMCU_IDCODE 寄存器低 16 位是 DEV_IDSTM32F103 系列一般读出 0x4100xE000ED00内核 CPUID 寄存器用来识别内核版本Cortex-M3 一般读出 0x411FC231。我使用 ST-Link Utility 读出的结果是Flash 大小 64KBUID 三个字全部是 0xFFFFFFFF。芯片出厂时都会写入真实 UID除非它是一块被擦写异常或打磨过的芯片否则全 FF 非常可疑。再配合丝印做工基本可以断定这不是正品新片。如果你手头有正品芯片可以把两边的 UID 打出来对比。正品 STM32F103 的 UID 一般由晶圆批次、晶圆 X/Y 坐标等信息编码而成每组都不一样但不会是毫无规律的重复值。如果你买了一批芯片UID 居然全部一样那几乎可以确定是假货或者翻新片。3.3 为什么“假芯片”会让 FreeRTOS 悄然崩掉拿到疑似假芯片后肯定有人会问为什么正品跑得好好的固件在这片芯片上会导致 FreeRTOS 完全不工作这里面其实有三层原因第一层是 Flash 容量不够。如果买的是 C8T6固件按 64KB 编译但实际芯片是 32KB 的 C6T6 打磨的烧录器在写入时会发现目标 Flash 空间不足下载失败或校验不通过。就算用某些工具强制写入成功程序运行到 0x08008000 附近时取指出来的是空数据或乱码CPU 很快会进入 HardFault任务调度自然就停了。第二层是 RAM 容量不匹配。C8T6 是 20KB SRAM如果实际芯片只有 10KBFreeRTOS 的堆内存分配和任务栈就会发生跨边界写入栈顶指针可能指到不存在的地址运行一段时间后程序就会跑飞。第三层是外设映射差异。很多国产兼容片的 RCC 时钟树、Flash 控制器、DMA 地址虽然大体兼容但细节实现不同。比如 GD32F103 在同样的 8MHz 外部晶振配置下PLL 倍频寄存器初始化方式相同但 Flash 等待周期需要不同设置。如果沿用 ST 标准库的 SystemInit有可能系统时钟实际没有达到 72MHz而是停留在某个倍频失败后的 HSI 频率这会直接导致 FreeRTOS 的时间基准不准串口波特率也乱掉。我当时观察到的现象是芯片没有任何输出这其实是所有底层问题叠加之后的结果晶振初始化失败、时钟配置停滞在死循环或者系统时钟没有正确建立程序根本没跳到 main 函数自然没有打印。3.4 一个反直觉的结论J-Link 识别到的 ID 并不能完全判定真伪很多论坛帖子说用 J-Link 读到的 IDCODE 是 0x2BA01477 就是假 STM32是 GD32。实际上这是个不太严谨的说法。J-Link 在连接过程中读到的 SW-DP IDCODE是调试访问端口DP的标识而不是芯片厂商的“身份证”。对于同一颗处理器核心这个 ID 可能因为调试组件版本不同而变化用它来甄别是不是 ST 原厂并不够可靠。尤其现在很多 GD32 的内核调试组件和 STM32F103 高度相似读出来的 SW-DP IDCODE 甚至一模一样的案例都存在。所以我的建议是别只靠一个 ID 值下定论。应该把丝印外观、Flash 大小、UID 内容、DBGMCU ID、实际运行行为综合在一起判断。尤其是 UID正品芯片的 UID 一定是有规律的不会全 FF也不会所有芯片完全重复。4. 伪装成“软件 Bug”的假芯片故障几个具体坑4.1 任务切换像“跷跷板”偶尔停摆朋友的一个项目也遇到过类似情况。他把程序从正品 F103C8T6 烧到一批“便宜料”里FreeRTOS 任务开始能跑LED 闪得也挺好但运行几分钟后任务就像是有人按了暂停键所有线程停在一个位置不再切换。用调试器暂停一看当前停在一个空闲任务里SysTick 中断已经不再生成。换回正品芯片同一份固件跑几天都没事。最后读取 Flash 大小发现实际芯片容量比标称小了一半某个任务栈分配到了 RAM 的越界地址数据不断被踩最终把 NVIC 的 SysTick 相关寄存器也给踩坏了。这就像一栋楼房的消防通道被杂物堵死火灾时整栋楼的人都没法逃生但平时你根本看不见那个杂物堆在哪。4.2 FreeRTOS 的栈溢出钩子被触发却不代表你栈不够FreeRTOS 提供了堆栈溢出检测机制在FreeRTOSConfig.h中把configCHECK_FOR_STACK_OVERFLOW设置为 1 或 2再在钩子函数里打断点就能检测任务栈溢出。本来这是排查任务栈大小是否够用的好工具但放在假芯片上就很容易误导人。原因是某些打磨片的 SRAM 某几个地址单元存在坏块任务栈指针一旦踩到这个区域数据读出来就是错的甚至可能触发总线错误。此时 FreeRTOS 会误认为“任务栈溢出”但实际上你的栈空间真正剩余的还有一大半。我那次就在vApplicationStackOverflowHook里加了个串口打印结果打印出来一串乱码之后立刻复位。如果只看现象会以为任务栈写得不够大但当你把任务栈从 256 字节加到 1024 字节问题依旧那就应该怀疑 RAM 是否真实存在了。4.3 Keil 提示 “not a genuine ST Device” 的来龙去脉Keil 配合 ST-Link 下载程序时如果弹出的提示是“not a genuine ST Device!”这说明 ST-Link 的固件检测到了某些非 ST 芯片的特征然后拒绝继续擦除和编程。这个提示在正品 STM32 上极少出现一旦出现基本说明芯片的身份和 ST 官方数据库对不上。但这并不绝对也有部分正品因读保护或调试口状态异常出现类似提示。碰到这种情况可以按下面顺序尝试换一个 ST-Link排除调试器固件误报用 STM32CubeProgrammer 读取芯片的 Device ID 和 Flash size确认是否正常如果确实读出来非 ST 家族的 ID那基本可以坐实兼容片或打磨片。当时我没有在 Keil 里直接烧录而是先用 ST-Link Utility 单独连接结果它提示无法读取某段存储器。再后来用 J-Link Commander 连上发现目标芯片的复位向量表区域是一堆异常数据和正常 F103 出厂状态不符。4.4 掉电保存数据的“灵异丢失”也与芯片有关热搜词里有一条“stm32f103掉电保存数据”让我想起不少人在假芯片上遇到的数据丢失问题。很多人实现掉电数据保存的方式是用 PWM 或外部中断检测掉电事件在电压跌落前把关键数据写进内置 Flash。这个思路依赖两个条件一是芯片能在掉电瞬间获得足够的时间完成 Flash 写入二是内部电压检测PVD阈值准确。正品 F103 的 PVD 阈值比较稳定但某些兼容片内部的参考电压和 PVD 比较器存在偏差导致掉电中断触发时刻比预期晚Flash 写入到一半电压就跌没了数据自然就损坏了。在 FreeRTOS 场景下这个坑更明显因为中断响应、事件标志置位和任务调度都有额外延迟本来就不充裕的掉电窗口会被进一步压缩。如果你的项目要做掉电保存现在就必须记住用正品芯片验证过的掉电时序换到假芯片或兼容片上必须重新测试否则量产后数据大概率丢到你怀疑人生。5. 验明正身五分钟自检脚本与工具交叉确认5.1 自己写一个来料检测程序串口打印“户籍信息”与其每次都用调试工具不如直接在板上烧一段自检程序让芯片自己把关键身份信息通过串口发出来。这个程序以后可以作为来料检测的标准固件每次芯片入库前烧一遍五分钟内就能完成筛选。完整示例代码使用 STM32 标准库 v3.5#include stm32f10x.h #include stdio.h void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, 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_Tx | USART_Mode_Rx; USART_Init(USART1, USART_InitStructure); USART_Cmd(USART1, ENABLE); } int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET) { ; } USART_SendData(USART1, (uint8_t)ch); return ch; } int main(void) { uint16_t flash_size; uint32_t uid[3]; uint32_t cpu_id; uint32_t dbgmcu_id; USART1_Init(); flash_size *(volatile uint16_t *)0x1FFFF7E0; uid[0] *(volatile uint32_t *)0x1FFFF7E8; uid[1] *(volatile uint32_t *)0x1FFFF7EC; uid[2] *(volatile uint32_t *)0x1FFFF7F0; cpu_id *(volatile uint32_t *)0xE000ED00; dbgmcu_id *(volatile uint32_t *)0xE0042000; printf(\r\n Chip Self-Check \r\n); printf(FlashSize: %u KB\r\n, flash_size); printf(UID: %08X-%08X-%08X\r\n, uid[0], uid[1], uid[2]); printf(CPUID: %08X\r\n, cpu_id); printf(DBGMCU_ID: %08X\r\n, dbgmcu_id); printf(\r\n); while (1) { ; } }这段代码编译下载后打开串口助手等待输出。判断要点如下FlashSize 应该和丝印型号一致。丝印写 C8 就应该是 64KB丝印写 CB 是 128KBUID 不应该是FFFFFFFF也最好不要出现连续好几片完全相同的情况CPUID 的数值稳定为 Cortex-M3 内核特征值是比较正常的DBGMCU_ID 寄存器低 16 位正品 F103 常见的是 0x410但个别兼容片也会伪装成 0x410所以不能单看这一项。这里要强调的是这一段不是“万能判据”而是用来快速筛选出那些明显不对的芯片尤其是打磨成更大容量的低端芯片、废片、UID 异常芯片。5.2 使用 STM32CubeProgrammer 和 ST-Link Utility 交叉验证如果芯片已经焊死在板子上不方便重新写程序可以用 STM32CubeProgrammer 读取芯片信息。连接成功后界面会显示当前读取到的 Device ID、Flash Size、RAM Size、UID 等信息。你需要重点观察两点一是 Device ID 是否属于 STM32F1 系列二是在 Memory and File 页面读取 0x1FFFF7E0 地址的值确认和丝印标称容量是否一致。如果这两个地方出现“Unknown”或者“0xFFFFFFFF”那就要高度警惕。ST-Link Utility 老版本也同样可以看但 ST 已经停止更新推荐直接用 STM32CubeProgrammer。J-Link Commander 也支持通过命令行查看目标芯片的调试 ID但正如前面所说这个 ID 的参考价值有限不能单独作为判定依据。5.3 更长时间的压力测试FreeRTOS 跑 72 小时静态登记特征没问题不代表芯片能稳定工作。很多废品芯片的个体缺陷是“间歇式”的可能刚开始运行都正常温度上来之后内部存储器写入错误开始增多最终导致看门狗复位或任务卡死。建议把所有新批次芯片统一烧录一份 FreeRTOS 压力测试固件至少包含以下内容一个周期 100ms 的 LED 翻转任务用于肉眼判断调度器是否活着一个通过串口每 200ms 输出一行递增计数器的任务记录最长运行时间一个看门狗任务每 500ms 喂一次外部或内部 IWDG一个 DMA 搬运任务不停把内存中的数据搬到外设或引脚模拟真实总线压力一个周期性写入内部 Flash 的测试任务写入次数不宜太多且备份关键数据用来暴露 Flash 内部坏块问题。跑 72 小时后统计每块板子的复位次数、HardFault 次数、串口输出是否中断。超过一定阈值就直接判不合格。这一套压力测试非常有效。我后来把未通过自检的芯片集中做实验有的在 3 小时左右复位有的在 48 小时左右开始乱码基本都过不了 72 小时关。而正品芯片通常能连续跑一周以上不出问题。6. 与假芯片“共处”的策略降级转正但别硬撑6.1 如果确认是国产兼容片就“转正”使用不是所有假芯片都必须扔掉。如果你确认自己买到的其实是 GD32F103 或 APM32F103那么它们本身也是一个可用的 MCU很多功能都能兼容。真正的问题是你不能继续用 ST 的库和配置去裸奔。正确做法是去对应厂商官网下载官方固件库。比如 GD32 有GD32F1x0_Firmware_LibraryAPM32 也有自己的库。把这些库里的启动文件startup 汇编、系统时钟初始化system_gd32f10x.c等文件替换掉原本 ST 标准库里的对应文件然后再编译运行。需要注意几个差异点Flash 等待周期设置不同RCC 时钟树中部分外设时钟使能位可能一致但个别寄存器的复位值不同USB 外设寄存器差异很大如果你用到了 USB 虚拟串口ST 的 USB 库基本不能用必须换成厂商自己的 USB 栈低功耗模式的行为差异也很大特别是 STOP 和 STANDBY 模式下的唤醒时间。强行用 ST 标准库去跑 GD32确实很多项目能跑起来因为它们的 Cortex-M3 内核和相当部分外设地址是兼容的但“能跑”和“可靠跑”是两回事。FreeRTOS 这种对时间精度要求高的系统一个不准确的时钟源就会让整个调度乱掉更别提后续做认证和量产了。6.2 采购端的实战建议渠道、抽检与合格线最好的解决方法是永远不要踩进去。下面是我现在执行的一套来料质量控制流程分享给你只从正规渠道采购优先选择原厂授权代理商或信誉好、支持原厂检测的电商卖家。价格明显低于市场正常价位的基本可以直接排除。不要因为省几毛钱就买入不确定批次的散新货到货后做外观抽检拆开真空包装后先看丝印是否清晰、批次是否一致、芯片表面是否有重新打磨的哑光痕迹。正品 STM32F103 的 Marking 具有很强的镭射光泽用手指甲刮上去能感觉到凹凸感打磨片的印字常发灰、发虚上机前烧自检程序批次抽样 10% 或至少 5 片烧入上面给出的串口自检代码核对 Flash Size、UID 和打印信息小批量试产做压力测试用 FreeRTOS 压力测试固件跑 72 小时记录复位记录。正品批次通常全部通过假货批次经常出现零星复位保留样品和记录每批芯片留几片封存一旦产品在客户端出现故障可以回溯对比。有的朋友可能会觉得这样做成本太高尤其只是做一个小项目。我的观点是如果你的项目将来要量产哪怕只做几百套这种检测成本也远低于产品在客户现场出问题之后的差旅、返工和口碑损失。6.3 每次看到“不亮”的板子先问芯片“你是谁”经过这次教训我养成一个习惯拿到新板子或者新批次芯片不再默认它们“一定是好的”。以前写裸机程序碰到芯片没反应我会先怀疑手残把某个寄存器配错了然后反复看手册。现在不会了我会先烧一个最简的自检程序让芯片告诉你它是什么。这套思路不仅适用于 STM32F103 FreeRTOS放到任何 MCU 项目上都适用。比如你用 STM32H743 跑 FreeRTOSLVGL碰到显示屏白屏也别急着去调 LVGL 的配置先量一下主频、读一下 JEDEC ID、确认 Flash 容量是否匹配。底层不确定上层永远调不通。最后再分享一个细节。我那次把自检固件烧进所谓“假芯片”后串口打印 FlashSize 是 64UID 是FFFFFFFF-FFFFFFFF-FFFFFFFFDBGMCU_ID 低 16 位是 0x410。单独看每一项似乎都和 F103C8T6 对得上但组合在一起看就非常可疑。后来我把芯片表面刮开一小块发现底层还有一行被磨掉的旧丝印隐约能看到“STM32F103C6T6”的字样。那一刻终于明白了单片机的“性格”再沉稳也架不住身份造假。所以下次如果你的 STM32F103 FreeRTOS 板子“没反应”在怀疑任务优先级之前先用自检脚本问问芯片“你到底是谁” 这个问题尽早问永远不吃亏。
