ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32F103上电没反应?FreeRTOS跑飞?你可能买了假芯片

STM32F103上电没反应?FreeRTOS跑飞?你可能买了假芯片 新焊好的板子程序烧进去芯片一点反应都没有或者是昨天还好好的今天再上电串口不打印LED也不闪。这种问题在 STM32F103 FreeRTOS 的项目里特别容易遇到很多朋友第一反应是查代码查 FreeRTOS 配置查任务优先级折腾一整天发现屁用没有。我跟你讲排除了电源和焊接问题之后你大概率是买到“假芯片”了。这里说的“假芯片”不一定是那种打磨Remark的报废片更多时候是指“非原厂正品”的ST芯片比如国产替代的APM32、GD32、MM32甚至是翻新片、散新片。这些芯片在裸机简单点灯的时候可能跑得飞起一旦上了 FreeRTOS涉及更复杂的时钟配置、外设中断、堆栈操作问题就全冒出来了。这篇文章我就结合自己踩过的坑从芯片识别、最小系统排查、FreeRTOS 移植适配这几个角度把“芯片没反应”这件事彻底讲透。不管你是学生做毕设还是工程师做产品只要你手上是 STM32F103这篇文章都值得你花十分钟看完。1. 现象归类先搞清楚“没反应”到底是怎么个没反应法芯片没反应听起来是一个问题实际操作中至少能分成四五种完全不同的现象。我建议你先别急着换芯片也别急着改代码坐下来把现象写清楚很多时候问题就藏在这些细节里。1.1 四种典型的“没反应”现象我整理了这几种最常见的现象你在排查的时候可以对号入座现象A上电后芯片发烫电流异常大。这种通常不是假芯片的问题而是焊接短路、电源接反或者芯片本身已经损坏。用手摸芯片表面如果烫得不敢碰赶紧断电检查。现象B程序烧录时报错连接不上芯片。提示“No target connected”或者“Cannot access Target”这种情况要么是下载线接线问题、BOOT引脚配置不对要么就是芯片根本就没跑起来。现象C程序能烧进去但上电后完全不运行。LED不闪、串口无输出、示波器量不到任何波形。这种情况最常见也最容易让人怀疑是程序问题但实际上很多是芯片本身的问题。现象D裸机程序正常一上 FreeRTOS 就跑飞或死机。这是最隐蔽的一种裸机点灯、跑个延时都好好的创建几个任务之后系统直接 HardFault或者任务调度器启动后芯片就像死了一样。我的建议是先把现象A和现象B排除掉因为这两种绝大多数是硬件连接和芯片物理损坏问题和 FreeRTOS 关系不大。现象C和现象D才是我们今天讨论的重点也是“假芯片”问题最典型的两种表现。1.2 为什么 FreeRTOS 更容易暴露“假芯片”问题很多人不明白为什么裸机好好的上了 FreeRTOS 就出问题这里面的核心在于 FreeRTOS 对硬件资源的利用方式和裸机完全不一样。裸机程序往往是顺序执行的对时钟精度的要求没那么高对内核外设如 SysTick、PendSV、SVC的使用也非常初级。但 FreeRTOS 一旦跑起来事情就变了FreeRTOS 依赖SysTick产生系统节拍如果芯片的时钟配置有偏差系统节拍的频率就会不准导致任务调度时快时慢表现就是任务“卡死”或者运行异常。FreeRTOS 使用PendSV和SVC中断来完成上下文切换这两个中断的优先级配置如果不对系统根本无法正常工作。FreeRTOS 对堆栈的操作非常频繁如果芯片的 RAM 有坏块或者容量不对任务创建时就可能直接 HardFault。国产替代芯片比如 GD32、APM32在硬件寄存器层面和 STM32F103 大多兼容但在某些细节上是有差异的。这些差异在裸机下可能根本不会触发但在 FreeRTOS 这种对时序敏感的系统下就会像一个定时炸弹一样随时爆给你看。我跟你说一个我自己的真实案例。之前做的一个项目用了某国产替代芯片裸机跑 MODBUS 从站一点问题都没有连续跑三天三夜都不死机。后来在这个基础上移植 FreeRTOS加了一个 1ms 周期的任务结果系统跑十几分钟就死一次而且死得很随机没有任何规律。最后排查了整整两天发现是芯片内部 RC 振荡器的精度和 ST 原厂有差异导致 SysTick 的节拍偶尔会丢一拍FreeRTOS 的时基就不准了任务调度跟着就乱了。后来换成正经渠道的 STM32F103同样的代码跑了一个月都没出过一次问题。2. “假芯片”识别手册从外观到内部一步一步验明正身既然标题叫“你可能买到假芯片了”那识别假芯片肯定是重头戏。我先跟你聊聊市面上最常见的几种“假芯片”再给你一套我在实际工作中用的验证方法。2.1 市面上常见的几种“非正品”芯片第一种是打磨Remark片。这种芯片是把旧芯片或者低端芯片表面的字磨掉重新印上 STM32F103C8T6 的标记。这种芯片最坑因为你是很难通过外观分辨的只有上电测试才能发现问题。我见过最离谱的一次客户寄过来的板子芯片表面丝印、封装尺寸、引脚定义全都对得上就是死活跑不起来最后用热风枪吹下来一看芯片底部的 die 根本就不是 ST 的。第二种是国产替代片。这个严格来说不能叫假芯片GD32、APM32、MM32 这些都是正规国产芯片但问题是它们的寄存器级别兼容性并没有做到 100%。很多人在淘宝上搜“STM32F103C8T6”搜出来的可能直接就是国产替代卖家也不会告诉你。你要是把它们当 ST 原厂芯片来用在某些外设和内核特性上就会踩坑。第三种是翻新片和散新片。翻新片是从废旧板子上拆下来重新整形、镀脚、印字的芯片散新片是原厂流出的未正式打包的芯片或者从散料市场流出的芯片。这两种芯片本身的性能参数没有保证上机前你根本不知道它经历了什么。我之前买过一批散新片外观漂亮得不行但十片里有三片上电就发烫剩下的七片里又有两片跑不了 FreeRTOS。第四种是低容量替代高容量。STM32F103 系列有 C8T664KB Flash20KB RAM和 CBT6128KB Flash20KB RAM等不同容量的型号。有些商家会把 C8T6 打磨成 CBT6 卖多卖好几块钱。你看着是 128KB 的 Flash实际上只有 64KB程序编译出来超过 64KB 就烧不进去或者烧进去就死机。2.2 外观检查的五个关键点收到芯片之后在焊接之前我建议你先做一轮外观检查。这轮检查不需要任何设备肉眼加手机微距镜头就够了。第一步看丝印字体。ST 原厂芯片的丝印字体清晰、均匀、有立体感用显微镜或者手机微距拍照放大看笔画边缘是干净的。打磨Remark片的丝印字体往往偏细、偏淡或者是激光打上去的那种很“浮”的感觉。正品 ST 芯片的丝印是激光烧蚀的有轻微的凹凸感打磨片重新印的字往往是喷墨打印的表面平滑放大看有颗粒感。第二步看引脚。原厂新片的引脚是亮银色或者亮金色的表面光滑没有氧化痕迹。翻新片的引脚往往经过重新镀锡颜色偏暗或者能看到引脚根部有残留的助焊剂痕迹。第三步看封装边缘。原厂封装的边缘是整齐的塑封体和引脚框架之间没有缝隙。翻新片因为经历过拆焊封装边缘经常能看到细微的裂纹或者变色。第四步看芯片厚度。这个需要一点经验STM32F103C8T6 的封装厚度是有标准的如果你手上的芯片明显偏厚或者偏薄就要多留个心眼。当然这个方法对新手来说不太好掌握我建议还是以前面三步为主。第五步看批次号。ST 原厂芯片的批次号和丝印是配套的同一批次的芯片批次号是一致的。如果你买的芯片表面丝印一样但批次号五花八门那大概率是散料凑的或者翻新片。2.3 上电验证从 ID 读取到功能测试外观检查只是第一道关真正靠谱的方法还是上电验证。先用 ST-Link 或 J-Link 连接芯片尝试读取芯片的 ID。STM32F103 的 Device ID 是 0x410放在内存地址 0xE0042000 处。用 STM32CubeProgrammer 或者 ST-Link Utility 都能直接读到。如果读出来的 ID 不是 0x410那这颗芯片不用看了直接退货。这里要提醒你一句很多国产替代芯片的 ID 也是 0x410比如 GD32F103 的 ID 就是 0x410。所以读 ID 只能排除那些打磨Remark的低端芯片不能区分原厂和国产替代。要区分是不是原厂 ST 芯片更靠谱的方法是读芯片的Flash 容量寄存器。STM32F103 的 Flash 容量寄存器在地址 0x1FFFF7E016 位宽。C8T6 读出来应该是 0x40即 64CBT6 读出来应该是 0x80即 128。如果读出来的值和丝印标的不一样那基本可以确定是低容量冒充高容量了。再进一步可以跑一个完整的外设自检程序。我自己的习惯是拿到新批次的芯片先烧一个测试固件进去这个固件会依次测试 GPIO、USART、SPI、I2C、ADC、定时器这些外设通过串口把每个外设的测试结果打印出来。只有全部外设都测试通过的芯片我才会拿来跑正式项目。这个过程看起来费时间但和产品出了问题再返工相比这点时间成本根本不值一提。2.4 购买渠道的避坑建议识别假芯片的终极方案其实是避免买到假芯片。我在采购上吃过太多亏了给你几个实在的建议不要去淘宝搜“STM32F103C8T6 便宜”。全网最低价的东西十有八九有问题。芯片不是普通商品价格低得离谱一定有猫腻。优先选正规代理商或者授权分销商。比如得捷电子、贸泽电子、安富利这些价格虽然贵一点但货源有保障。散新片、翻新片不是不能用但要明确它的风险。如果你只是做学习、做毕设买几片散新片练手完全没问题但如果你在做产品千万别在芯片上省这个钱否则量产的时候哭都来不及。批量采购前一定先打样测试。不管商家吹得多好先买十片回来跑一遍完整测试确认没问题再下大单。3. 最小系统排查芯片没反应先从“家底”查起说完了假芯片识别我们回到最基础的问题芯片没反应。不管你用的是不是正品芯片如果最小系统电路有问题芯片照样罢工。这一节我带你从头到尾排查一遍最小系统把基础打牢。3.1 电源是最大的嫌疑人STM32F103 的工作电压是 2.0V 到 3.6V推荐电压是 3.3V。很多朋友图省事直接拿 5V 接到 VDD 上这肯定是不行的。还有些朋友用的是 USB 供电USB 的 5V 经过板子上的 LDO 降压到 3.3V如果 LDO 的压降不够或者负载能力不足芯片就会在上电瞬间拉低电压导致启动失败。我跟你说一个很容易忽略的细节STM32F103 对电源的纹波和瞬态响应是有要求的。如果 VDD 引脚上的去耦电容没焊或者焊得不对芯片内部的逻辑电路就可能因为电压跌落而复位或者死机。尤其是在 FreeRTOS 这种系统下任务切换瞬间电流变化很大对电源的要求更高。正确的做法是在每个 VDD 引脚旁边放一个 100nF 的陶瓷电容电容要尽量靠近引脚走线要短。另外在 PCB 的电源入口处放一个 10uF 或者 22uF 的钽电容或者陶瓷电容用来吸收瞬态电流。我见过一个案例板子上的 100nF 去耦电容全部没焊裸机 LED 闪烁看起来没问题但一跑 FreeRTOS系统随机死机死机间隔从几秒到几小时不等。最后排查来排查去发现就是去耦电容的问题。加上电容之后同样的代码再也没死过机。还有一个非常关键的点检查复位电路。STM32F103 的 NRST 引脚是低电平复位正常工作时要保持高电平。如果 NRST 引脚被拉低芯片就会一直处于复位状态表现就是程序烧不进去或者烧进去也不跑。常见的错误是复位电容漏焊、复位电阻虚焊、或者复位按键的引脚短路。3.2 时钟电路芯片的“心脏起搏器”STM32F103 可以使用内部 RC 振荡器HSI8MHz或者外部晶振HSE作为系统时钟源。如果你用的是外部晶振晶振电路有问题芯片就会启动失败或者运行不稳定。外部晶振电路通常包括一个 8MHz 晶振、两个 20pF 左右的负载电容可能还有一个 1MΩ 的并联电阻。晶振的两个引脚分别接到 OSC_IN 和 OSC_OUT即 PD0 和 PD1负载电容分别从晶振的两端接到地。排查晶振电路的时候用示波器量 OSC_OUT 引脚应该能看到一个稳定的 8MHz 正弦波或者方波。如果没有波形先检查晶振焊接这是最常见的问题。晶振的两个引脚非常近很容易连锡如果连锡了晶振就不会起振。还有一个很多人不知道的坑晶振的负载电容选错了也会导致问题。不同规格的晶振需要的负载电容是不一样的常见的是 20pF但也有 12pF、18pF 的。如果你用的是 12pF 的晶振却在外面加了两个 20pF 的电容晶振的振荡幅度会变低导致系统运行不稳定。这种不稳定在裸机下可能只是定时不太准但在 FreeRTOS 下就可能表现为任务调度异常。另外提醒一句如果实在找不到问题可以试试把系统时钟切换到内部 HSI。在 system_stm32f10x.c 里把时钟源改成 HSI看看能不能跑起来。如果切到 HSI 之后就正常了那问题基本锁定在外部晶振电路上。3.3 启动模式和 BOOT 引脚STM32F103 的 BOOT0 和 BOOT1 引脚决定了芯片的启动方式。这个不多说直接看表BOOT0BOOT1启动模式0X从主 Flash 启动正常模式10从系统存储器启动用于串口下载11从内置 SRAM 启动用于调试正常运行时BOOT0 必须接低电平通过 10kΩ 电阻接地。如果 BOOT0 悬空或者被拉高芯片就不会从 Flash 启动结果就是你烧进去的程序根本不会执行。BOOT1 在正常模式下无所谓但在系统存储器启动模式下会被检查。我建议 BOOT1 也通过电阻接地避免悬空带来的不确定状态。3.4 下载接口排查程序烧不进去也是“芯片没反应”的一种常见形式。如果你用的是 ST-Link检查 SWDIO 和 SWCLK 两根线有没有接反SWDIO 接 PA13SWCLK 接 PA14。很多朋友在这两根线上栽过跟头颜色不对接反了折腾半天才发现。还需要注意如果你的板子上 SWDIO 和 SWCLK 引脚被复用做了别的功能下载器就连接不上了。在 FreeRTOS 项目中有时候为了省引脚会把 PA13/PA14 复用为 GPIO 或者别的外设烧录一次之后第二次就连接不上了。解决办法是在下载器的设置里选择“Connect under Reset”模式也就是在复位期间连接芯片绕过用户程序的引脚配置。4. FreeRTOS 在 STM32F103 上的移植与适配如果你的芯片确认是正品最小系统也排查过没有问题程序还是跑不起来那问题可能出在 FreeRTOS 移植和配置上。这一节我详细讲讲 STM32F103 上移植 FreeRTOS 的关键点。4.1 移植前的准备工作拿到正确的 FreeRTOS 源码很多朋友移植 FreeRTOS 失败不是因为操作有问题而是因为源码版本不对。我建议直接去 FreeRTOS 官网下载最新的 LTS 版本比如 V10.4.6 或 V10.5.1不要用网上那些来路不明的“精简版”或者“汉化版”。FreeRTOS 的源码包解压之后你会看到这样一个目录结构FreeRTOS/ ├── Source/ │ ├── include/ # 头文件 │ ├── portable/ # 移植层 │ ├── tasks.c # 任务管理 │ ├── queue.c # 队列 │ ├── list.c # 链表 │ ├── timers.c # 软件定时器 │ ├── event_groups.c # 事件组 │ ├── croutine.c # 协程一般不用 │ └── stream_buffer.c # 流缓冲 └── Demo/ └── CORTEX_STM32F103_Keil/ # 官方示例工程官方 Demo 工程是针对 STM32F103 的里面已经配置好了 SysTick、PendSV、SVC 的处理你完全可以基于这个工程改。但要注意官方 Demo 用的是标准外设库StdPeriph_Lib如果你用的是 HAL 库需要做一些适配。4.2 核心配置FreeRTOSConfig.h 的深度解析FreeRTOSConfig.h 是 FreeRTOS 移植的核心配置文件你的系统能不能跑起来、跑得稳不稳定很大程度取决于这个文件配得对不对。我挑几个对 STM32F103 来说特别重要的配置项详细说一下。首先是 configCPU_CLOCK_HZ。这个值必须和你的系统时钟频率一致。STM32F103 最高可以跑到 72MHz如果你的外部晶振是 8MHz经过 PLL 倍频到 72MHz那这个值就应该填 72MHz。如果你用的是内部 HSI 8MHz没有倍频那这个值就是 8MHz。这个值填错了FreeRTOS 的节拍就会不准任务调度的频率就会不对。其次是 configTICK_RATE_HZ。这是系统节拍频率一般填 1000也就是 1ms 一个 tick。也有人填 100也就是 10ms 一个 tick。节拍频率越高系统响应越快但 CPU 的开销也越大。对 STM32F103 这种主频不高的 MCU我建议用 1000Hz这是性能和响应速度之间比较平衡的选择。然后是 configTOTAL_HEAP_SIZE。这是 FreeRTOS 管理的内存总大小。STM32F103C8T6 只有 20KB 的 RAM抛去全局变量和堆栈能给 FreeRTOS 用的 heap 其实有限。我的经验值是 8KB 到 12KB。你可以根据实际需要调整但注意不要超过芯片的物理 RAM 大小 - 全局变量占用 - 中断堆栈占用。如果这个值设大了芯片上电就会 HardFault设小了任务创建会失败。最后注意configPRIO_BITS和configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY。这两个值对 STM32 来说必须正确设置。STM32F103 使用 4 位中断优先级也就是 configPRIO_BITS 填 4。configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 表示可以被 FreeRTOS 中断屏蔽的最高优先级一般填 5。这两个值如果配置不对会导致中断里调用 FreeRTOS API 时系统跑飞。4.3 SysTick、PendSV 和 SVC 的正确设置FreeRTOS 在 Cortex-M3 内核上运行依赖三个特殊的中断SysTick、PendSV 和 SVC。SVC系统服务调用用于启动第一个任务。FreeRTOS 在 vTaskStartScheduler() 中通过触发 SVC 来启动调度器。PendSV可挂起的系统调用用于上下文切换。FreeRTOS 在 SysTick 中断里判断是否需要切换任务如果需要就触发 PendSV在 PendSV 里完成上下文切换。SysTick用于产生系统节拍周期性触发驱动任务调度。这三个中断里优先级设置是最关键的。在 Cortex-M3 上FreeRTOS 要求 SVC 和 SysTick 可以被打断而 PendSV 必须是最低优先级。具体来说PendSV 的优先级必须设为最低数值最大这样才能保证在 PendSV 执行过程中如果有其他中断到来可以打断 PendSV等中断处理完再继续切换任务。如果把 PendSV 的优先级设高了在任务切换过程中来了其他中断上下文切换就会被打断可能造成系统崩溃。官方推荐的做法是在 FreeRTOSConfig.h 里定义#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5然后在启动文件里或者系统初始化代码里设置NVIC_SetPriority(PendSV_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY); NVIC_SetPriority(SysTick_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY);注意SysTick 的优先级低于数值大于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY也就是 5这样在 FreeRTOS 的临界区里SysTick 中断可以被屏蔽保证临界区的原子性。4.4 标准外设库和 HAL 库的选择问题STM32F103 的开发库有两种主流选择标准外设库StdPeriph_Lib V3.5和 HAL 库。这两种库在 FreeRTOS 移植上有个小小的差异我得提醒你一下。标准外设库的 SysTick 是直接操作寄存器的冲突不大。但 HAL 库自己在底层也会用 SysTick比如 HAL_Delay 函数如果你同时用 HAL 库的 HAL_Delay 和 FreeRTOS 的 vTaskDelay就会产生冲突因为 SysTick 只能有一个中断处理函数。解决办法有几个不要在 FreeRTOS 任务里调用 HAL_Delay全部用 vTaskDelay 代替。如果非要用 HAL_Delay需要在 FreeRTOS 的 SysTick 处理函数里或者单独开一个周期任务周期性地调用 HAL_IncTick() 来维护 uwTick 计数器。或者干脆不用 HAL_Delay自己写一个基于 DWT 或者 TIM 的毫秒延时函数。我自己的习惯是在 FreeRTOS 项目里禁用 HAL 的 SysTick 相关功能所有延时都走 FreeRTOS 的 API。这样代码风格统一不容易出问题。4.5 heap 方案的选择 heap_1 到 heap_5 怎么选FreeRTOS 提供了 5 种内存管理方案从 heap_1.c 到 heap_5.c它们在功能、性能和内存利用率上各有侧重。很多朋友在移植的时候随便选一个结果系统运行一段时间后崩溃了其实就是内存管理方案选错了。我把这五种方案的适用场景整理成了表格方便你对照选择方案内存分配内存释放适用场景heap_1支持不支持最简方案只创建任务不删除任务heap_2支持支持有删除任务需求但不需要内存碎片整理heap_3支持支持包装了标准库的 malloc/free需要开启 C 库堆heap_4支持支持带内存碎片合并最常用推荐首选heap_5支持支持支持非连续内存区域适合多 RAM 分区场景对 STM32F103 来说我推荐用heap_4.c。这是最稳妥的选择它会把多个空闲块合并成一个大的空闲块有效减少内存碎片。很多人踩过的坑是用了 heap_2任务反复创建删除之后内存碎片越来越多最后创建任务直接失败返回 NULL系统就崩了。另外需要注意heap_4.c 的首次分配会把整个堆分成几个固定大小的块如果任务栈大小种类太多反而会浪费内存。我的经验是尽量把任务的栈大小设成相同或者相近的值这样可以提高内存利用率。4.6 堆栈溢出检测怎么定位任务栈不够用FreeRTOS 有一个非常好用的功能堆栈溢出检测。开启之后系统会发现任务栈溢出并调用钩子函数帮你定位是哪个任务的栈不够用。很多朋友不知道这个功能任务栈不够用了也不自知系统跑飞了都不知道为什么。开启堆栈溢出检测的方法是在 FreeRTOSConfig.h 里#define configCHECK_FOR_STACK_OVERFLOW 2这个值可以设成 1 或者 2区别如下设为 1只在任务切换时检查栈指针是否越界速度快但可能检测不到某些溢出情况。设为 2除了检查栈指针还会检查栈的“金丝雀”值是否被覆盖。速度稍慢但可靠性更高。当检测到栈溢出时FreeRTOS 会调用这个钩子函数void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { // 在这里处理栈溢出一般做法是点亮一个错误 LED或者打印出错任务的名字 }我建议把 configCHECK_FOR_STACK_OVERFLOW 设为 2并且在钩子函数里把出错的任务名打印出来。这样你就能知道是哪个任务的栈不够用针对性加大栈大小就够了。4.7 常见 FreeRTOS 死机原因排查清单我自己在 STM32F103 上跑 FreeRTOS 踩过不少坑总结了一份死机排查清单。如果你严格按照前面的步骤做了还是死机按照这份清单逐项排查第一个要查的是中断优先级分组。STM32F103 需要调用 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4) 来设置中断优先级分组。FreeRTOS 的移植代码默认是 Group 4也就是 4 位全部用于抢占优先级。如果你用了其他分组FreeRTOS 的临界区保护就会失效。第二个要查的是 FreeRTOS API 是否在中断中使用。中断里可以使用 FreeRTOS API但只能使用带 FromISR 后缀的版本比如 xQueueSendFromISR 而不是 xQueueSend。如果搞混了系统会崩溃。另外中断的优先级必须不低于 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 的值否则 FreeRTOS 无法管理这个中断。第三个要查的是任务栈的大小。默认的任务栈大小是 configMINIMAL_STACK_SIZE一般设成 128以字为单位也就是 512 字节。如果你的任务里有 printf、sprintf 这种会占用大量栈空间的函数任务栈就要相应加大。我建议把任务栈设成 256 或者 512反正在 STM32F103 上 RAM 够用。第四个要查的是是否使用了浮点运算。STM32F103 的 Cortex-M3 内核没有硬件浮点单元FPU浮点运算全走软件模拟会占用大量栈空间。如果你的任务里有浮点运算任务栈至少加到 512 以上否则很容易栈溢出。第五个要查的是中断处理函数里是否调用了 FreeRTOS API。在 SysTick、PendSV 这些 FreeRTOS 自己管理的中断里不要调用任何 FreeRTOS API。用信号量或者消息队列的时候注意要使用专用版本。这个很多人不注意代码写得飞起然后就死机了。5. 实战案例从“芯片没反应”到“跑得稳如老狗”前面讲了不少理论这一节我分享一个完整的实战案例把整个排查思路串起来。这是我在一个真实项目里遇到的非常有代表性。5.1 案例背景新板子不上电FreeRTOS 直接躺平当时我在做一个基于 STM32F103C8T6 的数据采集模块硬件上是 MCU 加 RS232 串口芯片加几个传感器。软件架构是 FreeRTOS 加 MODBUS RTU 从站协议一共有四个任务一个 MODBUS 报文处理任务、一个传感器采集任务、一个数据上报任务、一个看门狗喂狗任务。板子打样回来后第一批焊了五块。上电测试五块板子全部“没反应”——晶振不振、串口无输出、LED 不闪。用 ST-Link 连接提示 No target connected。当时整个项目组都懵了代码在开发板上跑得好好的到了自己画的板子上就全部罢工。5.2 排查过程从硬件到芯片抽丝剥茧第一个怀疑对象是供电。用万用表量 3.3V 电源电压正常。示波器看电源纹波稍微有点大但还不至于让芯片罢工。第二个怀疑对象是晶振。用示波器量 OSC_OUT 引脚没有波形。检查晶振焊接发现有两块板子的晶振引脚连锡了。重新焊接之后两块板子恢复了正常。剩下的三块板子依然没反应。这时候开始怀疑芯片本身。用 ST-Link 连接还是 No target connected。量 NRST 引脚电压发现只有 1.2V正常应该是 3.3V。查复位电路发现复位电容虚焊NRST 被拉低了。修好复位电容之后两块板子恢复正常。最后剩一块板子无论如何都连不上。用热风枪吹下芯片发现这块芯片的 VDD 和 GND 引脚之间短路。换了一块新芯片之后板子正常工作了。这个案例说明了什么说明“芯片没反应”这个问题绝大多数情况下不是 FreeRTOS 的锅而是纯粹的基础硬件问题。你连最小系统都没搞定就别急着折腾 FreeRTOS。5.3 第二波问题稳定运行两天后随机死机板子能跑起来之后新的问题又来了。设备在实验室跑了大概两天出现了随机死机。死机没有任何规律有时候是几小时有时候是一天多。死机之后看门狗也不生效只能断电重启。这个问题的排查过程比较曲折。先是在每个任务里加了运行计数用串口周期性打印发现死机前最后一个打印的任务是不确定的有时是 MODBUS 任务有时是传感器采集任务说明不是某个特定任务的问题。然后用 FreeRTOS 的堆栈溢出检测功能没有报错。又从症状上看死机像是不可控的量电源也没发现异常。这时候我开始怀疑是不是芯片本身的问题。把这批芯片的丝印拍下来发给渠道商核实对方回复说这批货是某国产替代芯片。我把芯片换成正经渠道的 STM32F103C8T6同一个固件烧进去连续运行了一个月没有死机。后来又跑了几批都没有问题。5.4 案例复盘经验教训总结这个项目给我最大的教训是在做任何复杂软件包括 FreeRTOS之前先把硬件平台验证扎实。具体来说有三件事值得落实到你的日常开发流程里第一采购芯片要留好溯源。每批芯片买回来记录好渠道、批次、购买日期。出了问题可以快速定位是不是这批芯片的锅。第二新板子回来先烧一个硬件自检固件。这个固件不跑 RTOS仅仅是依次初始化各个外设把测试结果通过串口打印出来。这样能把“硬件问题”和“软件问题”快速分开。第三不要一上来就撸 FreeRTOS。先跑裸机把所有外设调通再移植 FreeRTOS。分步验证哪一步出问题就针对哪一步排查不要一堆问题搅在一起。6. 工具与技巧让排查效率翻倍的几个利器排查问题靠经验但好的工具能让你事半功倍。这一节我分享几个我常用的工具和技巧。6.1 STM32CubeProgrammer连接芯片的救命稻草当 ST-Link 连接不上芯片的时候STM32CubeProgrammer 是你的第一选择。这个工具是 ST 官方出的免费支持全系列 STM32。使用方法是在 STM32CubeProgrammer 里选择正确的连接方式ST-Link 或者 J-Link然后点击 Connect。如果连接成功会显示芯片的基本信息包括 Device ID、Flash 大小、RAM 大小等。如果连接不上ST-Link 又提示 No target connected你可以试试“Connect under Reset”模式。这个模式会在芯片复位期间强行连接绕过用户程序对调试引脚的复用配置。STM32CubeProgrammer 的界面里有一个 Reset mode 下拉框选 Hardware Reset 或者 Connect under Reset 就可以。6.2 串口打印最简单的调试手段在嵌入式开发里串口打印依然是最简单、最直接的调试手段。我建议你在每个 FreeRTOS 任务里都加一个运行计数器周期性通过串口打印出来。这样你能实时看到每个任务的运行状态定位是哪个任务出了问题。串口打印要注意几个坑STM32F103 的 USART1 默认引脚是 PA9TX和 PA10RX使用前要确认你的板子是不是接的这两个引脚。USART 的波特率要正确设置常用的是 115200 或者 9600。波特率不对打印出来的就是乱码。如果用了 FreeRTOS串口打印最好通过队列加任务的方式异步处理不要在中断里直接打印否则可能会阻塞系统。6.3 Keil 调试技巧HardFault 定位的黄金法则当 FreeRTOS 跑飞触发 HardFault 的时候很多人一脸懵不知道从哪查起。其实用 Keil 的调试功能定位 HardFault 并不难。HardFault 触发后程序会跳转到 HardFault_Handler 中断服务函数。你在 Keil 里打开这个函数所在的文件startup_stm32f10x_md.s 或者 stm32f1xx_it.c在 HardFault_Handler 里打一个断点。程序停在断点之后打开 Keil 的寄存器窗口View → Registers Window查看以下几个寄存器的值R14 (LR)异常返回地址可以推算出触发 HardFault 之前的程序地址。R0-R3函数调用时的参数可以帮助定位是哪个函数出了问题。PC当前程序计数器的值直接告诉你卡在哪条指令上。如果你的程序是 Debug 模式编译的直接在 Call Stack 窗口看函数调用栈就能直接看到是哪个函数调用链触发了 HardFault。6.4 Map 文件看透内存布局很多时候程序跑飞是因为内存不够用。这时候打开编译生成的 map 文件一目了然。Keil 生成的 map 文件在输出目录下文件名和工程名一致后缀是 .map。打开之后重点看这几块内容Section Cross References看哪些函数被哪些地方调用了帮助你理解代码执行路径。Memory Map of the image看各个段代码段、数据段、堆栈段在内存中的分布情况。Image Symbol Table看各个符号函数、变量的地址和大小帮你确认内存使用情况。如果发现某段代码或者数据的大小异常可能就是问题所在。6.5 示波器和逻辑分析仪硬件问题的一次性解决排查硬件问题示波器是必须的。我建议你在做 STM32 项目时至少准备一台带宽 100MHz 以上的示波器。用在以下几个方面测量电源纹波。上电瞬间和任务切换瞬间电源电压是否有跌落。测量晶振波形。确认外部晶振是否起振频率是否正确。测量串口波形。确认串口发送的数据是否正确。测量 GPIO 波形。调试 PWM、呼吸灯、WS2812B 等输出信号时直接量引脚波形最直观。如果没有示波器逻辑分析仪是个不错的替代方案。逻辑分析仪的价格比示波器便宜很多虽然不能看模拟波形但看数字信号UART、SPI、I2C、GPIO 翻转完全够用。7. 进阶话题STM32F103 上跑 FreeRTOS 的优化建议最后这一节我聊聊进阶的话题。如果你的 FreeRTOS 已经跑起来了但性能不够理想或者想做得更稳可以参考下面这些优化建议。7.1 任务优先级的合理分配FreeRTOS 的调度规则是高优先级任务优先运行只有高优先级任务阻塞或者被删除低优先级任务才有机会运行。如果高优先级任务不阻塞低优先级任务就会一直饿死永远得不到执行。所以你在分配任务优先级的时候要注意实时性要求高的任务比如通信报文处理、按键扫描优先级要高。实时性要求低的任务比如传感器数据上报、显示刷新优先级要低。不要让高优先级任务长时间运行应该用信号量、消息队列、事件组等机制让任务主动让出 CPU。空闲任务的优先级最低Idle 钩子函数里不要做耗时操作。7.2 使用消息队列解耦任务任务之间通信我强烈推荐使用消息队列。消息队列可以解耦任务的依赖关系让每个任务只负责自己的事数据通过队列传递。比如传感器采集任务负责读数据读完之后通过队列发送给数据上报任务数据上报任务从队列里取数据组包上报。这样两个任务互不干扰即使传感器采集任务偶尔卡顿数据上报任务也不会受影响。使用消息队列要注意队列大小的设置。队列太小数据会丢失队列太大浪费 RAM。我的经验是把队列大小设成缓冲区最多能缓存的数据条数一般是任务周期和数据处理速度的比值。7.3 软件定时器代替忙等延时在 FreeRTOS 中如果在任务里用 vTaskDelay 做延时任务会进入阻塞状态让出 CPU 给其他任务。这个没有问题。但如果你在任务里用 for 循环做忙等延时就会白白浪费 CPU其他低优先级任务都跑不了。对于周期性执行的任务推荐使用 FreeRTOS 的软件定时器。软件定时器回调函数是 LOW 优先级任务上下文执行的不会被阻塞适合执行周期性的轻量级操作。重操作还是建议用独立的任务阻塞在队列或者信号量上等事件来触发。7.4 低功耗模式与 FreeRTOS 的结合如果你的产品对功耗有要求可以考虑在 FreeRTOS 空闲任务的钩子函数里进入低功耗模式。这是 FreeRTOS 官方推荐的 tickless 模式。开启 tickless 模式需要在 FreeRTOSConfig.h 里定义 configUSE_TICKLESS_IDLE 为 1。实现 vPortSuppressTicksAndSleep() 函数。这个函数在进入低功耗前会被调用你需要在这个函数里配置低功耗模式并处理 tick 补偿。注意tickless 模式下 SysTick 可能会被停止所以基于 SysTick 的时间相关功能会受到影响。对于 STM32F103常用的低功耗模式是 STOP 模式。进入 STOP 模式后的唤醒方式可以是外部中断或者 RTC 闹钟。7.5 从 F103 升级到 H743 的注意事项最后提一个有意思的话题很多朋友做完 STM32F103 的项目想升级到更高性能的 STM32H743。F103 的代码要移植到 H743 上有几个关键差异要注意内核从 Cortex-M3 变成 Cortex-M7。M7 内核的主频更高H743 最高 480MHz而且有硬件 FPU 和 DSP 指令集。库从标准外设库变成 HAL 库。F103 的 StdPeriph_Lib 到 H743 已经不支持了只能用 HAL 库。时钟树完全不一样。H743 的时钟树比 F103 复杂得多用 CubeMX 配置最好。中断优先级位数从 4 位变成 8 位。FreeRTOS 的配置需要对应修改 configPRIO_BITS。Flash 访问延迟。H743 在 480MHz 下需要配置 Flash 的等待周期数否则程序运行不稳定。如果你有从 F103 升级到 H743 的打算先用 CubeMX 生成一个最小的 FreeRTOS 工程点灯跑通之后再逐步迁移你的应用代码。千万不要指望一键迁移那是不可能的。最后分享一个我自己的习惯做 STM32 这几年我养成了一个小习惯每买一批新芯片先用几颗做一轮全外设压力测试确认没问题再批量使用。测试固件里包括 GPIO 翻转、串口收发、定时器中断、ADC 采集、Flash 读写全部通过之后这批芯片才算“合格”。这个习惯帮我省了不知道多少排查问题的功夫。因为一旦你确认芯片本身没有问题后面遇到的所有 Bug 都可以放心大胆地往代码和电路上查而不会陷入“芯片到底行不行”的自我怀疑中。希望这篇文章能帮到被“芯片没反应”折磨的朋友。如果你在实际操作中遇到了更诡异的问题欢迎在评论区留言我们一起讨论排查思路。嵌入式开发就是这样坑踩得多了经验自然就丰富了。
返回列表