
最近调一块板子启动日志里突然多了一行NOR Flash相关的报错——具体表现是SPI读回来的数据全是0xFF或者JEDEC ID和预期不符系统直接把Flash标记为Unavailable。这种问题在嵌入式开发里太常见了几乎每个做过单片机外部存储方案的人都遇到过。NOR Flash本身是个很皮实的器件但“不可用”这三个字背后藏着太多可能性可能是硬件连接问题可能是电源和时序问题可能是驱动配置问题也可能是芯片真的挂了。最坑的是这些问题表现出的现象往往一模一样都是“读不到ID”“SFDP解析失败”“擦写超时”。如果你没有一套系统的排查思路就只能靠换芯片、换板子碰运气。这篇文章我按实际调试顺序把NOR Flash从“完全无法访问”到“偶发异常”的排查链路完整过一遍包括硬件层面的引脚和时序检查、软件层面的协议握手与驱动配置、以及和NAND Flash对比后你会更清楚为什么NOR出问题会在启动阶段直接暴露。文末还有两个我实际跟过的故障复盘希望能帮你少走点弯路。1. 先给故障画像NOR Flash不可用的四种典型表现动手修之前先判断你遇到的是哪一种“Unavailable”。不同表现对应完全不同的排查方向。我见过很多同事拿到板子就直接拿烙铁补焊补完发现没用因为问题压根不在焊接上。1.1 四种常见表现和它们背后的线索故障表现可能原因优先排查方向读JEDEC ID返回FF FF FF芯片没供电、MISO断开、芯片未进入工作状态供电、片选、MISO连通性读JEDEC ID返回00 00 00MISO被拉低、焊接桥连、IO损坏引脚焊接、对地短路ID能读但SFDP解析失败时钟频率过高、dummy周期配置错误、通信模式不匹配SPI时钟、SFDP读取参数ID正常读写偶发失败信号完整性、供电纹波、WP#/HOLD#引脚干扰、状态寄存器保护时序、引脚上拉、状态寄存器这里有个关键认知NOR Flash的Unavailable大多不是芯片真的坏了而是它所在的通信链路出了问题。芯片本身是一个很简单的SPI从设备只要电压对、时钟对、引脚状态对它就会老老实实响应命令。所以排查的重点应该放在“链路”而不是“芯片”上。1.2 排查原则先分层再动手我习惯把问题拆成三个层次硬件层供电、引脚连接、信号完整性、焊接质量。协议层命令是否能被正确发出、返回值是否能被正确采样。驱动层时钟配置、模式切换、状态寄存器、擦写等待逻辑。这三层是有依赖关系的。硬件不稳定协议层怎么调也没用协议层不对驱动层再严谨也白搭。所以排查顺序必须是硬件→协议→驱动不要跳步。很多人一上来就怀疑驱动代码反复改软件最后发现是WP#引脚悬空导致的杂散干扰浪费了好几天。2. 硬件链路排查从电源到WP/HOLD引脚的逐个排查顺序先说结论我在实际项目里遇到的所有NOR Flash“不可用”案例超过一半最终定位在硬件层。这一节按排查顺序讲每一步都有明确的验证方法。2.1 供电电压和纹波第一个要排除的嫌疑NOR Flash常见供电电压是3.3V和1.8V也有少数2.5V的型号比如ISSI的一些低功耗系列。拿到Unavailable的板子先用万用表量芯片VCC引脚上的实际电压不要只看电源指示灯亮就认为供电正常。有些板子的3.3V网络被其他大电流外设拉低到2.8VFlash还能勉强工作但已经处在不稳定区间。更隐蔽的问题是动态纹波。NOR Flash在擦写操作时电流会突然增加如果VCC引脚旁边的去耦电容缺失或者离得太远电源轨会出现毫秒级的跌落。示波器用DC耦合触发电平设在3.0V左右抓一次擦写命令期间的VCC波形。如果看到跌落超过100mV先别急着怀疑Flash把去耦补上再说。我习惯在Flash的VCC引脚旁边放两个电容一个0.1uF陶瓷电容靠近引脚用于滤高频噪声一个2.2uF到10uF的钽电容或大容量陶瓷电容用于应对擦写时的瞬态电流。这个组合在绝大多数MCU方案里都够用。2.2 SPI引脚的连通性CS/CLK/MOSI/MISO逐一确认确认供电正常后接下来用万用表蜂鸣档检查四条信号线的连通性片选CS、时钟CLK、主出从入MOSI、主入从出MISO。不要只测芯片引脚和MCU引脚之间的通断要测到MCU封装那边因为过孔断裂或PCB走线划伤会导致中间断开。这里有一个很实用的技巧把板子断电用万用表二极管档测MISO引脚对地的压降。SPI设备上电后MISO应该是高阻状态但芯片内部通常有静电保护二极管测出来的压降大约在0.4V到0.7V之间。如果MISO对地短路压降接近0V说明该引脚有桥连。如果MISO对VCC短路压降会异常。这一招能快速发现焊接问题。顺带检查一下CLK和CS之间有没有串扰。特别要注意的是CS信号它是异步控制信号如果走线旁边有一条频繁翻转的I2C时钟线CS上可能被耦合出毛刺。CS一旦出现非预期拉低Flash会认为主机发起了新的传输当前命令直接中断表现就是偶发读错数据。2.3 最容易翻车的WP#和HOLD#引脚这是NOR Flash“不可用”问题里最容易被忽略、也是最容易踩坑的地方。WP#是写保护引脚拉低后会锁住状态寄存器的BP位和SRP位此时即使软件往状态寄存器写0也无法解除保护。HOLD#是暂停传输引脚拉低后芯片会暂停当前的SPI通信MISO输出高阻CLK和MOSI上的信号被忽略。很多低成本板子为了让MCU多几个GPIO出来会把这两个引脚直接悬空。但不同厂商的NOR Flash内部上拉/下拉配置不一样有的芯片内部有弱上拉悬空时默认高电平问题不大有的芯片内部没有上拉或者上拉很弱引脚电压受外界干扰漂移就会偶发触发写保护或暂停传输。最稳妥的做法是无论用什么型号WP#和HOLD#都必须通过10kΩ电阻上拉到VCC。如果硬件已经做错了软件层面可以临时补救把这两个引脚对应的MCU GPIO初始化为输出高电平。但这种方法依赖MCU先跑起来如果Flash里放的是启动代码且需要在启动早期访问就会陷入死循环——芯片还没被正确配置CPU却要从芯片里取指。2.4 仪器使用顺序万用表、示波器、逻辑分析仪硬件排查阶段的仪器使用顺序很重要。先万用表确认连通性和短路再示波器看电源和关键信号波形最后用逻辑分析仪看协议时序。不要一上来就挂逻辑分析仪那是协议层该干的事。示波器看单次事件不好抓这时候逻辑分析仪的优势就出来了它能长时间记录SPI总线上所有命令交互方便回放分析。3. 协议握手排查JEDEC ID和SFDP解析失败到底卡在哪硬件链路确认没问题之后下一步是协议层。这一层的核心是看MCU和NOR Flash之间的命令交互是否符合芯片手册要求。3.1 读ID命令0x9F返回的信息量几乎所有SPI NOR Flash都支持0x9F命令读取JEDEC ID返回3个字节厂商ID、设备ID高字节、设备ID低字节。例如Winbond的W25Q128返回EF 40 18Macronix的MX25L12835F返回C2 20 18GigaDevice的GD25Q128返回C8 40 18。如果读回来的数据是FF FF FF说明芯片根本没响应优先怀疑供电、片选、MISO通路。如果读回来是00 00 00说明MISO被拉低了检查焊接桥连。如果读回来三个字节乱了比如EF 00 18说明MISO数据采样有问题通常是时钟极性/相位不匹配或者时钟频率过高。下面是一段最基础的ID探测代码我通常会在初始化流程最前面放这个函数失败就报错并在串口打印ID内容static int nor_flash_check_id(void) { uint8_t cmd 0x9F; uint8_t id[3] {0}; spi_cs_low(); spi_transfer(cmd, 1); spi_receive(id, 3); spi_cs_high(); /* 判断厂商ID0xEF是Winbond0xC2是Macronix0xC8是GigaDevice */ if ((id[0] ! 0xEF) (id[0] ! 0xC2) (id[0] ! 0xC8)) { printf(NOR Flash ID mismatch: %02X %02X %02X\n, id[0], id[1], id[2]); return -1; } return 0; }3.2 SFDP读取:签名校验不过基本是时序问题SFDPSerial Flash Discoverable Parameters是JEDEC标准用来让主机自动发现Flash的容量、扇区大小、擦除命令等参数。读取SFDP需要发5A命令后面跟24位地址和8个dummy周期然后读取数据。很多驱动会在上电后自动唤醒SFDP如果SFDP签名不是SFDPASCII码50444653就把Flash标记为不支持或不可用。实际排查中我发现SFDP解析失败最常见的原因有两个第一个是dummy周期配置错误。不同厂商的Flash对SFDP读取的dummy周期要求不总是相同有些是8个dummy周期有些是1个字节的dummy周期。如果你抄的驱动是从另一个型号的Flash上移植过来的很可能这里就错了。第二个是SPI时钟频率太高。SFDP读取对时序要求比普通读ID更严格一些低速模式比如标准SPI模式下的最高频率只有33MHz甚至更低如果你用50MHz去读SFDP偶尔能过、偶尔过不了最后表现为偶发Unavailable。3.3 用逻辑分析仪抓时序的实际经验软件层面的问题可以通过逻辑分析仪快速定位。把逻辑分析仪的四通道分别接CS、CLK、MOSI、MISO采样率至少要达到SPI时钟的4倍以上。触发条件设为CS下降沿。抓到的波形里你会看到主机发出0x9F命令字节后在接下来的24个时钟周期里MISO上有数据返回。如果MISO全程没有拉高过说明芯片没应答如果MISO上有一串杂散波形但数据沿不在CLK采样窗口内说明时钟相位配置有问题。我遇到过一个非常隐蔽的情况MCU的SPI外设配置成了Mode 3即CPOL1、CPHA1NOR Flash手册写的是支持Mode 0和Mode 3按理说能正常工作。但抓波形发现CLK空闲时是高电平而Flash有些厂商的型号在Mode 3下能读ID却会在执行擦除命令时状态机错乱。最后换成Mode 0问题消失。所以调试时如果Mode 0和Mode 3都试一下成本很低。4. 驱动配置里的隐形地雷时钟频率、工作模式与状态寄存器过了协议握手这一关Flash能被正常识别但读写时依然可能出现故障。这一阶段的问题大多藏在驱动配置细节里。4.1 SPI时钟频率超过上限偶发故障的头号来源很多MCU的SPI外设最大时钟能跑到几十甚至上百MHz但NOR Flash的标准SPI读命令有严格的频率上限。比如W25Q128的普通读命令最高频率是50MHzFast Read命令最高是133MHz但Fast Read要求指令后跟3字节地址和8个dummy周期。如果你的驱动没有区分普通读和Fast Read却把时钟配到80MHz芯片就会响应异常。我的建议是普通Read命令和写命令编程/擦除一律把SPI时钟降到20MHz以下只有读取大量数据时才用Fast Read并配到规格书允许的频率。调试阶段一律先降到10MHz验证功能功能正常后再逐步提频找到稳定边界最后留出30%的余量。4.2 Quad模式切换后回不到Standard模式这是另一个高频翻车点。为了提升读速度很多项目会启用Quad SPI模式即四线IO读写。切换过程通常是先发01命令写状态寄存器把QE位置1然后改用Quad命令读取数据。但这里有个问题软件复位命令66 99会把状态寄存器恢复为出厂默认值QE位会被清掉。如果驱动在初始化时执行了软件复位但后续代码没有重新设置QE位Quad指令返回的数据就会全部错乱。还有一种情况是擦除操作耗时较长看门狗超时触发了MCU复位复位后驱动重新初始化但如果初始化顺序是先读状态寄存器再设置QE就有竞争窗口。规避方案很简单每次初始化最后都检查QE位如果没置位就先写状态寄存器。写状态寄存器时要先发06写使能命令再发01命令和要写入的值缺少写使能那一步也是常见的静默失败原因。4.3 状态寄存器里的写保护位能读不能写的真相当Flash能正常读ID、能读取数据但擦除或编程时报错或超时大概率是状态寄存器里的块保护位BP3~BP0设置了。出厂默认一般为0但如果芯片是二手料、翻新料或者在其他设备上配置过可能存在非0值。用05命令读状态寄存器把返回值打出来看。正常情况WIP位为0BP位为0表示未保护。如果BP位不是0写操作会被拒绝芯片表现就是“读一切正常写全部失败”。有些驱动把这种情况直接归类为Unavailable其实解除保护就能解决。解除保护流程发06命令置位WEL写使能锁存。发01命令写入0x00清除所有BP位。发05命令读状态寄存器确认WEL为0且BP位全为0。另外还有两个状态寄存器里的坑SRP位状态寄存器保护位和WPS位写保护选择位。如果SRP位被置1且WP#引脚拉低状态寄存器就完全锁死软件怎么改都没用。遇到这种“软硬不吃”的芯片先检查WP#引脚是不是被异常拉低了。5. 为什么偏偏是NOR Flash和NAND Flash对比后你会更懂它前面讲的都是排查方法但如果你同时接触过NAND Flash会发现NOR Flash的“不可用”问题在表现上跟NAND完全不一样。搞清楚两者的本质差异很多时候能直接帮你判断问题方向。5.1 接口和访问方式的根本区别NOR Flash更像内存支持按字节随机读取可以直接连接数据总线执行代码XIPExecute in Place。这就是为什么MCU可以从NOR Flash直接启动CPU取指时不需要先把代码搬到RAM。NAND Flash则完全不一样它只能按页读取通常一页2KB或4KB而且不能随机访问任意字节必须先发命令、发地址、读整页到内部缓存再从中取数据。NAND没办法XIP所以MCU从NAND启动时需要BootROM先把引导代码搬到SRAM里执行。这种差异导致NOR Flash在系统上电瞬间就要被访问而这时候主控的电源、时钟甚至复位逻辑都还没完全稳定所以NOR的Unavailable往往出现在启动早期NAND则是在BootROM阶段才被访问如果BootROM本身不依赖NANDNAND故障通常表现为“能上电但启动到一半卡住”。5.2 从可靠性角度看两者的不同脆弱点NOR Flash的位翻转率很低也不需要ECC校验。NAND Flash出厂就可能有坏块使用过程中还会不断产生新坏块必须有坏块管理和ECC机制兜底。但这不代表NOR更可靠。NOR的XIP特性意味着它经常在主控时钟还没稳定的情况下被读取它承受的电源噪声和总线干扰比NAND更直接。我遇到过一种情况主控的SPI时钟配置成内部RC振荡器RC振荡器在低温下频率漂移超过5%导致NOR Flash读时序失效而同样的代码放到NAND方案上就没问题因为NAND的访问时序宽容度更高。另外一个容易忽略的点是NOR Flash擦写寿命通常是10万次听起来很多但如果代码里有一个高频写入的日志功能比如每秒写一次一万秒后芯片就逼近寿命极限。磨损后的NOR Flash不会立刻完全不能用而是会出现擦除超时、校验失败最终驱动判定Unavailable。如果你发现某块板的Flash坏块数量明显偏多先别怀疑芯片检查一下业务代码是不是有频繁写操作。5.3 选型时怎么避掉一部分Unavailable风险选型阶段做对几件事能减少后期排查的麻烦优先选主流大厂型号Winbond、Macronix、GigaDevice、ISSI、Adesto/Dialog。这些厂商的手册详细兼容型号多竞争对手之间pin-to-pin替换也容易。确认供电电压兼容1.8V和3.3V不能混用有些双供电型号要同时留意VCC和VIO两个引脚。封装选择要留余量QFN封装在手工焊接时容易虚焊SOIC-8更容易返修。产品如果计划手工量产选SOIC能降低不少焊接问题。替代料不要只看引脚兼容不同厂商的SFDP内容和状态寄存器定义可能有差异直接替换前必须跑一遍完整的读ID、SFDP解析、擦写校验测试特别是Quad模式的QE位行为替代料可能会在这里坑你。6. 两个真实故障复盘从误判到找到根因的完整记录理论说再多不如看两个实际案例。这两个案例都是我在项目中真刀真枪跟过的症状典型原因也很有代表性。6.1 案例一时钟过快导致的上电偶发不可用现象Cortex-M7主控外挂一块W25Q12816MB。开发环境烧录正常但批量样机重新上电后大约有三分之一概率启动失败日志里报SFDP签名错误、JEDEC ID读不到。偶尔重启一次又能正常启动。排查过程先量了VCC3.3V稳定纹波约30mV电源嫌疑排除。补焊Flash引脚问题依旧排除虚焊。用逻辑分析仪抓上电瞬间的0x9F命令时序。发现MISO上的数据翻转沿明显变缓尤其是在温度稍高的环境里数据建立时间不够。看PCB设计Flash放在板边走线到MCU大约8cm而且是两层板没有完整参考平面。SPI时钟配置为50MHz。把SPI时钟降到20MHz连续上电100次全部正常。再把CS和MOSI线上串了22Ω电阻实测30MHz也能稳定但保守起见量产仍然用20MHz。根因50MHz时钟在8cm走线和两层板环境下信号反射严重MISO采样窗口被压缩板子个体差异导致有些能过有些过不了。这不是Flash芯片的问题是信号完整性的问题。6.2 案例二HOLD#引脚悬空引发的写操作静默失败现象另一款产品用STM32L4外挂8MB NOR Flash。量产产测时有千分之几的板子在烧录完成后做读取校验失败报NOR Flash校验错误但重新跑一次校验又能通过。排查过程一开始怀疑校验算法问题写了个独立脚本反复读同一区域稳定复现后确认是Flash返回的数据偶发错误。观察现象失败时读出来总是连续几个字节错误而且这些字节在多次读取中不一致这说明不是数据写错了而是读到的数据不稳定。用示波器同时抓HOLD#引脚和SPI总线发现HOLD#引脚上有毛刺毛刺和相邻的I2C时钟线翻转同步。查硬件设计HOLD#引脚被连到MCU的一个GPIO但这个GPIO没有初始化处于浮空输入状态相当于HOLD#悬空。修复硬件把HOLD#加了10kΩ上拉到VCC软件也把该GPIO初始化为输出高。之后该问题零复发。根因HOLD#引脚浮空受到I2C信号串扰偶发拉低。HOLD#一旦拉低Flash暂停当前传输SPI总线上虽然看起来还在跑时钟但MISO已经不按正常序列输出了。6.3 复盘总结先怀疑链路而不是芯片这两个案例的共同点是最终根因都不在Flash芯片本身而在它周围的环境。第一个案例是时钟频率和信号完整性第二个案例是控制引脚浮空被干扰。如果你遇到类似的Unavailable问题建议按这个顺序怀疑电源、引脚状态、时钟频率、焊接质量、最后才怀疑芯片本体。芯片本体损坏的概率其实很低。7. 让NOR Flash不再“不可用”的工程习惯排查经验积累到最后你会发现好的设计习惯能让很多问题在源头就消失。下面这些做法是我在新项目里默认就带上的。7.1 硬件设计的几个低成本改动WP#和HOLD#各加一个10kΩ上拉电阻到VCC。这两个电阻总成本不到一分钱能省掉后期大量排查时间。VCC引脚紧挨着放0.1uF陶瓷电容电容到引脚走线尽量短过孔直接打在电容旁边。SPI时钟走线尽量不要跨分割保持完整参考平面。如果只能两层板时钟线和数据线旁边加地线包一下。如果SPI时钟超过30MHzCS、CLK、MOSI上串联22Ω到33Ω的端接电阻。这个阻值不用很精确能明显改善过冲。Flash附近不要放高频开关节点比如DCDC的SW引脚、晶振引脚保持至少5mm以上间距。7.2 软件层的重试、降级与恢复策略软件逻辑上我习惯在驱动里加三层防御第一层是ID探测重试。上电后读JEDEC ID失败就重试3次每次间隔50ms。有些板子在复位瞬间电源还没稳定给几十毫秒的缓冲就能过。第二层是模式降级。如果Quad模式初始化失败自动回退到Standard SPI模式。很多业务代码其实用Standard SPI就够Quad只是锦上添花没必要为了它牺牲稳定性。第三层是擦写状态机超时。擦除一个扇区通常需要几十到几百毫秒如果驱动里的WIP轮询超时设得太短比如只等100ms就会把正常的擦除误判为超时。建议设到1秒代价只是故障场景下多等一会儿。关键数据存两份还不够我建议把其中一份用Standard SPI模式写入另一份用Quad模式写入这样即使QE位状态异常至少有一份数据能正常读出。如果两份用的都是Quad模式QE位一旦丢失两份数据全部读不出来系统就只能恢复出厂设置了。7.3 生产测试环节的把关产测对NOR Flash的可靠性影响很大。我见过不止一次板子在产线上能过出货到客户手里就出现启动失败退回来看Flash ID读不到但用编程器离线读却一切正常。这里的问题通常出在产测环节没有覆盖上电循环测试。建议产测脚本里增加连续上下电循环50次每次上电都读一次JEDEC ID和关键配置数据确认每次都能正确识别。对Flash进行全芯片写0xFF、读回校验、再写0x00、读回校验至少跑一遍。这个测试能暴露大多数焊接和信号完整性问题。如果条件允许做24小时高温老化温度55度期间反复读写。很多偶发问题在高温下会加速暴露。另外如果客户退回设备报告Unavailable建议先用编程器离线读取Flash内容判断是Flash本身数据损坏还是主控访问链路的问题。离线读正常说明问题大概率在主控侧或PCB链路离线读也不正常才能怀疑Flash芯片本身。我做嵌入式这些年NOR Flash相关的“Unavailable”问题踩得够多了。现在的习惯是遇到这类问题先强制自己走一遍“电源→引脚→时序→驱动→芯片”的排查顺序不要上来就换器件。因为绝大多数Unavailable最后都会发现是链路或者配置的问题真正芯片坏掉的比例非常低。这个思路帮我省下的返工时间已经足够写好几篇调试笔记了。