尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32F103+FreeRTOS芯片没反应?从假芯片鉴别到硬件排查全指南

STM32F103+FreeRTOS芯片没反应?从假芯片鉴别到硬件排查全指南 上个月有个朋友给我发过来一块板子的照片说STM32F103 FreeRTOS做的最小系统上电之后芯片没反应下载器也连不上怀疑自己焊接把芯片烧了。我让他先别拆把芯片丝印放大拍一张。照片一放大我就觉得不太对劲丝印字体发虚位置歪歪扭扭芯片底部的原厂定位孔几乎看不出来。我问他这芯片哪来的他说是网上十来块钱一把买的“散新片”。听到这我心里基本有数了——这颗“STM32F103”大概率不是正经原装货。这几年芯片供应紧张之后各种渠道冒出来的STM32F103翻新片、打磨片、兼容片多到超乎你的想象。很多人把“芯片没反应”归咎于代码、归咎于FreeRTOS移植、归咎于自己的焊接水平唯独没有想到问题最可能出在那颗几块钱的芯片本身。这篇文章我想把这几年在F103 FreeRTOS项目里踩过的坑、用过的排查手段、以及鉴别假芯片的实操方法完整写出来。无论你是刚开始玩最小系统的新手还是被偶发HardFault折磨的嵌入式老兵这篇都能帮你省下至少一整天调试时间。1. 给“没反应”定性先把故障分成三档再动手排雷很多人一上来就怀疑芯片这其实不对。我这几年调试的经验是所有“没反应”都必须先分档不同档位的排查路径完全不同。分错档后面全是白忙活。1.1 三种常见的“没反应”现象我把遇到过的“芯片没反应”归纳成三档第一档上电后完全没有任何电流反应电源指示灯不亮板子总电流接近0mA。这种情况基本是电源路径断了比如5V没进来、3.3V稳压芯片虚焊、电源对地短路跟芯片本身关系不大。第二档上电后电流有几十mA但程序不跑IO口电平不变下载器也连不上或只能“烧录后不运行”。这一档嫌疑最大的是硬件复位、外部晶振、BOOT引脚再往后才是芯片本身有毛病。第三档调试器能正常连接程序也能烧进去但全速运行就死机、HardFault或不断复位。这一档容易让人以为是FreeRTOS配置问题实际上翻新片、Remark片也经常表现为随机死机。判断到底是哪一档不需要复杂仪器。一块万用表测电流一块示波器看复位和时钟波形基本就够了。1.2 从上电到运行的固定检查顺序我在项目里总结了一套固定顺序照着走效率最高顺序检查对象目标状态异常时可能的结论1电源输入万用表确认3.3V稳定纹波小于50mV电源芯片/电容问题2NRST复位脚上电后从0V拉高到3.3V无周期性跌落复位电容过大或复位芯片异常3BOOT0/BOOT1正常运行时BOOT0为低电平BOOT0悬空/拉高启动到了Bootloader4外部晶振用示波器测OSC_IN/OSC_OUT能测到几MHz振荡晶振没起振或电容配置不对5SWD连接ST-Link能识别到内核读取芯片ID芯片电源没进/内部损坏/翻新片异常6程序运行下载最小点灯程序IO翻转进入HardFault或启动文件配置错误这里有个关键点如果第5步用ST-Link或STM32CubeProgrammer都连不上先别急着怀疑芯片坏了先试“Connect under reset”连接时按住复位。有些程序把SWD引脚复用掉了或者芯片本身跑飞占用了调试口按住复位连接就能避开。这个功能在STM32CubeProgrammer里叫“Mode: Under reset”在Keil里对应“Connect under reset”新手往往不知道还有这招。1.3 为什么FreeRTOS工程最容易把“硬件病”放大成“软件病”我经常跟人说裸机点灯都没问题不代表芯片没问题而FreeRTOS工程出问题也不代表一定是软件问题。FreeRTOS跑起来之后内核要依赖SysTick定时中断、PendSV异常、SVC异常来做任务切换还要在启动调度器那一刻把当前上下文切到第一个任务。这个过程对Flash时序、内部时钟稳定性、RAM读写可靠性都很敏感。翻新片最大的问题恰恰就在这里芯片内部Flash可能是老化过的写进去的内容能跑但长时间运行或温度变化后读出出错内部RC振荡器漂移导致系统时钟不稳定甚至RAM单元有少量坏块裸机程序只用了前几百字节看不出来一跑FreeRTOS任务栈一分配就随机死机。所以我的原则是一切“没反应”先用裸机最小点灯程序验证再上FreeRTOS。如果裸机点灯都点不亮代码部分先放一边检查硬件和芯片如果裸机没问题FreeRTOS起不来才去怀疑代码。这个先后顺序能帮你少走一半弯路。2. 翻新片、Remark片和“换标兼容片”用外观和寄存器拆穿伪装判断“假芯片”不能靠玄学要有依据。我把市面上冒充STM32F103的芯片大致分成三类每一类的识别方法不一样。2.1 三种“假芯片”的真实身份翻新拆机片从旧电路板拆下来清洗、重新打脚、打磨表面再印上新的丝印。这种芯片引脚往往有轻微焊接痕迹或氧化芯片本体可能有打磨后的哑光区关键是内部Flash已经有一定损耗高温老化和写入可靠性都差。Remark片打磨重标高把低容量、低规格的芯片打磨后印成高容量、高规格的丝印。最常见的操作是把STM32F103C6T632KB Flash印成C8T664KB Flash或者把RAM更小的型号印成大RAM型号。外观上很难辨别只有读内部寄存器才能发现。换标兼容片把兼容型号芯片磨标后印上“ST”logo和STM32丝印。比如某些GD32F103兼容芯片本身是好芯片但被不良渠道磨掉原厂标冒充ST卖高价。这种芯片这才叫“假ST”但不代表GD32本身差——它只是被冒充了。这三种情况单看外观越来越难识别但只要接上调试器基本都能现原形。2.2 不用拆机就能做的外观检查外观检查虽然不能100%判定但能做初筛。重点看几处丝印字体原装ST芯片的丝印字迹清晰、边缘锐利字体大小和位置有固定规律。翻新片和Remark片的丝印往往发虚、字体粗细不均匀甚至同一个批次的芯片丝印位置都不一样。引脚状态原装新片引脚光亮、镀锡层均匀。翻新片引脚可能发暗、有细微划痕或者引脚根部残留烙铁高温过的痕迹。用放大镜或手机微距镜头看最清楚。芯片背面和定位孔原装芯片底部有一个圆形定位凹点位置固定在某个角附近翻新打磨片这个点可能变浅、模糊甚至消失。芯片侧面如果有明显的二次塑封或边缘毛刺也要警惕。溶剂擦拭用酒精棉轻轻擦拭丝印如果是二次油墨印刷很容易变淡甚至被擦掉原装激光刻字则非常牢固。这个只能做参考不要作为唯一判断依据。当然外观检查最大的问题是没法量化。真正能一锤定音的是读芯片内部的寄存器信息。2.3 用调试器读取“芯片身份证”IDCODE、Flash容量和96位UIDSTM32F103内部有两个非常关键的“身份证”信息DBGMCU_IDCODE寄存器和Flash容量寄存器。不需要额外硬件在Keil里用调试表达式或者直接用STM32CubeProgrammer读。如果用STM32CubeProgrammer连接成功后选择“Option Bytes”或“Memory and File”界面就能直接看到Device ID和Flash Size。STM32F103C8T6正品的信息一般是项目正品STM32F103C8T6期望值Device ID0x410中容量Flash Size64 KB96位UID唯一且非全0xFF如果你读出来Flash Size只有32KB那这颗基本就是C6T6磨标成C8T6如果Device ID是0x414那是大容量芯片磨标冒充小容量如果ID读出来完全不认识那就要高度怀疑是换标兼容片。想在自己的工程里直接打印可以用一段很短的代码#define DBGMCU_IDCODE (*(volatile uint32_t *)0xE0042000) #define FLASH_SIZE (*(volatile uint16_t *)0x1FFFF7E0) #define UID_BASE 0x1FFFF7E8 void CheckChipInfo(void) { uint32_t uid0, uid1, uid2; uid0 *(volatile uint32_t *)(UID_BASE); uid1 *(volatile uint32_t *)(UID_BASE 4); uid2 *(volatile uint32_t *)(UID_BASE 8); printf(DBGMCU_IDCODE 0x%08X\r\n, DBGMCU_IDCODE); printf(FLASH_SIZE %d KB\r\n, FLASH_SIZE); printf(UID %08X %08X %08X\r\n, uid0, uid1, uid2); }这段代码放在main函数最开头串口初始化完成后调用打印结果留档。如果发现IDCODE或Flash容量和丝印不符这颗芯片就可以直接判死刑了。2.4 一个简单的Flash压力测试让翻新片现原形读寄存器能查出Remark和换标但翻新片这种“半真半假”的家伙寄存器读出来可能都是对的就是运行不稳定。这时候需要做Flash压力测试对芯片Flash的空白区域反复擦写、回读、比对循环上百次看看有没有写入错误或读出错误。这个测试我一般放在定制测试板上做不会在产品固件里跑。示例代码可以这样写#define TEST_ADDR 0x0801F800 // 最后2KB区域确保代码区不在这个范围内 void Flash_StressTest(void) { uint32_t i, err 0; uint32_t write_val 0xA5A5A5A5; FLASH_Unlock(); for (i 0; i 100; i) { FLASH_ErasePage(TEST_ADDR); FLASH_ErasePage(TEST_ADDR 1024); for (int j 0; j 512; j) { FLASH_ProgramWord(TEST_ADDR j * 4, write_val j); } for (int j 0; j 512; j) { uint32_t read_val *(volatile uint32_t *)(TEST_ADDR j * 4); if (read_val ! write_val j) { err; } } } FLASH_Lock(); printf(Flash stress test done, error count %d\r\n, err); }正品新片跑一百遍错误计数通常是0翻新片或老化片跑着跑着就开始出错了。注意测试区域不能选代码所在扇区否则等于把自己正在执行的代码擦掉了直接跑飞。这个测试比较粗暴适合开发阶段的抽检不适合量产全检。3. FreeRTOS起不来的软件陷阱不要在芯片没问题时误杀芯片排查完芯片真伪之后再来说软件。我必须替FreeRTOS说句公道话很多时候芯片是正经原装用户把工程拷过来就是跑不起来最后破口大骂芯片有问题其实是被几个经典软件陷阱坑了。3.1 优先级分组和PendSV/SysTick的优先级陷阱FreeRTOS跑在Cortex-M3上靠的是SVC、PendSV和SysTick这三个异常。内核移植代码在启动调度器时会自动把PendSV和SysTick设置为最低优先级这是它实现任务切换的前提。如果你在工程里手动调用了NVIC_PriorityGroupConfig把优先级分组从默认的全抢占分组改成了带子优先级的分组比如NVIC_PriorityGroup_2那么FreeRTOS计算中断优先级偏移时就会算错表现为vTaskStartScheduler之后直接死机或者任务切了几次之后突然HardFault。标准做法是在main函数最前面初始化所有外设之前固定使用全抢占分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);这个配置表示4位全部用于抢占优先级没有子优先级。FreeRTOS官方要求Cortex-M3移植就是这个分组方式。你一旦改成别的分组很多优先级相关的宏就会失效。另外一个我经常看到的问题用户自己写了SysTick_Handler中断函数实现软件延时然后移植FreeRTOS时忘了删结果SysTick中断入口被占用了FreeRTOS的时基就停了所有任务调度全部卡住。这种问题现象也和“芯片没反应”一模一样。3.2 堆和栈任务栈溢出和堆不足的表现FreeRTOS一跑就死第二个高频原因是内存配置。Cortex-M3的RAM有限STM32F103C8T6只有20KB RAM分给全局变量、FreeRTOS堆、任务栈之后非常紧张。堆大小由FreeRTOSConfig.h里的configTOTAL_HEAP_SIZE控制。如果设太小xTaskCreate创建任务时会返回内存不足但很多人没检查返回值结果任务根本没创建成功程序看起来就像没反应。如果设太大加上任务栈之后总RAM超了链接器会报错或者运行后栈覆盖表现就是随机死机。任务栈溢出更隐蔽。默认的configMINIMAL_STACK_SIZE一般是128个word也就是512字节跑一个空任务足够。但如果任务里定义了大数组或者调用了printf这类吃栈的库函数512字节瞬间就爆了。开启栈溢出检测是必须的在FreeRTOSConfig.h里#define configCHECK_FOR_STACK_OVERFLOW 1同时实现钩子函数void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { while(1); }一旦任务栈溢出程序会进这个死循环至少能定位到是哪个任务把栈吃光了。我一般会在每个任务里留几KB余量只在确定需要的地方加大栈。可以配合map文件查看整个工程的RAM使用情况。在Keil里编译完成后生成的.map文件里搜索“Total RW Size”或“Stack”能直接看到栈顶地址和全局变量占用。如果发现RW Size已经接近20KB那FreeRTOS任务栈就只能省着用。3.3 启动文件、AC5/AC6和芯片型号三个最容易被忽略的工程配置启动文件选错是一个特别经典的坑。STM32F103C8T6和RBT6是中容量芯片应该用startup_stm32f10x_md.sZET6、RCT6等大容量芯片用startup_stm32f10x_hd.s。如果你把大容量的启动文件用在中容量芯片上向量表长度和异常处理入口不对烧录能成功一运行就死。Keil工程里还有一个更隐蔽的问题Target选项卡里的Device型号没有选对。如果设备型号选成了STM32F103ZE但实际焊的是C8T6下载算法会按大容量芯片擦写Flash轻则下载失败重则把芯片选项字节或Flash配置搞乱。还有一个新趋势很多人从AC5编译器切到AC6Arm Compiler 6后FreeRTOS莫名其妙跑不起来。AC6的优化策略和AC5差别很大默认O2优化下某些未初始化变量、中断服务函数内的局部变量可能被优化出问题。我的建议是新工程直接用AC6没问题但老工程免费RTOS迁移时先保持AC5跑通后再切AC6且把优化等级从O0逐步往O2调每次调完都要跑完整测试。Keil里对应“Options for Target → Target → ARM Compiler”选项。另外F103没有FPU千万不要在Target选项卡里开启硬件浮点单元否则编译出来的指令在芯片上执行就会触发HardFault。3.4 外部晶振频率和SystemInit程序卡在启动阶段的现象这是我在新手群里见过最多的一种“没反应”下载完程序芯片完全不跑但调试器能连上复位后PC指针停在SystemInit里的某个while循环。原因很简单STM32F103的SystemInit默认要等待外部8MHz高速晶振HSE就绪然后把它作为PLL的输入倍频到72MHz。如果你的板子上根本没接外部晶振或者晶振没起振或者晶振实际是12MHz而不是8MHzSystemInit就会一直等HSE稳定停在while里出不去用户程序当然不会执行。排查方法有两个。第一用示波器量OSC_IN和OSC_OUT引脚确认晶振是不是真的在振荡。第二临时绕过HSE把系统时钟切到内部HSI8MHz内部RC上如果程序能跑起来说明芯片没问题问题出在外部晶振电路。切内部时钟的代码大致是RCC_DeInit(); RCC_HSICmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);在开发阶段我习惯先这样验证芯片能不能点灯再回去排查晶振。PA8引脚的MCO功能也可以用来观察内部时钟是否工作正常。通过RCC_MCOConfig把PA8配置为输出SYSCLK用示波器量PA8如果能看到72MHz方波或者你配置的时钟频率就说明时钟树已经跑起来了。这个功能在调试“芯片到底有没有在跑”的时候特别好用。4. 最小系统硬件的那些“隐形雷”电源、复位、BOOT和晶振逐一过堂把芯片本身排除掉之后还要过最小系统的硬件关。这部分的坑不深但很阴因为排查起来都特别基础基础到很多人不好意思怀疑。4.1 3.3V不干净再好的芯片也白搭STM32F103对电源的要求绝对没有很多人想的那么宽松。VDD和VDDA必须同时供电尤其是VDDA如果悬空或者电压低于VDD芯片内部复位电路和ADC模块会处于异常状态可能出现上电复位不彻底、运行中随机复位。实际项目中我遇到过一块板子现象是FreeRTOS跑几分钟就死一次裸机也偶尔死。排查到最后发现是3.3V LDO输出端的10uF电容虚焊导致动态负载时电压瞬跌到2.8V左右。FreeRTOS任务切换瞬间电流变化大电源扛不住芯片就“没反应”了。这个教训让我养成了习惯凡是新板子先拿示波器看3.3V的纹波纹波大于50mV就先修电源再谈代码。给F103的每个VDD引脚配一个100nF去耦电容VDDA额外加一个10uF电容这不是玄学是数据手册的基本要求。用FreeRTOS做多任务项目时瞬态电流大这些电容一个都不能省。4.2 复位电路和NRST波形复位电路看似简单出问题却非常隐蔽。STM32F103的NRST引脚内部有上拉外部只需要接一个100nF电容到地即可。但很多开发板为了“稳定”额外加了一个10K上拉到3.3V这本身没问题问题出在电容取值过大。如果把复位电容加到1uF甚至10uF上电时NRST被拉低的时间会变得很长或者复位释放的上升沿过于平缓导致芯片内部的复位阈值判断不稳定结果就是上电后芯片没有正常进入用户程序看着就像“没反应”。用示波器量NRST正常的波形应该是上电瞬间从0V快速跳到3.3V没有周期性跌落没有长时间保持中间电平。如果波形呈“锯齿状”持续几秒或者反复下跳就是复位电路有问题。4.3 BOOT引脚决定程序能不能跑BOOT0和BOOT1这两个引脚是很多“芯片没反应”的元凶。STM32F103有三种启动模式BOOT0拉低时从用户Flash启动这是正常模式BOOT0拉高、BOOT1拉低时从系统存储器启动进入内置BootloaderBOOT0和BOOT1都拉高时从SRAM启动不上电。我自己犯过一个错为了省事BOOT0直接悬空结果芯片上电后大概率进入系统Bootloader程序烧进去就是不运行。后来规范改成BOOT0和BOOT1各接一个10K下拉电阻到GND确保上电时是确定的低电平。这里说的“确定”很关键悬空在芯片内部虽然有默认下拉但外部干扰或焊接残留会让它处于不稳定状态。4.4 外部晶振电路的关键细节STM32F103最小系统里的外部晶振通常用8MHz无源晶振接在OSC_IN和OSC_OUT之间两端各接一个负载电容到地负载电容一般取12pF到22pF同时在两个引脚之间并联一个1M电阻用于偏置。不起振的常见原因有几个负载电容太大导致起振困难晶体两端虚焊OSC_IN和OSC_OUT接反无源晶振无极性问题但如果是用有源晶振接错方向就会出大事或者是用了质量很差的晶振。有源晶振和无源晶振的接法完全不同。无源晶振接两脚有源晶振只需要把输出信号接到OSC_INOSC_OUT保持悬空。有些人习惯性把有源晶振信号接OSC_OUT那就怎么都不起振。如果用示波器探针去量OSC_IN时波形出现了一个明显的正弦波但幅度偏小多半是负载电容太大如果完全没波形先用万用表确认晶振两端没有对地短路再考虑换一颗晶振试。5. 采购验货与量产前的真伪测试把假芯片拦在焊接之前识别假芯片最好的时机是它还没焊到板子上的时候。等贴片机打完、进入测试环节再发现成本已经付出去了。这块的经验对研发阶段和批量生产都有参考价值。5.1 开发阶段怎么选芯片来源才稳我理解开源节流的心情但开发阶段买芯片首要目标不是省钱而是可控。FreeRTOS工程偶发死机如果一直在劣质芯片上排查可能查一个星期都找不到原因。我的建议是开发板或小批量验证阶段尽量从正规目录分销商或可信平台采购哪怕单价贵一两块钱换来的却是“芯片没问题”的确定性。更重要的是索要证据。批量采购时要求供应商提供原厂出货的批次号、包装标签或代理商的采购凭证。很多翻新片渠道根本拿不出这些文件。如果供应商含糊其辞只说“保证原装正品”那就果断换一家。有个简单的价格判断STM32F103C8T6的市场行情低于某个明显不合理的价位时基本可以断定不是原装新片。芯片不是白菜签收前先看一眼价格能避免后面所有的麻烦。5.2 到货验货流程从包装到焊前抽检我现在的流程分四步每一步都不能省外包装检查正规原厂编带包装封口整齐标签信息完整批次统一。如果收到的是散装袋、白纸标签或者同一袋里丝印批次五花八门直接拒收。丝印和外观抽检从整批里随机抽几颗用微距镜头看丝印重点看字体、定位孔、引脚状态。同一批次如果出现两种不同的丝印样式大概率是混料。上机读取ID把抽检的芯片放到测试座上用STM32CubeProgrammer逐颗读取Device ID、Flash Size和UID和丝印标称型号比对。所有C8T6的Flash Size都必须是64KBIDCODE必须是0x410。Flash压力测试在正式开发板或测试板上烧录压力测试程序对Flash连续擦写上100次检查错误计数。翻新片或者劣质片在这个环节基本都会暴露。这套流程看起来麻烦实际做一次只要半小时左右但能把99%的问题芯片拦在贴片之前。5.3 我踩过的一次“假芯片”事件复盘有一年我批量采购了一批STM32F103C8T6外观包装都挺正规价格也接近市场价。裸机点灯、串口打印都没问题Flash读写也能通过。结果产品开始量产跑FreeRTOS多任务串口通信时故障率突然到了3%左右设备不定时死机。一开始我怀疑是FreeRTOS配置问题调了一周的栈和堆问题还在。后来把故障板和正常板同时用CubeProgrammer读Flash Size发现故障板的Flash容量竟然是32KBDevice ID是0x412而不是0x410。也就是说这批混入了一批C6T632KB打磨重印成C8T6的Remark片。32KB Flash的芯片跑64KB固件编译链接时按64KB分配实际Flash后半段写入失败程序在运行过程中随机读到异常数据自然死机。这件事给我的教训是单片机程序层面能查的问题一定要在程序层面之前先堵住。从那以后每批芯片到货先抽检ID和Flash容量并把读取信息归档留底。省下来的调试时间和返工成本远超多加的那一点点采购精力。这几年我处理过不少“STM32F103 FreeRTOS 没反应”的求助真正代码写错导致任务起不来的是少数更多时候是芯片来源、最小系统硬件、移植配置这三类问题叠加。强烈建议你拿到一块新板子的第一步先做一个基础自检通电量电流、示波器看NRST和晶振、CubeProgrammer读ID和Flash容量全部通过之后再烧FreeRTOS工程。五分钟的验证能帮你省掉五个小时甚至五天的盲目排查。芯片是地基FreeRTOS是上层建筑地基都没验证过就别急着优化上层代码了。
返回列表