STM32F103 + FreeRTOS,程序烧进去之后芯片一点反应都没有,供电正常、复位正常、代码编译零错误——这种时候,绝大多数人的第一反应是“我代码写错了”。改了一整天,查了FreeRTOS配置、查了启动文件、查了时钟初始化,问题还在。后来被资深工程师点了一句“你确定你手上的芯片是真的?”,拿去一查,果然是打磨重标片。这篇文章就把我自己那次“假芯片”排查经历展开聊聊,包括怎么区分真假芯片、怎么用最不折腾的方式避开假片坑,以及FreeRTOS跑不起来时到底该怎么一步步定位是软件还是硬件的问题。
1. 芯片没反应,先别急着怀疑你写的FreeRTOS代码
先说一下我当时遇到的情况:手头一个STM32F103C8T6最小系统板,用的是标准库,程序逻辑很简单——上电后初始化一个LED闪烁任务,再加一个串口打印任务,跑FreeRTOS。代码本身是从一个验证过的工程改过来的,芯片型号选择、启动文件、宏定义这些都核对过。
烧录的时候一切正常,校验也通过了。但一上电,LED不闪,串口没有任何输出,用示波器量了PA8(当时用定时器输出一个1kHz的PWM做“心跳”),什么波形都没有。程序就像压根没跑起来一样。
大多数人的排查顺序是:先怀疑FreeRTOS配置、然后怀疑延迟函数、然后怀疑时钟初始化、然后怀疑启动文件。我也一样,前前后后折腾了几个小时。不过后来回头看,第一轮排查漏掉了一件最基础的事——芯片本身的供电和复位引脚状态。
STM32F103的复位脚NRST是低电平复位,内部有上拉,理论上悬空也能工作。但如果最小系统板上的复位电路有问题,比如复位电容漏电、按键短路,芯片会一直卡在复位状态,程序永远跑不起来。我当时量了NRST引脚,电压只有0.8V左右,明显不对——不是高电平。这个现象用“代码逻辑”解释不了,问题出在硬件。
测下来发现是板子上复位电容焊错了,贴了一个10uF的电容,上电瞬间复位时间过长,加上板子电源纹波大,导致芯片上电后反复复位。换回100nF电容,顺便给电源并了一个10uF的钽电容,问题立刻消失了。
所以芯片没反应,第一步永远是确认硬件基础三要素:
- 电源:VDD引脚实测3.3V,纹波控制在100mV以内,注意用示波器看,万用表量出来的直流值参考意义有限。
- 复位:NRST引脚在高位,不能用万用表量一下3.3V就完事,最好复位一下看波形。
- 时钟:外部晶振是否起振,用示波器探头点OSC_IN和OSC_OUT引脚。
这三个没问题了,再往软件层面找原因。不要一上来就怀疑FreeRTOS,更不要一上来就怀疑“假芯片”——虽然这篇文章标题说的是假芯片,但很多“芯片没反应”的案例,排查到最后其实是硬件外围的问题。
1.1 最小系统这几个坑,最容易误判为“芯片坏了”
做STM32F103最小系统,大部分人用的是网上买的开发板或者最小系统板,但还有人喜欢自己画板子。自己画板子踩坑概率高得多,尤其是下面几个细节:
第一,BOOT0和BOOT1引脚的处理。BOOT0默认接10K下拉到地,从主Flash启动,这个大家都清楚。但BOOT1如果要悬空,建议也加一个下拉电阻,防止干扰导致意外进入Bootloader。我就见过一个板子,BOOT0没接电阻直接接地,BOOT1悬空,结果上电后偶尔能跑、偶尔不能跑,非常诡异——后来用镊子碰了一下BOOT1引脚,芯片直接进入了Bootloader模式,串口还往外发数据。
第二,VCAP1引脚。STM32F103C8T6没有VCAP引脚,但F103ZE这类大容量芯片有VCAP1和VCAP2,需要接2.2uF电容到地。如果这个电容没焊或者焊错容量,芯片内核电压不稳定,现象也是“完全不工作”,而且用调试器连上去会报“Cannot access target CPU”。
第三,VDDA引脚。很多人画原理图的时候,VDDA直接跟VDD连一起,中间不加磁珠或电感,也不加滤波电容。这会导致ADC参考电压不干净,但如果只是跑GPIO和FreeRTOS,影响不大。真正的问题在于,有些芯片对VDDA的电压跌落非常敏感,电源上电瞬间如果3.3V建立太慢,芯片会锁死在一个异常状态,必须重新断电再上电才能恢复。
第四,NRST复位电路。上面已经提到了,复位电容不要太大,100nF是正常值,有些板子为了“抗干扰”加了1uF甚至10uF,结果上电复位时间太长,程序跑到一半又复位了,看起来就像“芯片没反应”。
1.2 Keil下载正常不等于程序在跑,先确认调试连接
还有一个常见误判——Keil提示下载成功、校验通过,就觉得程序肯定烧进去了。但有一种情况是,芯片读保护被打开过,调试器连接时自动做了全片擦除,程序烧进去了但Option Bytes里读保护状态可能不正常。早期某些盗版ST-Link在对F103操作时,这种情况很容易出现。
另外,如果用的是ST-Link下载,程序跑不起来还有一个经典问题:SWDIO和SWCLK引脚复用冲突。F103的PA13和PA14默认是SWD调试引脚,但如果你在代码里把这俩引脚重映射成GPIO或者复用功能,并且初始化代码执行太快,调试器还没来得及保持连接,SWD就直接断开了。断开之后,如果你在代码里禁用了调试端口(比如设置了DBGMCU_CR),那芯片再上电后调试器就连不上了——现象就是芯片没反应,程序好像没烧进去。
遇到这种问题,可以先用ST-Link Utility或者STM32CubeProgrammer连接芯片,如果连接成功,说明芯片本身是好的,问题大概率还是出在代码上。如果连接都失败,再去怀疑硬件和芯片本身。
2. 什么情况下才需要怀疑“假芯片”,以及假芯片的常见套路
把硬件基础和调试连接都排查了一遍之后,还是没思路,这时候再把“假芯片”放到台面上来,算是合理的怀疑路径。但“假芯片”这词太笼统了,其实市面上流通的STM32F103问题芯片,主要分这么几类。
2.1 打磨重标片:最典型的“假芯片”陷阱
打磨重标片,就是回收旧板子上的芯片,把表面原来的丝印打磨掉,重新印上一个市场上容易卖好价钱的型号丝印。比如把STM32F103C8T6(Flash 64KB、RAM 20KB)磨掉,印成STM32F103CBT6(Flash 128KB、RAM 20KB),甚至印成STM32F103RCT6(Flash 256KB、RAM 48KB)拿去卖。
这种打磨片你说它完全不能用吧,也不一定——如果只是做简单的GPIO控制,64KB Flash的芯片当CBT6用,可能跑好久都发现不了。但如果代码量超过了64KB,烧录的时候Keil会提示“No space in execution regions”,这还算好的,至少能发现。怕的是代码量不到64KB,但RAM用超了,或者用到了被“阉割”的外设,那问题就隐蔽多了。
F103系列不同型号,除了Flash和RAM容量有差异,外设也有区别:
- C8T6没有USB OTG,CBT6也没有,但RCT6有USB OTG_FS。
- 某些型号的串口数量不同,C8T6只有3个USART,RCT6有5个。
- 某些型号的定时器数量不同,高级定时器TIM1和TIM8在C8T6上也有,但如果是打磨自更小容量的芯片,就可能缺失。
我在一个项目里就遇到过,用了一个“CBT6”,代码里初始化了USART3,上电后发不出数据,折腾了一整天,最后发现这个芯片其实是C8T6打磨的——C8T6上USART3引脚确实存在,但硬件连接到了另一个USART上(具体是USART2),因为芯片本身只是C8T6的Die。这种情况最坑,因为不仔细对比数据手册里的外设映射,根本看不出问题。
2.2 翻新片:引脚氧化、内部键合损伤是隐雷
翻新片是从旧板子上拆机下来的芯片,清理引脚、重新植球后二次流入市场。这种芯片最大的问题不是“能不能用”,而是“能用多久”和“是否稳定”。
拆机片在拆焊过程中,引脚和内部键合线可能受到热应力损伤,短期内测试可能一切正常,但上电跑几天甚至几小时后突然就死机了,复位也没用,必须断电重启。如果你在产品里用了这种片子,售后问题会让你想砸了自己的脚。
翻新片从外观上比较难分辨,但有几个观察点:引脚尖端是否有焊锡残留痕迹、引脚是否有重新整形过的压痕、芯片底面是否有被加热过的变色印记。当然,这些观察手段只能作为参考,因为有些翻新片处理得相当干净。
2.3 低端型号冒充高端型号:容量与ID都能造假?
还有一种情况,是拿更低端的芯片冒充。比如用STM32F103T8U6(QFN36封装)冒充C8T6,或者用STM32F030系列芯片打磨成STM32F103——因为F103的价格比F030高出好几倍,利润空间大。
这种“跨系列”冒充,其实很容易识破,因为F030和F103的内核都不一样,一个是Cortex-M0,一个是Cortex-M3。上电后直接读DBGMCU IDCODE就能看出来。但普通用户如果只看丝印,很容易被忽悠。
还有更狠的,用国产的GD32F103、APM32F103来冒充ST的STM32F103。GD32F103和STM32F103引脚完全兼容,程序也基本兼容,但外设寄存器有细微差别,有些外设的时钟分频方式不一样。如果你在STM32F103上跑得好好的代码,放到GD32上可能偶尔工作正常、偶尔不正常。这种“李鬼”芯片,你用串口指令读取芯片ID(0xE0042000地址的UUID)就能看出区别,但前提是你得先怀疑它。
2.4 假芯片怎么鉴定,几块钱成本就能搞定
说了这么多假芯片的类型,具体怎么鉴定,我列一个自己用过的操作流程:
第一步,看丝印。正品ST芯片的丝印工艺很稳定,字体边缘清晰锐利,激光打标的深度一致。打磨重印的丝印往往字体发虚、边缘有毛刺、油墨感偏重。另外ST的丝印上有一个“M”标志,是马来西亚封装工厂的标识,正品的M标志拉丝工艺细节在40倍放大镜下非常清晰,翻新打磨片很难模仿。
第二步,读芯片ID。用ST-Link或者J-Link连接芯片,读DBGMCU的IDCODE寄存器,正常STM32F103应该是0x410(或0x411,不同批次有差异)。如果读出来不是这个值,甚至读不出来,那就非常可疑了。STM32F103C8T6的UID(唯一设备标识)在0x1FFFF7E8地址读取,96位。虽然UID不能直接证明真假,但至少能排除“连ID都读不出来”的低端假货。
第三步,读Flash容量选项字节。STM32F103的Flash容量存储在0x1FFFF7E0地址(Option Bytes区域),是一个16位值,单位是KB。读出来如果是64,说明是C8T6;如果是128,说明是CBT6。如果丝印写着CBT6但读出来是64,恭喜你,买到重标片了。
第四步,跑一个跨引脚测试程序。把所有GPIO口都配置成推挽输出,轮流输出高低电平,用万用表或示波器逐个量。有的假芯片某些引脚内部没绑定Die,这个测试能直接暴露问题。不过这一步比较耗时,我一般是在已经怀疑到具体芯片时才做。
第五步,用功耗判断。工作状态下,STM32F103C8T6跑8MHz外部晶振,空循环电流大约在10mA左右(关闭外设时),如果芯片一上电电流就异常大或者异常小,都值得怀疑。当然这个判断需要经验积累,只能做辅助参考。
3. FreeRTOS运行异常的排查链路:先判断软件还是硬件
排查完芯片本身,假设确认了芯片没问题,但FreeRTOS还是跑不起来,这时候就需要一条系统的排查链路。我见过太多人卡在FreeRTOS上,最后发现其实跟FreeRTOS半毛钱关系没有。
3.1 第一步:用裸机代码验证基础外设
不要一上来就上FreeRTOS。先用裸机代码,只初始化时钟、一个GPIO、一个定时器,让LED以固定频率闪烁。如果这个程序都跑不起来,那就别折腾FreeRTOS了,回到第一章的基础硬件排查。
这一步看着简单,但真能省下大量时间。FreeRTOS像一个精密的调度系统,它默认让你觉得“代码写得对不对”是关键,但实际上底层硬件没跑通,系统再复杂也白搭。
我自己的习惯是,所有新板子、新芯片,先烧一个经典的“LED闪烁裸机程序”,然后配一个串口打印。这两个都正常了,再往上面叠加FreeRTOS。
裸机验证时要注意一个坑:STM32F103的标准库和HAL库,在时钟初始化上是有差异的。标准库是SystemInit() + 用户在main里调用RCC配置函数,HAL库是SystemClock_Config()。如果你之前在HAL库工程上跑通了某个外设,切到标准库时忘了重新配置时钟树,外设初始化可能没问题,但实际外设时钟频率不对,导致串口波特率完全不准——这时候看起来就像“程序没跑起来”,但LED可能其实在闪只是你没注意。
3.2 第二步:FreeRTOS能编译能下载,但任务不执行
这是FreeRTOS入坑最常见的问题:编译通过、下载成功,但任务要么一个都不执行,要么只有一个任务执行。
优先级配置是第一个检查点。FreeRTOS的调度规则是,高优先级任务就绪时会抢占低优先级任务。如果你在main函数里创建了一个高优先级任务,然后这个任务里有一个死循环且没有调用任何可能阻塞的API(比如vTaskDelay、vTaskDelayUntil、xQueueReceive等),那么低优先级任务永远不会被执行。
还有一个非常隐蔽的坑:空闲任务(Idle Task)的优先级是0,理论上最低。但FreeRTOS要求至少有一个任务处于就绪态,否则会触发configASSERT。如果你把所有任务都阻塞了,空闲任务会接管,但如果你在钩子函数里做了什么耗时操作,会影响系统节拍。
更好的检查方式是:在任务A里加一个计数器变量,在其他任务里加另一个计数器,然后在主循环或一个低优先级任务里比较两个计数器的增长速率。如果哪个计数器不增长,就知道哪个任务没跑。
另外一个经典问题:FreeRTOS堆栈溢出检测。configCHECK_FOR_STACK_OVERFLOW这个宏,可以设置成1或2,但很多人的工程里是0(关闭)。如果任务内使用了较大的局部数组,导致任务栈溢出,FreeRTOS的行为是未定义的——可能是直接HardFault,也可能是系统卡死,但最诡异的是“看起来一切正常,只是某个任务偶尔不跑”。我之前排查过一个案例,一个任务里定义了一个512字节的局部数组,任务栈分配了256字(1024字节),按理说够用,但加上函数调用栈帧和中断嵌套开销,实际栈使用已经接近顶格,运行几个小时后栈溢出,系统崩了。
所以新工程建议直接把configCHECK_FOR_STACK_OVERFLOW设为1,同时打开一个串口打印或者LED指示,在vApplicationStackOverflowHook里打一个标记。
3.3 第三步:SysTick被占用,FreeRTOS的时间脉搏断了
STM32F103上跑FreeRTOS,系统节拍默认是用SysTick实现的。如果你在裸机工程里用了delay函数,比如著名的delay_ms(),它也是基于SysTick的——这样就会跟FreeRTOS产生冲突。
典型现象是:程序烧进去后LED闪了一下就再也不动了,或者干脆连第一次闪烁都没有。因为SysTick_Handler里既有裸机的延时递减逻辑,又有FreeRTOS的xPortSysTickHandler,两者互相干扰,系统节拍直接错乱。
排查方法很简单——屏蔽掉所有裸机延时函数相关的SysTick处理逻辑,统一使用FreeRTOS的vTaskDelay和vTaskDelayUntil。
还有另一种情况:你的工程用到了HAL库的HAL_Delay(),它内部用的也是SysTick,但HAL库在初始化时会把SysTick优先级设置成最低,而FreeRTOS要求SysTick中断优先级是最高优先级组里的最低优先级(数值最大),两者一冲突,同样会导致调度异常。幸好FreeRTOS的移植层里有vPortSetupTimerInterrupt这个函数,你可以在里面重新配置SysTick,但如果你用的是默认的HAL配置,就需要手动覆盖掉。
3.4 第四步:中断优先级分组,FreeRTOS最苛刻的要求
FreeRTOS在Cortex-M3上有一个硬性要求:NVIC中断优先级分组必须设置为优先级组4(全部4位用于抢占优先级,即NVIC_PriorityGroup_4)。这是因为FreeRTOS的临界区保护依赖BASEPRI寄存器,它限制了可屏蔽的中断优先级阈值。如果优先级分组不是组4,BASEPRI机制就无法正常工作,临界区代码会被高优先级中断打断,导致FreeRTOS内核数据结构损坏。
这个错误不会导致编译失败,甚至不会立即导致崩溃。它往往是系统运行了一段时间后,某个中断触发了,FreeRTOS的链表操作被破坏,然后系统进入HardFault,或者任务行为完全不可预测。
在STM32F103标准库中,调用NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)即可。HAL库中是HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)。关键是在系统初始化早期就设置好,最好是在main函数的第一行,任何外设中断初始化之前。
3.5 第五步:堆分配器选错,内存管理直接崩掉
FreeRTOS源码里提供了5种堆分配器,从heap_1.c到heap_5.c。F103这种小内存芯片,常用的是heap_2.c或heap_4.c。
heap_1是最简单的分配器,只支持分配不支持释放,适合所有任务都是常驻创建、从不删除的场景。如果你用了任务删除功能、队列删除功能,heap_1会直接报错。heap_2支持释放,但不会合并相邻空闲块,容易产生碎片。heap_4是heap_2的改进版,会合并相邻空闲内存块,是比较推荐的选择。
常见错误是:工程里同时添加了多个heap_x.c文件,导致链接时出现重复符号定义,编译报错。还有一种情况,是你在main里申请了很大一块内存(比如128KB的数组),然后把configTOTAL_HEAP_SIZE也设置得很大,结果芯片总共只有20KB RAM,程序一启动就爆了。
怎么确认堆分配器够不够用?最简单方法:xPortGetFreeHeapSize()函数返回当前空闲堆大小,把它打印出来。如果你的任务栈加堆的总需求超过芯片总RAM,FreeRTOS会在初始化时直接断言失败。在FreeRTOSConfig.h里把configASSERT定义为自定义函数,可以打印出错行号,排查起来效率高很多。
3.6 第六步:HardFault处理的野路子
系统跑着跑着突然HardFault了,但没有接调试器,不知道卡在哪个位置。这时候用一个原始的黑科技:在HardFault_Handler里加一个死循环,同时翻转一个LED引脚。看LED闪烁的频率变化,就能粗略判断是哪个中断导致的问题——比如“滴答”提示音频率跳变时崩溃的,可能是定时器中断优先级配置出了问题。
更正规的做法是:在HardFault_Handler里读栈指针MSP或PSP,把压栈的8个寄存器(R0-R3、R12、LR、PC、xPSR)解析出来,PC值就是触发HardFault的指令地址,配合Keil的map文件就能定位到具体函数。这个流程两步走:先用IPSR寄存器判断是否在中断上下文,再根据CONTROL寄存器判断用的是MSP还是PSP。
不过说实话,对大多数FreeRTOS初学者来说,与其分析寄存器和栈,不如先跑一遍裸机、再逐步加任务——把问题缩小到某一个任务或某一个外设中断上,往往更快。
4. 实测中“假芯片+FreeRTOS”的联合坑:症状比单一问题更迷惑
前面说的都是单一问题。但在实际项目中,最头疼的是“假芯片”和“FreeRTOS”两个问题叠加,那排查起来才真是炼狱模式。
4.1 假芯片的RAM容量不足,FreeRTOS任务莫名消失
我经历过一个项目,用的是从某宝买的STM32F103C8T6,丝印看起来没毛病,C8T6该有的标记都有。但项目里跑了一个比较复杂的FreeRTOS应用:6个任务,每个任务栈1-2KB,加上各种队列和信号量,预估RAM需求大约17KB。C8T6的RAM是20KB,理论上是够的。
但程序跑起来经常出现这样的情况:某个任务执行了一会儿就再也不执行了,其他任务正常。用调试器看任务状态,发现那个任务不在了,但这不像是被删除了,更像是任务控制块被破坏了。
最终定位到,那批芯片实际封装的是F103C6(RAM 10KB),剩余内存根本不够跑6个任务。任务创建时的内存分配其实已经失败了,但xTaskCreate返回的错误被忽略了,空指针任务控制块被塞进了就绪链表,系统没有立即崩溃,而是运行几秒后才开始出现各种奇怪现象。
这种问题有一个规律:代码量不大、芯片内Flash也够用,但运行时间越长越容易出问题。因为任务栈和堆的内存分配依赖可用RAM,RAM不足不会在编译时报警,只有在运行时才会暴露。排查思路是:把configTOTAL_HEAP_SIZE打印出来,看xPortGetFreeHeapSize()在初始化后剩余多少,如果少得离谱,就要怀疑芯片RAM容量是否真实了。
4.2 假芯片外设缺失,导致任务阻塞后无人唤醒
另一种让人崩溃的场景:某任务使用xQueueReceive等待一个数据队列,数据由USART中断(或DMA中断)填充。程序烧进去后,这个任务永远等不到数据,系统看起来就像“卡死”了。
这种情况,先排查的不是FreeRTOS,而是USART中断到底有没有触发。用调试器在USART中断服务函数里打断点,如果断点根本没有被命中,说明中断没有进来。
然后查外设寄存器。如果芯片是假的——尤其用更低端型号打磨的——某些外设的基地址确实存在,但对应的外设时钟门控寄存器(RCC_APB2ENR/RCC_APB1ENR)中对应的位可能被“阉割”了(Die上没有实际外设逻辑),使能时钟后外设寄存器没有任何反应。
我遇到过最离谱的情况:伪造芯片上的SPI1寄存器读取值全是0xFFFF,初始化时写入CR1寄存器后回读,永远是0xFFFF。在代码里加了回读校验后,程序立刻停在断言处,这才发现是芯片问题。
所以在FreeRTOS的代码里,建议给关键外设的初始化加上校验逻辑。比如SPI配置完后回读CR1,确认设置的使能位真的写进去了。正常芯片回读值应该是预期值,如果完全是0xFFFF或0x0000,说明外设不存在或引脚没映射对。
4.3 假芯片的系统节拍不准确,任务调度看着像“随机”
还有一种情况,芯片是高频晶振启动的翻新片,但内部RC振荡器校准值已经丢失或偏差过大。FreeRTOS的vTaskDelay用SysTick计数,SysTick的时钟源来自内部RC还是外部晶振,取决于RCC配置。
如果外部晶振没起振但代码配置的是外部晶振,SysTick就卡在等待时钟标志位的死循环里,系统表现就是“完全没反应”。如果芯片内部HSI校准偏差大,外部晶振又不起振,但HSI被配置成系统时钟源,系统能跑,但时间基准漂移严重。原本1000ms的vTaskDelay可能变成800ms,也可能变成1300ms,具体取决于芯片内部RC振荡器的真实频率。
排查方法:用一个外部秒表计时,看LED闪烁周期是否稳定。如果周期明显偏差超过5%,先把时钟配置改成外部晶振,或者检查晶振两脚的波形。如果晶振不起振,多半是芯片的RTC域Vbat引脚没接电源(有些最小系统板的VBAT直接接地了,但F103的VBAT和VDD在内部有切换,VBAT接地虽然能工作,但在某些芯片版本上会影响外部晶振起振)。
4.4 用软件手段识别“FreeRTOS跑不动”是否源于硬伤
识别假芯片和软件问题的干扰,不能只靠硬件测量,纯软件也能做几件事。
第一,任务创建时检查返回值。xTaskCreate返回pdPASS才说明创建成功。之前见过有人在main里连写6个xTaskCreate,完全忽略返回值,任务创建失败了就静默失败,系统“看起来”跑起来了,但任务少了,功能就不正常。把返回值打印出来,如果某个任务返回的是errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY,那就是内存不足,得调大堆或者缩小任务栈。
第二,用vTaskList或者vTaskGetRunTimeStats查看任务运行状态。vTaskList需要打开configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS,它能把任务名、状态、栈高水位线、优先级都列出来。如果某个任务的栈高水位线几乎为0,说明这个任务栈快爆了。
第三,监控空闲任务的运行时间占比。如果是假芯片RAM不足,导致内存分配失败,空闲任务的运行时间比例会异常高(超过99%),因为大部分任务都没被调度起来。如果空闲任务占比正常但任务诡异的“失联”,那大概率是某个任务进入了死循环或永久阻塞,跟芯片关系不大了。
5. 如何从源头避开假芯片,以及买到芯片后的三道验证关
排查了半天,最终还是要从源头解决问题。买STM32F103,几个渠道的靠谱程度完全不一样。
5.1 采购渠道按优先级排
- 原厂授权分销商:比如得捷电子、贸泽电子、安富利,价格偏高但货源最可靠。
- ST官方天猫旗舰店或者ST代理商的电商平台:良莠不齐,要看是不是官方背书。
- 某宝个人店铺:价格诱人,但翻车概率极高,尤其是标注“国产替代”“兼容ST”的,基本可以默认是GD32或者APM32的打磨片,除非你明确要买国产型号。
- 拆机板/二手市场:只适合做实验和验证代码,千万别用在批量产品上。
5.2 三种验证手段,我只挑最实用且成本可控的说
第一,用J-Flash或者STM32CubeProgrammer,读芯片的IDCODE和Flash容量选项字节。这个方法最直接,成本为零,只需要一个ST-Link。连上芯片后,如果Flash容量选项字节和丝印型号对不上,直接退货。
第二,用示波器测外部晶振引脚波形。正常外部晶振起振后,OSC_OUT引脚能测到稳定正弦波,频率跟晶振标称值一致。如果完全没有波形,大概率是代码配置了内部时钟,或者晶振相关的负载电容有问题,但结合“芯片丝印正常却怎么都不起振”这种情况,就要考虑芯片内部的RTC振荡器是否损坏。
第三,跑一个压力测试程序。把芯片的所有外设全部初始化一遍——SPI、I2C、USART、ADC、定时器、PWM——每个外设都做一个回环测试。这个方法能发现“引脚内部未连接”“外设被阉割”的假芯片。写测试程序不需要太复杂,关键是覆盖所有功能模块。我一般用UART回环测试(TX接RX外部短路,发送固定数据再接收,校验是否正确)、SPI内部Flash/外部EEPROM读写测试、定时器输出波形比对测试这三项,基本能发现大部分问题。
5.3 复盘:当时那批“假芯片”到底是怎么回事
回到文章开头说的那次经历。当时查到最后,我把芯片拆下来,用显微镜看丝印,确实发现M标志有点异样,但程度有限,还不是一眼假。真正让我确认的,是读Flash容量选项字节——丝印上写着CBT6(128KB),实际读出来是64KB。
而FreeRTOS跑不起来的直接原因也很清晰:CBT6和C8T6的Flash布局虽然容量不同,但在硬件上CRC校验寄存器地址有差异。我用CBT6的选项字节参数替换工程后,代码重新编译烧录,仍然跑不起来。最后换成一颗从可靠渠道买的正品CBT6,同样的代码、同样的FreeRTOS配置,上电后一切正常。
那一次经历让我养成了一个习惯:无论是拿开发板做实验还是做小批量产品,新芯片到手,先花三分钟读ID和Flash容量,再烧一个几分钟就能跑完的裸机外设自检程序。这“三分钟”看起来耽误工夫,实际上比后面几天排查问题划算得多。
6. 写在最后的一些经验和建议
跟STM32F103和FreeRTOS打了这么些年交道,踩过数不清的坑,有些经验对刚入门的朋友可能更有用。
6.1 如果调试器连不上,先检查这几样
- ST-Link驱动是否正常,设备管理器里识别到的是什么设备。
- 目标板供电电压是否是3.3V,如果调试器给板子供电,注意板子是否存在短路,导致电压被拉低。
- 连线长度和接线方式。SWD其实只需要4根线(SWDIO、SWCLK、GND、3.3V),但线序接反是最常见的低级错误。
- 如果还连不上,试试用STM32CubeProgrammer的连接选项里选“Connect under reset”,在复位期间建立初始连接,能解决很多“芯片锁死”的误判。
6.2 FreeRTOS配置的一个推荐基准值
给用F103的朋友一个可以直接抄作业的FreeRTOSConfig.h关键配置基准(当然,要根据自己的项目调整):
- configCPU_CLOCK_HZ:如果是外部8MHz晶振,系统时钟72MHz,填(72000000)。
- configTICK_RATE_HZ:默认1000,如果任务切换很频繁,可以降到100减少SysTick中断开销。
- configMAX_PRIORITIES:5就够用,任务优先级范围0-4,优先级5以上不要直接使用。
- configMINIMAL_STACK_SIZE:128(字,不是字节),这是空闲任务的最小栈,也就是512字节。
- configTOTAL_HEAP_SIZE:我一般用12KB起步,任务多、队列多就按需调整。C8T6有20KB RAM,堆+全局变量+任务栈的总和不要超过18KB,留一点余量给中断嵌套。
- configUSE_TIME_SLICING:1,启用时间片轮转,尤其适合多个同优先级任务轮流执行。
6.3 最后分享一个我常说的“三板斧”排查法
遇到STM32F103+FreeRTOS的“芯片没反应”,从怀疑假芯片开始当然可以,但更高效的做法是先把“假芯片”问题放在最后怀疑,先走三板斧:
第一板斧:确认硬件基础(供电、复位、时钟、BOOT)。用示波器看NRST引脚电平,用示波器看外部晶振是否起振,用万用表量VDD和VDDA,确认BOOT0是低电平。 第二板斧:烧裸机最小程序(LED闪烁+串口打印)。如果裸机程序跑不通,跟FreeRTOS没关系。 第三板斧:把FreeRTOS任务逐一添加,每加一个任务就验证一下。如果加某个任务后系统开始异常,问题就锁定在你刚加的这个任务里——不管是栈溢出、内存不足、优先级配置还是硬件外设冲突,都能快速暴露。
三板斧都走完了还没解决,再去找供应商的麻烦。先自己排查到能排除99%的可能性,再怀疑芯片本身,才能做到有理有据。假芯片在市场上的比重虽然不算低,但也不至于每块都是假的——盲目怀疑芯片,反倒容易把自己带偏。