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

资讯详情

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

SPI EEPROM读回全FF?从硬件到软件一步步排查搞定

SPI EEPROM读回全FF?从硬件到软件一步步排查搞定 如果调试SPI EEPROM时发现读出来的数据全是0xFF恭喜你踩进了嵌入式开发里最经典也最让人抓狂的一个坑。最近帮朋友排查一个M95M04的故障现象就是标题这句话“Only receiving FF out of the M95M04”。芯片是ST的4Mbit SPI EEPROM读任何地址返回的都是0xFF写进去的数据再读回来也还是0xFF整个芯片像是失忆了一样。这篇把这个问题从现象、原理到排查步骤完整捋一遍文末附问题速查表做硬件和固件调试的朋友可以直接对照排查。M95M04这种SPI EEPROM在工业设备里很常见仪表校准参数、设备配置、日志存储都靠它。全FF意味着系统拿到的是空白数据轻则配置丢失需要恢复出厂重则设备直接判定硬件异常、拒绝启动。很多人第一反应是“芯片坏了”但我实际见过的情况里真坏的比例很低大部分是硬件连接、SPI配置、写保护引脚处理这几类问题。所以别急着换芯片按下面的思路一步步来。1. 现象定性全FF到底说明了什么1.1 先区分两种“全FF”同样是读到0xFF背后原因可能完全不同排查方向也南辕北辙。我习惯把它分成两种情况。第一种芯片从头到尾没有任何响应。你发指令、发地址MISO上一点反应都没有读出来全是0xFF。这种情况的根因基本在通信层——要么芯片没上电要么MOSI/MISO/SCK/CS四根线里有问题要么SPI模式完全不匹配。芯片就像一个没插电的收音机你调频没有任何声音。第二种芯片有响应但读出来的数据本身就是0xFF。这里要特别提醒全新EEPROM出厂数据本来就是全FF这在Flash和EEPROM领域是标准状态不是故障。但如果你是写完数据之后再读发现还是FF那就要怀疑写操作根本没生效——写保护没解除、写时序不对、页写越界都可能导致“写了个寂寞”。所以拿到全FF先别慌先问自己一个问题这个芯片之前写过数据吗如果确认写过什么时候写的写完之后有没有成功读回来过这几个问题的答案能把排查方向缩小一半。1.2 全FF在SPI协议层面的含义SPI是同步串行协议主机提供时钟从机在时钟边沿输出数据。当从机不驱动MISO线时这条线处于高阻态。如果主机端MISO内部有上拉电阻或者板子上有外部上拉那么高阻态被上拉到高电平读到的就是0xFF。这条逻辑很关键。全FF意味着SPI总线上每个bit都是1也就是MISO一直保持高电平。虽然可能是芯片工作正常但数据区为空但更常见的是芯片根本没参与总线对话。你可以把MISO想象成一条共享电话线多个设备挂在上面谁都不说话时线路是安静的被上拉到高电平只有被CS选中的设备才会在这条线上输出数据。你一直听到“嗡嗡”的静音噪声说明你要找的那个设备根本没拿起话筒。1.3 全FF问题的典型影响范围这类问题在项目里的影响有多大取决于芯片里存的是什么数据。如果M95M04存的是设备唯一标识或出厂校准数据全FF会让设备在开机自检阶段就判定数据非法直接拒绝运行。如果存的是用户配置可能表现为每次断电重启都恢复出厂设置。如果是OTA升级里的引导参数芯片数据损坏甚至会导致设备变砖。所以在产线调试、样机验证阶段全FF这种问题杀伤力很大但好消息是根因通常不复杂关键是别瞎猜按电路和时序一层一层查。2. M95M04基础从芯片特性反推问题点2.1 M95M04到底是什么M95M04是ST意法半导体出品的SPI接口EEPROM容量4Mbit换算过来是512KB。这个容量在EEPROM里算比较大的所以它内部需要2字节地址来寻址512KB需要19根地址线的寻址范围一个字节8位不够用。它的引脚不多典型8脚封装。CS是片选C是时钟对应SCKD是数据输入对应MOSIQ是数据输出对应MISOW是写保护HOLD是保持。还有电源和地。有些朋友第一次用这芯片把W和HOLD直接悬空结果就是问题不断。2.2 必须搞懂的关键指令M95M04的指令系统不复杂但几个核心指令必须背下来排查问题时刻都要用到0x06 WREN写使能。任何写操作之前必须先发这个指令这是EEPROM防误写的重要手段0x04 WRDI写禁用0x05 RDSR读状态寄存器0x01 WRSR写状态寄存器0x03 READ读数据后面跟2字节地址0x02 WRITE写数据后面跟2字节地址加数据0xB9 深掉电指令0xAB 唤醒指令特别要注意EEPROM的写操作比读操作麻烦得多。读操作随时可以做不需要先发WREN。但写操作前必须先发0x06写使能然后检查状态寄存器里的WEL位确认写使能成功再发写指令。而且写完后不能立刻写下一笔必须轮询WIP位等内部写周期完成。很多人第一次用EEPROM写完了立刻读发现数据没更新其实就是写周期还没结束。2.3 地址长度这个经典坑M95M04这种4Mbit的芯片地址是2字节。而M950404Kbit、M9516016Kbit这些老型号地址只要1字节。如果你把之前1字节地址的例程直接搬到M95M04上读出来的数据十有八九不对而且很可能是全FF。原因很简单芯片等你发两个地址字节你只发了一个它把第一个数据字节当成了地址的高字节后面所有数据都错位了读到的自然不是你想访问的地址内容。再加上如果你读的是0x0000这种低位地址高字节地址被误认为0x00时看起来还行但一旦地址超过255就全面错乱。排查全FF问题前先确认你的代码发的地址字节数对不对。正确写法是先发0x03再发两个地址字节比如读地址0x1234就发送 0x03, 0x12, 0x34然后开始读。3. 硬件排查从引脚到波形逐项确认3.1 供电、接地与连接硬件排查第一步不是看软件而是确认芯片“活”着。用万用表量芯片电源引脚对地电压。M95M04工作电压范围常见的是1.8V到5.5V具体看尾缀有-D后缀的多支持1.8V以上但有个细节电压不同最高时钟频率也不同。5V时能跑20MHz3.3V时可能就只有16MHz甚至更低。如果供电只有3.3V你却按20MHz配置SPI时钟芯片跟不上读出来的数据也有可能是乱的。量电压的同时顺便量一下CS、SCK、MOSI、MISO这四根线到主控引脚之间的通断。我之前遇到过一块板子目测焊接没问题但MISO过孔在PCB内层断裂万用表蜂鸣档一量就现原形。还有一次是排线接触不良SPI信号时通时断。注意SPI从机的MISO在未选中时是高阻态所以主机端MISO必须有上拉电阻。很多MCU的GPIO内部上拉默认是关闭的如果你用的是硬件SPI且没有外部上拉电阻MISO悬空时读到什么全靠运气最常见的就是读到全FF或者全00。解决方法是加一个10kΩ外部上拉或者在初始化GPIO时把内部上拉打开。3.2 WP和HOLD引脚——全FF的隐形元凶M95M04的WWP引脚是硬件写保护输入低电平有效。这个引脚的电平状态直接决定写操作能不能生效。如果WP被拉低芯片的写保护区域无法写入但读操作不受影响。注意这里有个迷惑性写保护时读操作完全正常你能读ID、能读状态寄存器、能读数据区只是写不进去。所以如果读出来全是FF而且这些FF是“写不进去”的FF先查WP。HOLD引脚同样低电平有效。HOLD拉低时芯片暂停通信忽略SCK上的时钟信号此时如果你正在读数据MISO会停在当前电平你读到的是重复的位也可能表现为数据异常。HOLD上有一个内部上拉电阻但如果你把它接到地芯片就永远处于暂停状态这时候SPI通信是完全不响应的读出来自然全是FF。实际项目里WP的正确接法有两种。一种是直接接VCC永久关闭硬件写保护靠软件指令保护数据另一种是接MCU的GPIO需要写的时候拉高平时拉低保护。新手最容易犯的错是把WP直接接地然后发现写不进任何数据。HOLD则是强烈建议接VCC或者用GPIO拉高不要让它悬空。虽然HOLD内部有上拉但悬空引脚在电磁干扰环境下电平可能抖动危险隐患很大。3.3 示波器看波形一锤定音的实测方法软件排查了半天还定位不到问题别犹豫上示波器。这是我实际排查SPI问题时最有效的手段甚至可以说学会用示波器看SPI波形能解决80%的SPI通信故障。先把示波器探头接在SCK上触发方式设置成上升沿或者下降沿然后运行读操作。先看时钟是否正常SCK是否有连续脉冲空闲电平是否正确如果配置的CPOL0空闲电平应该是低如果CPOL1空闲应该是高。接着看CS发指令时CS是否拉低拉低的时机是否正确CS必须在第一个时钟沿之前拉低并且在整个指令过程中保持低电平。然后看MOSI上的指令波形对照数据手册确认发送的0x03后面确实跟了两个地址字节。最后把探头移到MISO上在读数据阶段MISO上是否真的有电平变化如果MISO一路平坦高电平不动那么芯片确实没有回应你如果MISO有波形但读出来的数据和预期不符那就要查数据是不是在正确的时钟边沿被采样。示波器还能帮你确认SPI时钟频率是否过高。如果波形显示SCK频率太高边沿已经变形、幅度不足芯片很可能就无法正确采样。具体判断标准看数据手册但经验上建议先用1MHz这种低速排查问题通信正常了再逐步提高频率。4. 软件排查SPI配置与指令序列4.1 SPI模式必须匹配CPOL和CPHA硬件排查没问题的话问题大概率在软件配置上。SPI有四种模式由CPOL时钟极性和CPHA时钟相位组合而来。M95M04的数据手册明确支持模式0CPOL0, CPHA0和模式3CPOL1, CPHA1。这两个模式的区别是模式0是空闲时钟为低电平第一个边沿上升沿采样数据模式3是空闲时钟为高电平第二个边沿通常是下降沿采样。如果你把芯片配成了模式1或模式2采样边沿和芯片输出数据的时机不匹配读到的数据就可能是全FF或者乱七八糟。我之前遇到过最隐蔽的情况主控用的是STM32硬件SPI配置时误设置了SPI_CPOL_Low和SPI_CPHA_2Edge也就是把CPHA写错了结果读M95M04全FF读别的SPI设备却正常。因为不同的SPI从机对时序的要求差异很大A设备能容忍的错误时序B设备就是不行。所以排查时一定先看数据手册确认芯片支持的模式然后回头看MCU的SPI初始化代码。4.2 先读RDID别急着读数据区排查SPI EEPROM问题时我强烈建议先做一件事读设备ID。这比直接读数据区的命中率高得多因为读ID需要芯片完整地响应一次SPI通信能验证时钟、CS、MOSI、MISO全链路是否正常。M95M04数据手册里对应的指令比较常见的是0x9FJEDEC ID或0x83不同批次和封装略有差异。发送指令后连续读几个字节如果读回了有意义的ID比如厂商字节是ST的ID0x20说明SPI通信链路基本没问题。如果连ID都读不出来返回全FF那就回到硬件排查问题和数据区内容无关就是芯片没回应你的SPI指令。如果ID能读出来但数据区全FF问题范围就缩小了。这时候重点检查写保护和写入流程因为能读ID说明通信是好的读不出数据可能只是数据真的没写进去。4.3 状态寄存器EEPROM的体检报告M95M04有个状态寄存器读完它等于给芯片做了一次体检。读状态寄存器的指令是0x05发送指令后直接读一个字节。状态寄存器里几个bit含义不一样。bit0是WIP写进行中标志为1表示芯片内部正在执行写操作此时不能发起新的写指令。bit1是WEL写使能锁存位为1表示刚才的WREN指令生效可以进行写操作。bit2和bit3是BP0和BP1块保护位决定哪些地址区域被写保护。这几个位如果被设置成非预期状态写入就会失败读回来自然全是FF。排查顺序是这样的先读状态寄存器。如果WIP一直为1说明芯片卡在写操作中可能是之前的写指令没有正确结束。如果WEL为0说明WREN没生效写使能不成功就不用谈后续写入。如果BP位不为0说明有一部分地址被保护了写入那些地址会被忽略。还有一种可能有人曾经往状态寄存器里写了非零值把BP位设上了芯片就进入了块保护状态。这种情况在旧板子二次开发时尤其常见代码里初始化时没写状态寄存器但上一版固件可能已经设置过保护。解决办法是发0x01写状态寄存器把BP位清零。4.4 写操作的完整姿势如果你发现自己确实是在写数据后读回全FF大概率是写操作步骤没走全。EEPROM写操作的标准流程如下拉低CS发送0x06WREN写使能指令拉高CS结束指令拉低CS发送0x02WRITE发送2字节地址发送要写的数据拉高CS触发内部写周期等待WIP位清零轮询0x05读状态寄存器直到bit0为0重新发送0x03读数据验证这里最容易犯错的是第3步。WREN指令必须在CS拉高之后才算完成有些人在WREN之后不拉高CS直接发WRITE指令芯片不会接受。我在代码里见过不少这种错误看起来逻辑没错实际完全无效。还有一个高频坑写操作后没有等内部写周期完成。EEPROM写入一个字节内部需要几毫秒M95M04典型值是5ms左右。如果写完立刻发读指令芯片可能还在忙读到的当然还是FF。正确做法是轮询WIPWIP为0才说明写完。页写也要注意边界。M95M04支持256字节页写但页写不能跨页。如果你从地址0x00FF开始写两个字节第二个字节会写到0x0100吗不会它会回绕到本页开头0x0000。这种跨页回绕是EEPROM的通用行为很多人不知道数据写出来和预期完全不符。5. 实战排查记录从全FF到正常读取5.1 案例一WP引脚悬空导致的写不进去朋友的项目里M95M04焊在板子上MCU通过软件模拟SPI访问。现象是能读出ID数据区读出来全FF写入后立刻读还是FF。我问他WP引脚怎么接的他说“没接悬空的”。这就是典型问题。WP引脚内部没有可靠的默认电平悬空状态下受周围电路影响可能处于不定状态。实测时用万用表量WP引脚电压只有0.8V低于高电平门槛芯片实际上一直处于写保护状态。把WP用飞线接到VCC后写入恢复正常数据能正确读回。这个案例说明EEPROM的每个功能引脚都不能裸奔。WP接GND会写保护悬空会遇到不定电平最稳妥的做法是接VCC或者由MCU控制。5.2 案例二SPI模式配置错误另一个案例更隐蔽。主控用的是国产MCU的硬件SPI初始化的时候按照之前用一个SPI Flash的配置CPOL1, CPHA0也就是模式2。读M95M04返回全FF。从波形上看SCK空闲为高数据在下降沿被采样但M95M04期望的采样时刻是第二个边沿或者第一个边沿取决于是模式0还是模式3和当前配置完全不匹配。芯片输出数据的时机会和主机采样的时机错开导致采到的全是1。把SPI配置改成模式0或者模式3后问题立即消失。这个案例的教训是SPI设备不是一样的换芯片必须重新确认数据手册。尤其国产MCU的SPI硬件模块在实现细节上百家争鸣有些还支持“SPI模式自动检测”用不好反而更复杂。5.3 案例三时钟频率过高导致数据错乱还有一个案例是SPI时钟频率太高。主控的SPI外设时钟设置成了18MHzM95M04在3.3V供电下最高只支持16MHz具体看数据手册表格超频运行的结果是读ID偶尔正常读数据全FF。示波器上看SCK波形时钟边沿有明显振铃波形幅度在上升沿后没有稳定芯片在这个边沿采样时采到的是不确定电平。把SPI分频系数改一下降到8MHz所有问题消失。所以排查时如果波形紊乱、数据不稳定不要一味怀疑芯片先检查SCK是否在芯片支持的频率范围内。尤其注意不同电压下最大时钟频率不一样这个参数容易看漏。6. 常见问题速查表与调试建议6.1 全FF排查速查表我把实际调试中遇到过的情况整理成一张表按“现象 → 原因 → 解决办法”排列。项目里遇到全FF问题直接对照表格排查比从零分析快得多。现象可能原因排查/解决方法读ID、读数据都返回全FF芯片没上电或供电异常万用表量VCC确认电压在规格范围内读ID、读数据都返回全FFSPI模式配置错误确认CPOL/CPHA匹配模式0或模式3读ID、读数据都返回全FFMISO虚焊/断开量MISO通断确认主控引脚与芯片Q脚连接读ID、读数据都返回全FFHOLD引脚被拉低确认HOLD为高电平不要接地读ID、读数据都返回全FFCS片选时序错误示波器看CS时序确认指令期间CS为低ID能读数据区全FF出厂空白全新芯片默认全FF执行写操作后重读ID能读写入后读回全FFWP引脚写保护确认WP接VCC或由GPIO控制为高ID能读写入后读回全FFWREN未正确执行检查WREN后CS是否拉高再发起写指令ID能读写入后读回全FF写入后未等待WIP清零轮询状态寄存器bit0为0后再读ID能读写入后读回全FFBP位设置保护读状态寄存器写0x01清除BP位ID能读写入后读回全FF写地址越界/页回绕确认地址不超过容量页写不跨页读数据部分正确部分FF供电电压不足检查电源纹波确认在标准范围内读数据部分正确部分FF时钟频率过高降低SPI频率到芯片支持范围读数据部分正确部分FF地址字节数不对4Mbit必须发2字节地址不是1字节6.2 几个值得长期保留的调试习惯调试EEPROM这类SPI芯片有几个习惯是长期积累下来的对排查问题帮助巨大。第一个习惯单独写一个SPI回环测试函数。把MOSI和MISO短接发一串数据看是否能原样读回。如果回环测试都失败说明MCU的SPI配置或引脚有问题根本没到芯片那一层。这个办法能快速区分是主控问题还是芯片问题。第二个习惯读回来的数据打印成十六进制不要只打印字符串。0xFF和FF在串口助手里看起来有区别但当你调试大量数据时十六进制格式更容易发现规律。我见过全FF被误认为空字符串的情况耽误了不少时间。第三个习惯准备一个已知好用的样板。新项目里遇到SPI问题如果手头有另一块确认能正常读写M95M04的板子直接把芯片拆过去试或者把好板子的初始化代码拿来对比。很多时候问题就在几行初始化配置的差异上。第四个习惯EEPROM调试时务必把读操作的代码和写操作的代码分开封装用独立函数测试。不要写一个“写后读校验”的大函数一把梭不然出了问题你分不清是写失败还是读失败。分开测定位快得多。6.3 最后再分享一个实用技巧M95M04这类大容量EEPROM调试时建议先只写一个字节、读一个字节验证通路的正确性。很多人一上来就写整个扇区、整页数据然后发现全FF根本不知道从哪查起。从最小操作开始确认单字节读写正常了再扩展到页写和连续读。这个思路不只是适用这个芯片所有I2C/SPI存储芯片调试都通用。另一个小技巧是初始化GPIO时把CS、SCK、MOSI都先设置成确定的电平通常是高再初始化SPI外设。有些MCU的GPIO复用功能切换瞬间会有毛刺导致EEPROM收到错误的片选信号进入异常状态。先确认GPIO电平稳定再打开SPI外设可以避免这类偶发问题。如果你按上面的步骤排查之后发现全FF问题依然存在试着用示波器抓一次完整的读写操作波形发给我看也没用——我不在你的实验室但你可以自己对照数据手册里的时序图一个边沿一个边沿地核对。SPI调试就是这样看似玄学的问题最后都能落到某一条具体的信号线上。我也在博客里整理过几份常见SPI EEPROM的数据手册时序对比手头有这类项目的话可以翻翻不同芯片之间的差异往往就是故障的根源。
返回列表