玩ESP32遇到这行日志“External RAM failed memory test!”,十有八九是板子刚焊好、PSRAM刚贴上去,或是换了一颗Flash/PSRAM之后上电就卡死在启动阶段。ESP32在启动流程里会自动检测并用缓存控制器测试外部SPI RAM,这个测试一旦不过,直接abort,整个系统起不来,连app都进不去。这篇文章就把这个报错的机制、排查思路、常见坑位和怎么快速定位问题一次性讲清楚。
我见过太多人第一反应是“是不是芯片坏了”,其实这个报错背后牵扯到的点很多——硬件焊接、供电、电平匹配、引脚冲突、IDF配置、甚至Flash的QIO模式都可能让memory test失败。而且它跟“PSRAM is not found”是两回事,前者是检测不到,后者是检测到了但读写数据对不上,两者排查方向完全不同。
1. 报错机制与问题全貌
1.1 这行日志到底是怎么触发的
ESP32的启动流程里,如果启用了外部SPI RAM支持,IDF的spiram初始化代码会在内存管理单元初始化之前先做一次基础读写测试。测试用的地址通常分布在低地址区,比如0x00000000、0x00000100、0x00008000这些位置,往里面写入特定的pattern再读回来比对。数据线、地址线、控制线只要有一根接触不良,或者时序边缘不满足,测试立马失败,然后打印你看到的:
E (74) spiram: External RAM failed memory test! abort() was called at PC 0x400d46a2 on core 0注意,走到这一步意味着PSRAM已经被“找到”了。如果你看到的是类似“PSRAM is not found”的日志,那是另一条分支,说明ID根本读不回来,大概率是CS、SCK、供电或者ID命令通路的问题。而“failed memory test”这个报错说明IDF已经和PSRAM建立了初步通信,甚至可能已经把ID读回来了,只是在做实际数据读写时发现数据线和既定时序对不上。
搞清楚这一点太重要了,因为从“not found”到“failed test”之间隔着完全不同的排查路径。
1.2 为什么ESP32非要在开机时测一遍RAM
很多人不理解,为啥不直接用,非要测一下?原因其实很简单:PSRAM是挂在外置缓存控制器上的,CPU访问外部RAM的地址区域时,走的是cache映射。如果PSRAM根本没工作,CPU一旦往映射的地址区域读数据,得回来的全是垃圾,程序直接跑飞,而且这种跑飞极其难排查,连个报错都看不到。
所以IDF的做法很保守:在早期初始化阶段先主动读写几个测试点,能过就继续往下走,不能过就直接abort。这跟PC的内存自检是一个道理,宁可开机慢一点,也不能让系统带着坏内存跑。
如果你把这个测试关掉,系统是可以继续启动的,但后续你一旦真正用psram分配内存,崩溃概率极高,且问题会变得非常难查。所以我个人的观点是,开发阶段不要轻易绕过这个测试,宁可把硬件问题暴露在开机阶段,也不要让它在运行期变成玄学bug。
1.3 名字上的一个误区:SPI SRAM和PSRAM的关系
标题里写的是“SPI SRAM”,其实ESP32外扩的这类RAM芯片准确叫PSRAM(Pseudo SRAM),也就是伪静态随机存储器。它外部接口是SPI,使用时不需要像普通DRAM那样由外部控制器频繁刷新,内部自带刷新逻辑,所以接口看起来跟SRAM一样简单。这也是为什么很多人叫它SPI SRAM,但数据手册上大概率写的是PSRAM或者QSPI PSRAM。
这个区别会影响你的排查思路吗?会。普通SRAM是掉电即失、随时读写、几乎不需要时序配置;而PSRAM芯片内部有模式寄存器,需要上电后通过SPI命令完成配置,比如进入Quad模式、设置延迟参数等。如果这些配置没做对,memory test照样失败。好在IDF把这些都封装好了,正常情况下只要你选对芯片类型和运行模式,不需要自己操心寄存器配置。
2. 硬件连接与供电:最容易翻车的环节
2.1 虚焊和飞线长度是头号杀手
我排查过太多memory test失败的案子,排第一的原因永远是焊接。PSRAM芯片很多是WSON封装或者BGA封装,手焊难度不低。尤其是底下有散热焊盘的,烙铁温度不够或者锡膏没充分熔化,某个引脚虚焊了,表面上看起来芯片贴得挺好的,实际上一根线就没接通。
怎么确认是不是虚焊?我的习惯是用放大镜逐脚检查,然后用万用表二极管档位测每个引脚到测试点之间的导通性,重点是CS、SCK、SIO0~SIO3这几根线。不要只看表象,一定要量到具体的引脚末端。
另外很多人喜欢用杜邦线把PSRAM模组飞在ESP32外面做验证,这在一二十厘米的短线范围内一般能跑起来,但如果你把线拉到30厘米以上,80MHz的SPI时钟下信号完整性基本就崩了。信号反射、串扰、地线弹跳都会导致读回来的数据不对,而这个不对在memory test阶段就会爆发出来。
我的建议是,飞线验证时先降到40MHz跑测试,能过再逐步往上提。如果40MHz能过但80MHz过不了,九成是走线寄生电容太大或者电源地线回路太长,而不是芯片本身的问题。
2.2 1.8V和3.3V的电平匹配问题
这个坑特别隐蔽,尤其是自己做板子接裸PSRAM芯片的时候。很多PSRAM芯片,比如常见的ESP-PSRAM64H,IO电压是1.8V,而ESP32的GPIO是3.3V电平。直接把3.3V的信号怼到1.8V芯片的引脚上,短时间不会烧,但芯片可能无法正确识别高电平,导致ID读回来是乱的,数据写进去读出来也是乱的。
那为什么有些模块直接飞线连接也能跑?因为很多现成的PSRAM模块板子上已经自带了电平转换电路,或者选用的PSRAM本身就是宽电压版本,支持3.3V输入。这并不代表所有芯片都可以这么干,一定要看数据手册的VDD和IO电压范围。
如果你做的是量产板或者自己设计的载板,电平转换不能省。用74LVC2T45这类双向电平转换芯片,或者直接选用支持3.3V的PSRAM型号,都是成熟方案。在这个地方省成本,后面调试的时间成本会成倍增加。
2.3 引脚冲突和片选信号浮空
ESP32的PSRAM数据线一般占用GPIO12~GPIO17(具体以模组原理图为准),这组IO同时也是JTAG相关的引脚,比如GPIO12是MTDI,GPIO15是MTDO。如果板子上还有别的外设占了这几个引脚,或者你飞线的时候不小心跟其他信号搭在一起了,PSRAM通信就会被干扰。
片选浮空的问题也很常见。有些设计里CS没有接上拉电阻,上电瞬间PSRAM可能会被误选中,这时候其他SPI设备正在通信,数据线冲突,轻则丢数据,重则烧毁IO。正确的做法是CS引脚加一个10k左右的上拉电阻到VDD,保证上电默认无效电平。这个细节PCB上不明显,但排查时能省很多事。
3. 软件配置与调试流程
3.1 menuconfig里那些关键的SPI RAM配置项
如果硬件连接看着没问题,接下来就要检查IDF的配置了。我这个流程只针对ESP32经典款,ESP32-S3的配置位置略有不同,但思路一样。
打开配置菜单:
idf.py menuconfig然后按路径找到:
- Component config → ESP32-specific → Support for external, SPI-connected RAM:这个总开关必须先打开
- Component config → ESP32-specific → SPI RAM config → Mode (QUAD/OCT):根据芯片实际支持的模式选
- Component config → ESP32-specific → SPI RAM config → Set RAM clock speed:支持80MHz/40MHz等选项
- Component config → ESP32-specific → SPI RAM config → Enable SPI RAM boot initialization:这个控制是否在启动时初始化并测试PSRAM
这三个选项是最容易出错的地方。如果你用的是Quad PSRAM但配置里选成了Octal,或者反过来,初始化序列完全不同,测试直接失败。同样地,速率选太高也会失败,尤其在你飞线或者PCB走线质量一般的情况下。
我建议先用最保守的配置跑一遍:Quad模式、40MHz、关闭Octal,能过了再往上优化。别一上来就追求80MHz,先把链路跑通,后面再调教性能完全不迟。
3.2 日志里隐藏的关键信息
配置正确后重新编译烧录,观察串口日志。正常的流程大概长这样:
I (60) spiram: SPI RAM mode: flash quad, psram quad I (67) spiram: PSRAM ID: 0x5d5d I (70) spiram: PSRAM initialized I (72) spiram: Testing SRAM...注意中间这行“PSRAM ID: 0x5d5d”。这个ID非常重要。不同PSRAM芯片有固定的ID值,全0xFF说明芯片没响应,全0x00可能是供电或者数据线短路,读出一个奇奇怪怪的值可能是时序问题或者ID命令不兼容。
如果ID读出来了但“Testing SRAM...”之后还是报“failed memory test”,说明问题大概率在数据线上——SIO0到SIO3这四根线里至少有一根接触不良,或者芯片型号比较特殊、IDF内置的初始化序列不完全兼容,需要换一颗已知兼容的PSRAM交叉验证。
3.3 一套标准的排查操作流程
我每次遇到这个报错,都会按一套固定流程走,从最可能到最不可能的逐个排除:
- 先量电压。确认PSRAM VDD引脚上有正确的供电,且纹波不大。用示波器看下上电瞬间的电平爬升是否干净,如果有一个缓慢的斜坡,可能是电源时序问题。
- 核对原理图。确认CS、SCK、SIO0~SIO3这六根线(加上VDD和GND一共八根)一一对应无误,没有接反或接错位。
- 用万用表量所有线到芯片引脚的导通性,排除虚焊和断线。
- 检查周围是否有外设占用PSRAM引脚,必要时临时把其他外设摘下来,只留PSRAM做最小系统测试。
- 把配置降到最保守:Quad模式、40MHz、关掉任何和PSRAM相关的自动优化选项。
- 看日志是否出现PSRAM ID,判断通信链路是否建立起基本连接。
- 如果ID正常但test失败,用逻辑分析仪同时抓CS、SCK和四根数据线,对比IDF发出的初始化时序与芯片数据手册要求的是否一致。
这套流程走完,至少能覆盖九成以上的问题场景。
3.4 用逻辑分析仪抓时序的实战技巧
逻辑分析仪在这种问题里能发挥奇效,但前提是你会看时序。先把采样率设到芯片时钟的4倍以上,比如跑40MHz就至少用200MHz采样率的设备,不然抓出来的波形跟实际完全对不上。
触发条件设CS下降沿,抓一个完整的ID读取命令。正常的流程是:CS拉低、SCK开始出时钟、SIO0上输出命令字节、然后芯片在后续时钟沿上把ID从数据线上推出来。如果你能看到时钟但数据线上完全没响应,基本可以断定芯片没收到有效命令或者命令格式不对。
有一种情况比较坑:SPI总线上同时挂了Flash和PSRAM,Flash跑QIO模式,数据线和PSRAM存在复用,这时候抓波形时会发现数据线上有其他外设的信号串扰进来,导致PSRAM读出来的ID时对时错。解决方法是临时把Flash改成DIO模式或者用ESP-IDF的配置让两者分时复用更干净,优先排除总线竞争。
4. 常见问题速查与修复实录
4.1 故障现象快速对照表
我把平时遇到过的真实故障按“现象 → 原因 → 处理方式”整理成了一张表,排查时可以直接对照:
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
| 日志显示PSRAM not found | CS或SCK没接通,VDD无供电 | 量电压、量导通性,补焊 |
| PSRAM ID读到全0xFF | 芯片未响应或SIO0断线 | 检查SIO0通路和CS时序 |
| PSRAM ID读到全0x00 | 数据线对地短路或芯片损坏 | 断开数据线逐个测通断 |
| ID正确但memory test失败 | SIO1~SIO3其中一根有问题 | 逐根测量,重点查虚焊 |
| 40MHz通过但80MHz失败 | 走线过长、寄生电容大 | 缩短飞线、优化PCB走线 |
| 时好时坏,重启后偶发失败 | 焊接不良、接触点氧化、上电时序不稳定 | 补焊、加去耦电容、检查电源上升时间 |
| 原本正常,升级IDF后失败 | 配置被重置或驱动变更 | 查看git diff,重新核对PSRAM配置 |
这张表虽然简单,但每一条背后都是真金白银踩过的坑。
4.2 一个自研板的虚焊案例
有一次帮朋友排查一块自研的ESP32板子,现象就是这个报错,而且非常稳定,每次都是同一行日志。我拿着放大镜看PSRAM芯片的引脚,表面上看锡都融了,也没有桥接,但用万用表一量,发现SIO2这根线的过孔到芯片引脚之间其实是断开的,只靠焊锡的氧化层挨着,接触电阻时大时小。补焊那一个点之后,测试立马通过。
从那之后我养成一个习惯,凡是新板子第一次上电跑PSRAM测试失败,不管现象多像时序问题,第一件事永远是拿万用表把这六根线从头到尾量一遍。很多时候问题就出在一根线上。
4.3 一个热风枪温度导致的坏片案例
另一个案子比较诡异,板子用的是QFN封装的PSRAM,手焊时用了热风枪,温度调得偏高,结果芯片内部封装产生了细微裂纹,导致ID能读出来但写入的数据总有几位会翻转。memory test失败得非常随机,有时候能跑过去,有时候跑不过去。最后用显微镜看芯片表面才看到一条几乎不可见的裂缝。
这个案例想说的是,memory test失败不一定是设计问题,也可能是芯片已经在焊接过程中损坏了。如果你把所有检查都做完了还是不稳定,果断换一颗芯片试试,不要在一颗疑似损坏的芯片上死磕到底。
4.4 应急手段:先绕过测试让系统跑起来
如果项目deadline近在眼前,硬件修复需要时间,但你急需验证其他功能,可以暂时绕过PSRAM的内存测试。具体做法是把menuconfig里的“Enable SPI RAM boot initialization”关掉,这样引导阶段不会去做测试,系统能正常启动,但你后续代码里不要分配psram内存,否则大概率崩溃。
这只是应急方案,不能作为长期解法。因为外部RAM不经过测试直接使用,等于在踩地雷,你不知道什么时候会踩到坏地址。正确姿势还是修好硬件或配置之后,再开启初始化测试。
5. 从失败到稳定运行的个人体会
排查“External RAM failed memory test!”这个报错,本质上是在和信号完整性、焊接质量、供电稳定性和配置正确性做斗争。我最大的体会是:先确认硬件物理链路完全没问题,再去动软件配置,这能避免大量的无效调试时间。
还有一个小技巧:拿到一块新板子时,不要一上来就烧正式固件。先烧一个最简单的配置,把PSRAM降到40MHz Quad模式跑一遍,确认这个最保守的链路能通,再去开高速度和复杂模式。这样等于把硬件的底线摸清楚了,后面每一步调优都有依据。
另外,开发阶段如果可以,尽量买带PSRAM的原厂模组做对照。用原厂模组跑同一份固件,如果原厂模组过而你自研板不过,问题基本锁定在硬件设计上;如果原厂模组也过不了,那就要回头检查你的配置是否选错了芯片类型或者模式。
最后再分享一个经验:千万别省示波器或者逻辑分析仪。memory test出问题时,多用仪器看几眼波形,比盲猜快得多。没有条件的话,就用最笨的办法——万用表一根线一根线量,再慢也比反复烧录固件试错来得快。这个报错虽然看着吓人,其实只要思路清晰、一步一步来,绝大多数问题都能在半小时内定位出来。