前几年做 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. 按顺序自我排查:先硬件后软件,先裸机后 RTOS
2.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 系列有几个关键地址可以读取:
0x1FFFF7E0:Flash 大小寄存器,单位是 KB。比如 F103C8T6 烧写这段地址会读到 0x40,也就是 64KB;F103CBT6 读到 0x80,128KB;0x1FFFF7E8起三个 32 位字:芯片唯一 ID(UID),一共 96 位;0xE0042000:DBGMCU_IDCODE 寄存器,低 16 位是 DEV_ID,STM32F103 系列一般读出 0x410;0xE000ED00:内核 CPUID 寄存器,用来识别内核版本,Cortex-M3 一般读出 0x411FC231。
我使用 ST-Link Utility 读出的结果是:Flash 大小 64KB,UID 三个字全部是 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,如果实际芯片只有 10KB,FreeRTOS 的堆内存分配和任务栈就会发生跨边界写入,栈顶指针可能指到不存在的地址,运行一段时间后程序就会跑飞。
第三层是外设映射差异。很多国产兼容片的 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 是 128KB;
- UID 不应该是
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_Library,APM32 也有自己的库。把这些库里的启动文件(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 跑 FreeRTOS+LVGL,碰到显示屏白屏,也别急着去调 LVGL 的配置,先量一下主频、读一下 JEDEC ID、确认 Flash 容量是否匹配。底层不确定,上层永远调不通。
最后再分享一个细节。我那次把自检固件烧进所谓“假芯片”后,串口打印 FlashSize 是 64,UID 是FFFFFFFF-FFFFFFFF-FFFFFFFF,DBGMCU_ID 低 16 位是 0x410。单独看每一项,似乎都和 F103C8T6 对得上,但组合在一起看就非常可疑。后来我把芯片表面刮开一小块,发现底层还有一行被磨掉的旧丝印,隐约能看到“STM32F103C6T6”的字样。那一刻终于明白了:单片机的“性格”再沉稳,也架不住身份造假。
所以下次如果你的 STM32F103 + FreeRTOS 板子“没反应”,在怀疑任务优先级之前,先用自检脚本问问芯片:“你到底是谁?” 这个问题,尽早问,永远不吃亏。