拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32F103芯片没反应?从假芯片识别到FreeRTOS移植排查全攻略

STMicroelectronics 的 STM32F103 系列尤其是 C8T6 和 ZET6 这两个型号这几年几乎成了嵌入式开发圈的“硬通货”。不管是学生做毕设、工程师做样机还是小批量产品到处都能看到它的身影。但正因为用量太大、太经典市场上流通的芯片来源变得非常复杂拆机片、翻新片、Remark 片打磨重新打标、国产替代片鱼龙混杂。很多朋友遇到的情况是板子焊好了程序烧进去了但芯片就是不工作甚至第一次上电就毫无反应。第一反应往往是“我买到假芯片了”但真相往往没那么简单甚至很多时候芯片本身是好的问题出在你的配置、你的最小系统电路或者你对 FreeRTOS 移植的理解上。这篇内容就围绕“芯片没反应”这个现象从芯片真伪辨别、最小系统硬件排查、程序烧录与调试接口、FreeRTOS 移植后的运行异常这几个维度把常见的坑和排查思路完整捋一遍。内容不光是讲理论更偏重拿到实物后可以立刻上手的操作手段适合刚接触 STM32 的新手也适合被“假芯片”坑过、想搞清楚根因的工程师。1. 先别急着骂“假芯片”市场水到底有多深很多同学芯片一没反应就怀疑自己买到了假货。说实话市场上确实有假的 STM32F103但“假”的方式可能和你想的不一样。1.1 市面上的“假芯片”到底有哪几种目前市面上流通的所谓“假 STM32F103”大体可以分为四类每一类的坑都不一样第一类是翻新片和拆机片。这类芯片本身是正品 STM32F103但是是从旧板子上拆下来或者使用过的经过清洗、重新打标、重新切脚后流入市场。这类芯片最大的问题是引脚可能有氧化、内部 Flash 可能有残留数据、芯片损耗程度未知而且很多时候打标看起来和原厂几乎一模一样拿万用表量不出任何区别只有上机跑一段时间才能发现问题。第二类是Remark 片也就是低规格芯片被打磨后重新打上高规格型号的标。最常见的是把 Flash 容量更小的芯片标记成更大容量的型号比如把 64KB 的 C8T6 标成 128KB 的 CBT6或者把低主频版本标称成高主频版本。这种芯片用起来问题最大因为程序按照高规格芯片编写占用的 Flash 空间和 RAM 可能超出实际芯片的物理资源编译能过但烧录时会出现校验失败或者在运行到特定代码段时突然死机。第三类是国产替代芯片。这些芯片在引脚定义和指令集上兼容 STM32F103电路可以直接替换但在内部寄存器、Flash 特性、调试接口细节上存在差异。常见的有 GD32F103、APM32F103 等。这并不完全是“假”但如果你按照 STM32 的标准库和工具链配置来用遇到一些莫名其妙的问题时不能完全套用 STM32 的调试思路。第四类才是真正意义上的劣质假芯片通常是低端单片机打磨后伪装成 STM32F103。这类芯片引脚定义可能完全不同IO 电平特性也不一样烧录时校验大概率直接失败。1.2 如何通过最低成本手段判断芯片真伪在没有专业检测设备的情况下有几个非常有效的判断手段操作简单实测下来很管用。第一个手段是看丝印和底面的刻字。正品 STM32F103 的表面丝印字体清晰、边缘锐利不会出现模糊、深浅不一的情况。芯片底面会有一个类似二维码的标识这是激光刻蚀的用手摸有凹凸感。翻新片底面通常很光滑或者有明显的打磨痕迹。看的时候重点看丝印的“ST”logo 和型号字体的细节同一批次的正品丝印风格高度一致。第二个手段是用 ST-Link 或 J-Link 读取芯片 ID。这算是最可靠的判断方式。连接好 SWD 接口后用 STM32CubeProgrammer 或者 ST-Link Utility 读取芯片信息正品 STM32F103 的 Device ID 是 0x410Revision ID 会根据版本不同有差异。如果读出来的 ID 是 0x411那说明你手里的实际上是 STM32F105 或者 STM32F107如果 ID 异常或者根本无法连接那就值得怀疑了。国产 GD32F103 有时也能被识别为 0x410但通过读取 Flash 容量寄存器0x1FFFF7E0 地址处可以进一步区分GD32 的容量配置往往和 ST 有所不同。第三个手段是焊接前用万用表测电源对地阻抗。正品 STM32F103 的 VDD 对 GND 之间在不通电的情况下用二极管档测量通常会有一个 0.4V 到 0.6V 左右的正向压降反向则是无穷大。如果测出来直接短路或者压降明显异常那芯片大概率有问题。这个方法很适合在上电前快速筛掉明显的不良品。提示用万用表测阻抗只能筛掉明显损坏的芯片对翻新片和 Remark 片几乎无效所以不要因为有阻抗就排除所有风险。2. 芯片焊上去没反应先检查最小系统电路很多时候芯片没反应问题不在芯片本身而在板子的最小系统电路。STM32F103 的最小系统不复杂无非就是电源、复位、时钟、启动模式选择和调试接口这几个部分但每一个部分都藏着坑。2.1 电源部分电压纹波与去耦电容的讲究STM32F103 的工作电压范围是 2.0V 到 3.6V最常用的 3.3V 供电。很多自制板子直接用 AMS1117-3.3 从 5V 降压这本身没问题但有几个容易被忽略的细节。第一个细节是去耦电容的布局。每个 VDD 引脚旁边都应该有一个 100nF 的陶瓷电容而且这个电容必须尽量靠近芯片的引脚走线要先经过电容再到芯片。很多同学画板子的时候把电容放得离芯片很远或者用长走线连接这样高频噪声滤不掉芯片运行到关键时刻就容易复位或者死机。我见过一个案例板子工作极不稳定偶尔上电完全没反应最后发现就是 VDDA 引脚的滤波电容放在了 1 厘米之外移动电容位置后问题彻底消失。第二个细节是VDDA 和 VREF 的接法。STM32F103 的 VDDA 必须单独滤波建议串联一个 10Ω 的磁珠或小电阻再接一个 1μF 和一个 100nF 的电容对地。如果你的板子上 VDDA 直接接 VDD没有做任何滤波ADC 的精度会受影响极端情况下会造成内部复位电路不稳定。第三个细节是电源上电斜率。STM32F103 有上电复位 POR 电路要求电源电压必须在一个特定的速率范围内上升。如果电源上电太慢比如用大电容加电阻缓慢爬升可能导致 POR 电路不能正确触发芯片上电后处于不确定状态。实测中有些 LDO 的输出电容太大上电时间拉长到几十毫秒以上就可能出现第一次上电没反应、手动复位一下又好了的现象。2.2 复位电路与 BOOT 引脚最容易忽略的配置项复位电路是老生常谈但还是有人在这里栽跟头。STM32F103 的 NRST 引脚内部有上拉理论上只需要外接一个 100nF 到 GND 的电容就可以。很多人习惯用经典的 10kΩ 上拉加 100nF 电容到地的 RC 复位电路这个问题不大但如果这个电容的值过大比如用了 10μF上电时复位引脚的低电平时间会很长芯片迟迟不启动。另外如果 NRST 引脚不小心被长走线引出到板边又没有加 ESD 保护很容易被静电打坏导致芯片永远处于复位状态表现就是“上电后完全没有反应”。BOOT 引脚的配置是另一个高频坑。STM32F103 的 BOOT0 和 BOOT1 决定了芯片从哪启动BOOT0 拉低是从主 Flash 启动这是正常工作模式BOOT0 拉高、BOOT1 拉低是从系统存储器启动也就是进入 ISP 下载模式BOOT0 和 BOOT1 都拉高是从 SRAM 启动。如果你的板子上 BOOT0 悬空或者不小心被拉高代码即使烧录进了 Flash 也不会执行。很多批量生产的板子为了跳线方便BOOT0 接了一个 10kΩ 下拉电阻这是没问题的但如果你用了网上买的某些模块BOOT0 默认被拉高而你自己不知道就会出现烧录成功但芯片毫无反应的现象。2.3 外部晶振不起振就全盘皆输STM32F103 可以使用内部 HSI 时钟启动但大多数实际项目都会使用外部 8MHz 晶振配合 PLL 倍频到 72MHz。外部晶振电路虽然简单却是问题高发区。常见的问题包括晶振的两个负载电容取值不合适、晶振引脚走线过长导致寄生电容过大、晶振旁边走过高频信号线被干扰、使用了质量差的晶振导致起振时间过长等。这些问题表现出来就是芯片时好时坏或者温度一变就不工作。排查晶振是否起振最简单的办法是用示波器或者逻辑分析仪探针测量 OSC_OUT 引脚正常的 8MHz 正弦波或者方波清晰可见。如果手头没有示波器可以在代码里做一个 LED 闪烁程序强制使用 HSI 内部时钟运行如果改用内部时钟后芯片正常工作了那问题基本就锁定在外部晶振电路上。注意STM32F103 的 OSC_IN 和 OSC_OUT 引脚对地阻抗很小用万用表电阻档测量时读数很低是正常的不要误以为芯片短路。2.4 启动模式与最小系统的常见组合检查顺序综合上面几个因素当芯片上电完全没反应时建议按以下顺序排查确认 VDD 与 GND 之间电压是否为 3.3V用示波器看纹波是否过大。确认 NRST 引脚电压是否为高电平接复位按键的话按下时为低、松开为高。确认 BOOT0 是否为低电平BOOT1 状态是否为低或高都不影响从 Flash 启动。确认外部晶振是否起振或者临时改用内部时钟测试。确认 SWD 调试接口能否连接能连上就说明芯片核心逻辑在跑。按照这个顺序排查下来90% 的“上电没反应”都能找到原因。剩下的 10% 才需要往芯片本身质量上怀疑。3. 程序能烧录但芯片不跑调试接口与 Flash 的隐蔽坑有时候芯片上电、时钟都没问题SWD 也能正常连接程序烧录显示成功但芯片就是不执行代码或者执行到一半停下来。这种问题排查起来更让人抓狂因为硬件看起来都是好的问题往往出在软件配置和芯片内部状态上。3.1 读保护芯片被锁死的经典表现ST 的芯片有一个 RDPRead Protection机制也就是读保护。如果芯片的 RDP 等级被设置为 Level 1外部调试器就无法读取 Flash 内容只能做整片擦除。很多时候从网上买来的二手芯片原主人调试时开了读保护但没有解除你拿到手后可以连接 SWD可以看到芯片信息但烧录时调试器会报错或者烧录后代码不执行。解决方法是整片擦除。用 STM32CubeProgrammer 连接芯片后在选项字节界面把 RDP 等级切到 Level 0执行全片擦除把整个 Flash 清空后再烧录程序。如果擦除时报错或者一直失败那这颗芯片的内部 Flash 可能已经损坏属于硬件层面的故障。3.2 SWDIO 和 SWCLK 被程序占用这是一个非常折磨人的问题。你的程序功能没问题但代码里把 PA13SWDIO或者 PA14SWCLK配置成了普通 GPIO而且驱动能力很强或者频繁翻转就会导致调试器连接不稳定甚至完全无法连接。表现就是第一次烧录没问题程序跑起来后第二次烧录就连接不上了。解决方法有三条路一是按住复位键在调试器连接成功的瞬间松开复位让芯片保持在复位状态时被调试器接管二是使用 ST-Link 的 Connect under reset 模式在连接前拉低复位引脚三是如果上面两个方法都不行只能通过 BOOT0 拉高进入 ISP 模式用串口把 Flash 擦除。3.3 Flash 容量与烧录地址不匹配STM32F103 的 C8T6 是 64KB FlashCBT6 是 128KB FlashRCT6 是 256KB FlashZET6 是 512KB Flash。如果你用的工程模板是从别的型号拷贝过来的编译器里的 Flash 起始地址和大小没有修改可能造成烧录地址超过芯片实际容量校验就会失败。这个问题在 Keil 里很常见因为魔术棒Options for Target里的 Flash 大小设置和芯片型号绑定很多人换了芯片忘了改。另外如果你的程序编译出来的 bin 文件超过 64KB但你手里的芯片标注是 C8T6烧录时 Keil 不一定报错但运行到超出 Flash 的区域时芯片就会 HardFault 或者直接卡死。从这个角度讲程序体积异常增大时先检查芯片容量是否匹配是一个值得养成的习惯。3.4 从 Map 文件看懂程序跑飞的原因当程序烧录成功但运行异常时查看编译生成的 .map 文件是一个高效的排查手段。很多人对 map 文件有畏惧心理觉得信息量太大但其实只需要关注几个关键区域。第一个是程序体积Total RO Size 和 Total RW Size对照芯片的实际 Flash 和 RAM 容量看是否超限。第二个是栈顶地址__initial_sp这个值决定了程序启动时的栈指针如果栈顶地址不在 RAM 范围内程序上电就会跑飞。第三个是中断向量表的位置如果向量表被放在了错误的地址任何中断触发都会导致程序跳到错误位置。我在实际排查一个“程序烧了没反应”的案例时就是通过 map 文件发现 __initial_sp 指向了 0x20005000但芯片的 RAM 只有 20KB也就是 0x20000000 到 0x20004FFF栈顶超出 RAM 末尾说明工程的芯片型号选错了RAM 大小和实际芯片不匹配。改正型号后问题立刻消失。4. 程序在跑但“看起来没反应”FreeRTOS 移植后的隐形杀手还有一种情况非常具有迷惑性你用裸机程序测试 LED 闪烁一切正常一旦把 FreeRTOS 移植进去或者往原有的 FreeRTOS 工程里加任务芯片就像“死”了一样灯也不闪了、串口也不输出了。这种时候你可能会怀疑芯片是假的但实际上问题几乎都出在 FreeRTOS 的配置和使用方式上。4.1 堆栈溢出FreeRTOS 最常见的隐形杀手FreeRTOS 的每个任务在创建时都会分配一块独立的栈空间。栈空间不够的直接后果就是任务运行到一半溢出踩到相邻的内存区域把其他任务的数据或者系统内核的数据破坏掉。表现非常多样有的任务运行几次后突然卡死有的系统运行一段时间后随机复位有的干脆创建任务后就进 HardFault。FreeRTOS 提供了两种堆栈溢出检测机制由 configCHECK_FOR_STACK_OVERFLOW 宏控制。设为 1 时在任务切换时检查栈指针是否越界设为 2 时在任务切换时检查栈末尾的标记字节是否被改写。建议开发阶段把该宏设为 2配合 vApplicationStackOverflowHook 回调函数在溢出时进入一个死循环或用串口打印错误信息。但这种方法只能发现“栈已经溢出了”不能告诉你“栈需要多大”。更实用的方法是用uxTaskGetStackHighWaterMark()函数在任务运行稳定后查询任务栈的剩余空间最小值。这个函数返回的是任务栈历史上最低剩余的空间量以字为单位。开发阶段可以在每个任务的主循环里加一个周期性打印观察水印值有没有接近 0。实测下来一个简单的点灯任务只需要 128 字左右的栈但如果你在任务里调用了 printf、sprintf 这类占用大量栈空间的库函数栈需求量可能翻三四倍通常建议起步就分配 256 字调试阶段甚至分配 512 字。4.2 中断优先级配置错误导致系统卡死这可能是 FreeRTOS 移植过程中最容易被忽略、却最有杀伤力的问题。Cortex-M3 内核支持可编程中断优先级但 STM32F103 的 NVIC 只使用了高 4 位也就是优先级可以从 0 到 15。FreeRTOS 的临界区保护依赖 BASEPRI 寄存器当 BASEPRI 被设置后所有优先级数值大于等于 BASEPRI 的中断都会被屏蔽。FreeRTOS 官方要求如果要使用中断管理相关的 API比如xSemaphoreGiveFromISR中断优先级必须设置得低于configMAX_SYSCALL_INTERRUPT_PRIORITY。这里有一个 STM32 的特色数值越大表示优先级越低正好和很多人直觉相反。最经典的问题出自 STM32 固件库里默认调用的NVIC_PriorityGroupConfig()函数。很多人习惯用NVIC_PriorityGroup_2也就是 2 位抢占优先级、2 位子优先级。但 FreeRTOS 在 Cortex-M3 上要求使用4 位抢占优先级也就是NVIC_PriorityGroup_4。如果优先级分组不对configMAX_SYSCALL_INTERRUPT_PRIORITY的映射就会出错导致某个中断的优先级数值高于系统调用阈值但实际上被 FreeRTOS 认为不可屏蔽从而在中断里调用 FromISR 系列 API 时直接触发断言或者死机。在实际项目中建议在 main 函数最开始就调用NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);然后根据实际需求把需要用 FreeRTOS 信号量或队列唤醒任务的中断优先级设置为 5 到 15 之间的一个值把绝对不允许被 FreeRTOS 延迟的中断比如某些时间关键的控制中断设置为 0 到 4。这个划分逻辑在绝大多数项目中都能稳定运行。4.3 空闲任务与内存堆配置不足FreeRTOS 内核在运行时需要一个 IDLE 任务和可选的定时器任务这两个任务的栈都在启动调度器时创建。如果你的configMINIMAL_STACK_SIZE设得太小或者configTOTAL_HEAP_SIZE设置的堆内存不足以创建这两个内部任务结果是系统调度器启动后立刻崩溃表现就是程序停在vTaskStartScheduler()里出不来。很多从网上拷贝的 FreeRTOS 工程堆大小直接沿用默认值比如configTOTAL_HEAP_SIZE设置为 1024 或者 2048。这在只有一两个简单任务的演示工程里能跑但如果任务变多、创建了队列或信号量堆不够用的问题就会暴露。建议静态分析一下工程里的任务数量和每个任务需要的栈大小把堆大小设置为所有任务栈总和再乘以 1.5 到 2 倍留出余量。另外要说的就是 heap 方案的选择。FreeRTOS 源码里的 portable/MemMang 目录下提供了 heap_1.c 到 heap_5.c 五种实现很多人不知道它们的区别随便选一个。实测下来heap_1.c 只支持创建任务不支持删除任务功能最简单适合永远不删除任务的场景。heap_2.c 支持分配和释放但是不支持内存碎片整理长时间运行的系统可能有碎片问题。heap_3.c 封装了标准库的 malloc 和 free需要你提供互斥保护如果代码里使用了标准库内存函数可以考虑。heap_4.c 是目前最推荐的默认选项支持内存合并能有效减少碎片。heap_5.c 支持多内存区域适合 RAM 分布分散的芯片。5. 一部到位的 FreeRTOS 验证模板帮你排除“芯片假货”的嫌疑排查芯片是否真的是假货最直接的办法是让芯片完成一套“确定性”的操作上电后初始化时钟、初始化 LED 引脚、创建两个不同优先级的 FreeRTOS 任务、通过队列或信号量通信然后观察 LED 是否按预期闪烁。只要这套模板在芯片上能跑通基本可以排除假芯片的嫌疑。下面是这个模板的核心逻辑思路。5.1 硬件准备与工程基础我这里用最常见的 STM32F103C8T6 最小系统板来说明使用标准外设库Standard Peripheral Library配合 FreeRTOS 9.0 或 10.x 版本。硬件接线只需要一根 SWD 调试线、一个板载 LEDC8T6 板一般接在 PC13外加一个串口转 USB 模块用于打印调试信息。工程创建时注意三个基础配置Flash 大小C8T6 选 64KB如果编译器选项里没有对应值手动改。RAM 大小C8T6 是 20KB起始地址 0x20000000。优化等级调试阶段建议 -O0 或者 -O1-O2 以上优化可能导致调试时机和代码行为不一致。5.2 最小验证任务的代码设计创建三个任务一个高优先级任务负责处理串口命令一个普通优先级任务负责翻转 LED一个低优先级任务负责上报系统状态。核心代码如下#include FreeRTOS.h #include task.h #include queue.h QueueHandle_t xQueue; TaskHandle_t xLEDTaskHandle; void vLEDTask(void *pvParameters) { uint8_t msg; for (;;) { if (xQueueReceive(xQueue, msg, portMAX_DELAY) pdPASS) { if (msg 0x01) { GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // LED ON } else { GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // LED OFF } } } } void vUARTTask(void *pvParameters) { uint8_t data 0x00; for (;;) { // 模拟外部事件翻转信号并发送到队列 data (data 0x00) ? 0x01 : 0x00; xQueueSend(xQueue, data, 0); vTaskDelay(pdMS_TO_TICKS(500)); } }这个模板的意义在于验证三个关键点系统时钟是否正常LED 能按预期改变状态、调度器是否能创建和调度多任务、队列通信是否正常工作。只要这三个点都过了芯片的基本工作能力就没有疑问。5.3 用断言与错误钩子让问题自动现形在 FreeRTOS 的调试过程中除了 LED 和串口打印利用内核自带的断言机制能大幅提高定位效率。把configASSERT宏定义成一个自定义函数捕获断言失败位置#define configASSERT(x) if ((x) 0) vAssertCalled(__FILE__, __LINE__) void vAssertCalled(const char *file, int line) { /* 进入这里说明内核检测到非法操作禁止继续运行 */ __disable_irq(); while (1); }当芯片运行进入while(1)死循环时用调试器查看调用栈就能知道是哪一行内核代码触发了断言。这是一个非常高效的定位手段比盲改配置快得多。5.4 实测中的判定标准与经验数据用上面的模板在正品 STM32F103C8T6 上实测FreeRTOS 内核占用资源大约是空闲任务栈 128 字两个测试任务各 256 字加上队列和内核对象整个系统的堆内存开销在 3KB 左右。如果configTOTAL_HEAP_SIZE设为 8192运行一周都没有问题。如果上述模板在芯片上反复测试LED 不按预期闪烁或者系统运行一段时间后卡死可以先不怀疑芯片本身优先检查一下有没有把 FreeRTOS 的vApplicationStackOverflowHook和vApplicationMallocFailedHook补上。这两个钩子函数是最容易遗漏的少了它们堆栈溢出和堆分配失败不会报错芯片会默默死掉。6. 常见问题速查表与排查工具清单6.1 快速诊断速查表现象优先排查方向验证手段上电完全没反应SWD 无法连接电源、复位、BOOT 引脚万用表测量电压、短按复位键SWD 能连接但烧录报错读保护、Flash 容量不匹配全片擦除、检查工程芯片型号烧录成功但程序不执行BOOT 引脚配置、启动模式确认 BOOT0 低电平、用内部时钟测试裸机程序正常FreeRTOS 下芯片卡死优先级分组、堆大小、任务栈检查 NVIC_PriorityGroup_4、增大堆运行一段时间后随机复位硬件看门狗、电源纹波、栈溢出检查 IWDG 配置、增大任务栈偶发 HardFault函数指针、数组越界、中断未处理调试器定位 HardFault 处的 LR6.2 值得常备的软件工具排查 STM32 问题我比较依赖这几个工具STM32CubeProgrammer官方工具读保护解除、烧录、查看选项字节、读取芯片 ID 都在这里完成比第三方工具对 ST 芯片的支持更可靠。STM32 ST-LINK Utility老牌工具界面简洁调试器连接速度比 CubeProgrammer 快一点。Keil MDK 的调试视图进入 Debug 后可以实时查看寄存器、外设状态、FreeRTOS 线程列表。配置好 FreeRTOS 插件后能在调试器里直接看每个任务的状态、栈使用率排查问题效率翻倍。串口调试助手别小看它在 task 里每隔一秒打印一次任务栈水印是最朴素的稳定性测试手段。6.3 关于芯片采购的一些实际操作建议想从根本上减少“假芯片”困扰采购时要注意三点。第一优先选择正规授权代理商或者信誉好的大型电商自营渠道虽然价格比散货贵一些但胜在批次稳定、质量可靠。第二如果必须从电子市场拿货用前面说的读 ID、看丝印、测电压降三个手段在焊接前筛一遍。第三批量生产时务必先做小批量试产焊接 5 到 10 颗上电点亮 LED、跑一下通信接口、用示波器确认主频都通过后再放量。不要从散货商那里直接几百片一买出了批量问题退换货的时间成本远超那点差价。最后再分享一个小技巧。如果你怀疑芯片是国产兼容片不用慌它在大部分场合是可以直接替代 STM32F103 的。但由于国产兼容片在 Flash 的擦写时序、RDP 保护等级等细节和原厂存在差异调试时如果遇到烧录失败不要死磕试着用全片擦除后重新烧录很多时候能顺利解决。这个“擦掉重来”的做法听起来很简单但实际排查中救了我好几次。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门