尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

嵌入式调试实战:PSRAM、NOR Flash与USB的不确定性排查

嵌入式调试实战:PSRAM、NOR Flash与USB的不确定性排查 拿到这个标题的时候我第一反应是想起之前做过的一个物联网网关项目主控外扩了PSRAM做图像缓冲NOR Flash存固件和配置USB口既要连4G模组又要做虚拟串口和PC通信。三个外设凑在一起看起来各管一摊但联调起来什么都跟你玩“薛定谔的稳定”有时候跑一整天没事有时候上电就卡死有时候只有某一片板子复现有时候换个USB口就正常。所谓“Uncertainity in PSRAM, NOR Flash, USB”我理解不只是这三个外设本身的不确定性更是它们共存于一个系统时时序、总线、电源和软件初始化顺序相互纠缠产生的那种“时好时坏”的玄学问题。这篇文章就是我个人把这些不确定性逐一拆开、定位、消灭的完整记录适合正在做带外部存储和USB接口的嵌入式项目的朋友参考尤其是那种被“偶发故障”折磨到怀疑人生的阶段。1. 内容整体设计与思路拆解先搞清楚不确定性到底从哪来1.1 为什么偏偏是这三个外设容易“不确定”先说结论PSRAM、NOR Flash、USB三个都是典型的“外部设备”它们的不确定性根源完全不同但症状很像。PSRAM的不确定性主要来自时序余量。它本质上是伪静态随机存储器内部需要刷新对外却要模拟SRAM的读写时序。主控侧如果时序配得刚好卡在高低电平阈值附近温度一变、电压一抖读回来的数据就可能随机出错。这种错误不像硬故障那么容易复现表现出来就是“偶尔花屏”“偶尔校验失败”。NOR Flash的不确定性则集中在擦写和掉电。读操作相对简单但写和擦除需要完整的命令序列任何一步被打断都可能让内部状态机卡死。更麻烦的是如果系统在擦写过程中掉电你可能会得到一整个扇区的全0xFF或半旧半新的数据而且这种损坏在下一轮擦除之前很难被检测到。USB的不确定性是最“社交”的它涉及协议握手和主机端的驱动。USB枚举过程有一堆超时限制只要应答晚了哪怕几毫秒主机会直接判定设备失败。而且USB的电平标准只有3.3V以下信号完整性稍微差一点眼图闭合就会出现“插上没反应”“偶尔识别为未知设备”这类问题。1.2 方案选型背后的考量我在这个项目里选型时没有追求“高大全”而是按数据特性分了工PSRAM选的是8MB容量、SDR接口的型号用来放摄像头原始帧和协议转换缓冲。之所以不用普通SRAM是因为同容量SRAM价格高到离谱而PSRAM在容量和成本之间是最平衡的。NOR Flash选了16MB的四线QSPI接口存bootloader和两个固件副本A/B分区方便远程升级失败后回滚。USB则设计了两个物理口一个是Device模式做虚拟串口另一个通过USB Hub扩展成Host模式接4G模组。这个选型有一个隐藏考虑三个外设可以共享同一组高速GPIO或者同一套DMA通道吗可以但我不建议。我在设计时把PSRAM接到专用的SMC/EXMC控制器NOR Flash走独立的QSPI控制器USB则走内置USB PHY和专用的DMA。这样做的理由是越少共享越少不确定性。一旦它们共用总线任何一方出时序问题另外两方都会被拖下水。1.3 从“玄学”到“工程”的转变思路遇到偶发问题我一般不会直接改代码而是先把“不确定性”量化。具体做法是把所有外设的初始化成功次数分别打点连续跑100次上电看失败率。把每个外设的读写延迟记录下来用逻辑分析仪抓时间戳。人为制造干扰降低供电电压0.1V、升高环境温度、把线束拉长看哪个外设先崩溃。这套方法看起来笨但非常管用。它能把“有时候不行”变成“在什么条件下不行”一旦变成可复现条件定位就只需要二分法了。2. 核心细节解析与实操要点逐个拆解PSRAM、NOR Flash、USB的脾气2.1 PSRAM的时序配置别信参考代码PSRAM对外接口虽然像SRAM但内部刷新机制决定了它读和写的最小时序约束不一样。很多参考代码直接把SRAM的时序参数套上去结果就是看起来能读能写但跑几个小时才出错一次。我调PSRAM时重点盯四个参数tRC读周期时间从片选有效到下一次片选有效的最小间隔。tPACE页访问时间同页内连续访问的间隔。tCDD数据输出延迟读命令发出后到数据线上出现有效数据的时间。tWR写恢复时间写信号结束到片选信号撤销的间隔。这四个参数数据手册里都给了最小值但实际要按1.5倍余量设置。比如手册说tRC最小是70ns我配置成105nstWR最小是10ns我配置成15ns。代价是最高吞吐率打了折扣但换来的是代码不需要加各种“失败重试”逻辑。还有一个很容易踩的坑PSRAM的刷新和访问冲突。某些PSRAM在刷新期间读写访问会被延迟而主控时序配置里没有等待状态的话数据总线就会采样到不定值。解决办法是开启主控的“可变等待周期”功能或者干脆把PSRAM的访问时钟降到数据手册推荐频率的80%。实测降到80%以后PSRAM相关的偶发坏帧率直接降了两个数量级。2.2 NOR Flash的擦写与掉电保护NOR Flash的读只要SPI时序对、时钟频率在范围内基本不会出错。写和擦可就不一样了。QSPI NOR的写使能WREN、页编程PP、扇区擦除SE这三条命令必须严格按顺序发每一步都要读状态寄存器RDSR确认WIP位清零。我见过太多人把擦写流程简化成“发命令、等一会、再发下一条”中间不查状态寄存器。这在大部分时候没事但一旦Flash内部正在擦除时你发了写命令Flash会直接忽略它或者把状态寄存器里的WEL位搞乱后续所有写操作都变成无效操作。更恶心的是这种错误不会报错你读回数据时发现全是0xFF才意识到根本没写进去。掉电保护是另一个经典的不确定性来源。我的做法是在硬件上给NOR Flash的供电加一个几毫秒的掉电保持电容至少撑到固件把当前写操作的逻辑完成。在软件上写操作分两步先把数据写到两个影子扇区完成后更新一个“提交标志”下次启动时检查提交标志如果不完整就回滚到上一个有效固件。这套双影子加提交标志的做法本质上就是把“不确定”变成“最终一致”。虽然占用了额外空间但至少每次升级失败后系统都能正常启动不会变砖。2.3 USB的枚举时序、驱动和信号完整性USB的不确定性三个层面协议时序、主机驱动、物理信号。协议层面最关键的枚举流程是设备连接后主机发送复位信号接着在控制传输的地址0阶段设备要返回设备描述符。这里有一个容易被忽略的约束设备必须在复位结束后的10ms内完成端点0的配置并在收到第一个SETUP事务后快速响应。如果固件里把USB初始化和PSRAM初始化放在同一个函数里而PSRAM初始化又死慢就会导致主机还没收到描述符就直接超时。我习惯把USB初始化放到一个高优先级任务里保证枚举期间不被其他外设初始化阻塞。这里有个更细的小技巧很多MCU的USB控制器内置了程序不可读的RAM用于缓存端点数据。这个RAM的访问和USB模块时钟有关系如果你为了省电把USB时钟关了一会儿再打开时RAM内容可能已经乱了表现就是“第一次枚举成功拔掉重插就不识别”。解决办法是每次检测到USB断开事件后把端点RAM重新清零并重新初始化一次。驱动层面的不确定性主要在主机侧。特别常见的是所谓“USB转串口”芯片的驱动FT231x、FT232R、CP2102N这些如果主机上装了多个版本的驱动或者系统更新后驱动签名失效就会出现“设备管理器里感叹号但换一台电脑又正常”。我在项目里做到两件事一是把CDC类的描述符写得非常标准尽量用系统自带驱动二是发布文档里写清楚对主机驱动版本的要求附带官方签名的驱动包。不要指望用户都是驱动专家。物理信号层面USB的D和D-差分对需要90欧姆差分阻抗。如果PCB上走线过孔太多、或者串了太大的电阻信号质量就会差。我实测遇到过最奇葩的现象在USB线上串了22欧姆电阻台式机前置USB口识别失败后面板却正常。原因是前置口线缆更长信号衰减更大22欧姆串阻彻底把眼图搞闭了。后来把串阻改成0欧姆问题消失。3. 实操过程与核心环节实现一步一步调通三个外设3.1 环境准备和测量工具进入实操之前我先把工具备齐逻辑分析仪一台至少16通道采样率100MHz以上用来抓PSRAM的地址/数据总线命令和NOR Flash的SPI信号。示波器一台带宽至少200MHz带差分探头最好用来量USB D/D-和电源纹波。一个可调限流电源用来做低压测试。软件方面USB协议分析用Wireshark开USBpcap或USB Device Tree Viewer查描述符NOR Flash调试用串口打印状态寄存器PSRAM调试用片内DMA反复读写并做异或校验。环境不要将就没有这些工具遇到的问题只能靠猜。我有个习惯任何外设的偶发问题如果三次复现不出来就上逻辑分析仪连续抓两小时总能找到规律。3.2 PSRAM的读写测试和时序验证先写一个最原始的PSRAM裸机读写测试不要跑操作系统不要开缓存直接地址映射volatile uint32_t *psram_base (uint32_t *)PSRAM_BASE_ADDR; // 写全地址寄一个递增数 for (uint32_t i 0; i PSRAM_SIZE / 4; i) { psram_base[i] i; } // 再读回来比对每一项 uint32_t errors 0; for (uint32_t i 0; i PSRAM_SIZE / 4; i) { if (psram_base[i] ! i) { errors; } }不要觉得这测试太简单它能暴露基本接线问题和时序问题。如果错误地址是连续的大概率是地址线虚焊如果是随机分布的基本指向时序余量不够。我的经验是先跑一遍全地址写完再读回这是最宽松的模式再跑一遍边写边读同一个地址写完立刻读后者更容易暴露tWR不够的问题。如果边写边读出错了就把写恢复时间调大一档直到错误清零。最后再过一遍“棋盘格”和“反棋盘格”模式检查相邻单元干扰。调完时序后我还会做一次低电压下的压力测试。把电源电压从标称值往下调0.1V同样跑边写边读如果出错说明时序余量还不够。这个过程我称之为“给不确定性留预算”。3.3 NOR Flash的固件读写和升级流程NOR Flash调试的关键不是“能读”而是“能可靠地写完再读”。我的标准流程分四步uint8_t status; // 1. 写使能 qspi_send_cmd(0x06); // 2. 发送页编程命令带地址和数据 qspi_send_cmd_with_addr_and_data(0x02, addr, buf, len); // 3. 等待WIP位清零 do { status qspi_read_status(); } while (status 0x01); // 4. 读回校验 qspi_read(addr, readback, len); if (memcmp(buf, readback, len) ! 0) { // 重试或上报 }第三步的“等待WIP清零”不可省略。有些人喜欢用固定延时替代但固定延时要么太长浪费启动时间要么太短在边界情况下读到未完成状态。轮询WIP才是最稳的。真正高级的坑出现在“擦除过程被中断”。比如系统在擦除扇区时收到了高优先级USB中断而你恰好在中断里也访问了NOR Flash这会导致SPI总线时序错乱。解决方法是给NOR Flash的访问加互斥锁任何中断服务程序里都不要直接调Flash驱动而是发信号给后台任务处理。这个原则我一般在架构设计阶段就定死。还有一个细节QSPI模式切换。从普通的SPI模式切到QSPI模式后芯片会把IO2和IO3脚复用为数据线。如果固件在启动早期用了GPIO控制这些引脚切模式时一定要把对应引脚配置成QSPI功能否则写使令发不出去。这种问题表现为“第一次写成功复位后再写失败”因为复位后引脚恢复成GPIO了。3.4 USB虚拟串口和Host模式的实现要点我这次做的USB功能有两路Device模式当虚拟串口CDC ACMHost模式接4G模组。两者在同一个USB控制器上分时复用这里的不确定性主要在“角色切换”。在Device模式下固件要做的事情很明确初始化USB控制器使能D上拉电阻。等待主机复位中断。枚举结束后注册CDC回调函数。用DMA收发串口数据。这里最值得注意的是DMA和端点缓冲区的一致性问题。USB每个端点都有自己的缓冲区如果DMA正在往端点缓冲区写数据的同时主机发送了IN令牌控制器会禁止写入DMA传输直接失败。这个问题的现象是“发送大数据时偶尔丢帧”而且只在系统繁忙时出现。解决办法是开启USB控制器的“端点多包”并且DMA中断和USB完成中断里都用临界区保护共享缓冲区指针。Host模式更麻烦因为4G模组的枚举不一定顺利。我遇到最多的是模组上电后需要几十毫秒固件加载时间如果主控上电马上枚举模组还没准备好就会枚举失败。解决办法是Host模式下先给模组上电等待至少100ms再发起总线复位。做了这个延时后4G模组的识别率从70%提升到99%。USB功能调试时强烈建议使用USB分析仪或者Wireshark抓包不要只看设备管理器。因为有些错误是主机已经枚举成功但设备响应某个控制请求时返回了STALL导致功能不可用。这种问题用逻辑分析仪在D/D-上也能抓但效率低。USB分析仪能直接看到SETUP包和数据包的内容一下子就能定位到是哪个描述符字段不对。4. 常见问题与排查技巧实录把不确定性变成确定性4.1 现象到根因的速查表我在项目过程中做了个表格每次遇到新问题就往上加一行。现在整理出来几乎涵盖了这三个外设最常见的“不确定性”问题现象可能涉及的设备首选排查方向上电后偶发花屏重启后正常PSRAM检查tRC和tCDD时序余量降低PSRAM时钟频率跑一段时间后系统死机读Flash数据错乱PSRAM NOR Flash检查PSRAM是否访问越界覆盖到Flash映射区Flash写入失败状态寄存器WIP一直为1NOR Flash降低SPI时钟频率检查供电是否稳定复位后固件校验失败偶尔能启动NOR Flash检查启动时是否在Flash擦写尚未完成时复位USB第一次插入识别成功拔掉再插失败USB检查USB断开事件处理重新初始化端点RAMUSB枚举成功但无法打开虚拟串口USB查看主机设备管理器是否有驱动感叹号检查描述符CDC类字段USB在台式机前置口失败后面板正常USB 物理层减小信号线上的串阻检查90欧姆差分阻抗插入USB后系统其余外设工作异常PSRAM NOR Flash USB 地弹检查USB的GND平面和电源去耦测量地弹噪声4G模组偶尔枚举失败USB Host上电后增加延时确保模组固件加载完成表格只是起点重点是后面的排查方法。4.2 独家排查技巧二分法和最小复现遇到“偶尔不行”我从来不盲目改代码。我的方式是先做最小复现。比如PSRAM偶发出错我会把缓存、DMA、中断全关掉只留裸机读写循环看错误是否消失。如果消失说明问题在复杂交互如果还在就是时序或硬件本身。对于三外设共存的问题我发明了一个“静默排查法”先把USB Host关闭只跑PSRAM测试和Flash擦写测试跑一百遍。如果没出错再把USB Host打开跑一百遍。如果出错就用逻辑分析仪抓USB Host访问内存总线的信号看是不是DMA抢占导致PSRAM访问被拖慢。这个办法虽然耗时但能完美隔离出是“USBDMA传输占用了带宽”还是“USB产生的中断影响了Flash状态机”。另一个技巧是制造温度变化。用热风枪对着电路板特定区域加热到50度同时跑压力测试。如果某个外设的时序余量不足温度一高就会暴露出来。测试完记录下哪个芯片先出错这里十有八九是根因。我把这招叫“热加速老化测试”帮我抓过至少三次元器件批次问题。4.3 调整优先级和初始化顺序的实战记录项目里我最成功的一次调整是把初始化顺序从“USB优先”改成“PSRAM优先其次是NOR Flash最后USB”并且把所有外设初始化都放到了同一个管理框架里支持超时重试。最初版本是上电就初始化USB我本意是让PC端尽早看到设备。结果PSRAM还没配置好USB枚举过程中DMA又占用了内存总线导致PSRAM访存延迟增加部分数据被覆盖。改成PSRAM优先后开缓存USB枚举时PSRAM已经稳定运行问题消失了一半。NOR Flash的初始化反而是最简单的。我把它放在PSRAM之后但因为要读固件配置所以启动早期就需要访问。这里的技巧是在Bootloader阶段只做NOR Flash的读操作不启用写功能。写功能等应用层加载完、电源稳定后再打开避免在电压爬坡时误触发擦写。最后USB初始化时我特意关掉了所有非必要中断等枚举完成后再恢复。实测这个过程从原来的1.2秒缩短到400毫秒而且100次上电测试全部枚举成功。5. 联调时的工具配合与实测数据分析用数据说服自己5.1 逻辑分析仪和示波器的配合姿势工具不能只会接还要会看。我调PSRAM时用逻辑分析仪抓地址线和数据线触发条件设为“数据线上出现某个异常值”然后往回看地址变化。如果发现某个地址段反复访问但数据不对说明那段地址有硬件问题或者时序问题。调NOR Flash时我用逻辑分析仪抓SPI的CS、CLK、DI、DO四线。重点关注CS低电平期间CLK的下降沿是否都对应有效的DO采样。我发现过由于GPIO复用配置错误导致CS信号在传输中间出现一个短暂的高电平毛刺Flash误以为一个命令结束了于是下面的数据全部错位。这种毛刺在示波器上看得很清楚但在逻辑分析仪上也能看到CS中间有个小的高电平脉冲。USB信号则必须用示波器配合差分探头看眼图。没有差分探头时可以用两个普通探头分别测量D和D-然后利用示波器数学通道做减法也能得到近似的差分信号。虽然精度差点但足够判断眼图是否闭合。5.2 一次典型的三外设联调故障记录我想记录一次最典型的“不确定性”排障症状是设备在实验室跑12小时都没有问题但客户现场在办公室环境有空调、有日光灯、有静电跑半天必然出现一次USB虚拟串口连接断开同时PSRAM里缓存的图片数据出现几十字节的坏数据。第一次怀疑是USB信号被干扰加了一堆滤波电容和磁珠没用。第二次怀疑是PSRAM时序余量不够降频处理也没根治。最后我上了逻辑分析仪抓USB和PSRAM的访问时间戳发现USB断开之前主控总是先收到一个外部中断这个中断来自电源管理芯片的“输入电压跌落”提示。原来是现场的USB口供电不稳电压跌落导致USB PHY工作异常进而产生一个错误中断而错误中断服务程序里因为要读状态寄存器访问了内存总线间接抢占了PSRAM的刷新时间窗口导致PSRAM数据损坏。这个案例说明了一个深刻教训外设之间的不确定性往往不来自外设本身而来自它们共同依赖的中断服务程序和电源。后来我在电源前端加了一个220uF的电容并且把USB错误中断里所有涉及外部设备的操作都改成了标志位延迟处理问题才彻底解决。5.3 设计阶段预留的调试接口所有问题定位到最后都指向设计阶段要预留测试点。我的建议是PSRAM的地址线和数据线在PCB上拉一排过孔方便飞线逻辑分析仪。NOR Flash的SPI线单独引出4个测试焊盘并且预留0欧姆电阻跳线方便断开主控调试或接Flash编程器。USB的D/D-在接口后面留串阻焊盘这样改差分阻抗或者加ESD器件时不用重做板子。主控的串口日志引脚一定要引出而且日志级别要能动态开关。很多“不确定性”问题最后都是靠日志中的时间戳发现的。这些预留看起来占地方但在调试阶段能节省至少一周时间。6. 架构层面消灭不确定性的几条经验代码和硬件一起改6.1 用状态机管理每个外设的初始化我在这个项目里最大的架构改变是把三个外设都看作独立状态机每个外设有IDLE、INIT、READY、ERROR四种状态并且由一个统一的调度器驱动。这样一个好处是任何一个外设初始化失败系统不会整体死掉而是进入错误恢复流程。比如USB枚举失败状态机自动重新初始化USB控制器最多重试三次。NOR Flash擦写超时状态机自动上报错误而不是让整个系统卡在while等WIP位。PSRAM校验失败状态机自动清空缓存并重新从Flash加载默认配置。这个改动把“不确定性”变成了“可恢复性”。用户感知到的结果不再是死机而是“设备自动重启了一次”或者“配置恢复默认了”。在大部分工业应用里这种表现是可以接受的。6.2 超时和重试机制要有但不能乱用有人会认为给所有外设操作加超时和重试问题就解决了。实际不是。重试过多反而会掩盖真实故障让现场维护变得困难。我建议重试策略分级PSRAM读写出错立即报错不重试。因为PSRAM出错通常是时序问题重试也不会成功反而可能让系统带着坏数据继续跑。NOR Flash写失败重试最多三次并且每次重试前重新发写使能。如果三次都失败记录错误码并切换到备用分区。USB枚举失败重试三次每次之间延时200ms。如果还失败进入低功耗模式等待外部拔出再插入。这个策略的核心思想是重试只适合可能被瞬态条件解决的情况不适合处理硬件确定性故障。6.3 电源和地的设计再怎么强调都不为过很多USB和PSRAM的不确定性根源都在电源噪声。PSRAM内部刷新电路对电压毛刺特别敏感电源上几十mV的跌落就可能造成刷新失败。NOR Flash虽然皮实一些但高压擦除时需要较大的电流峰值如果电源回路阻抗过高擦除时的电压跌落会影响同一电源域内的PSRAM。我给出的硬件设计清单PSRAM供电尽量独立LDO不要直接挂在主电源上除非主电源余量很足。NOR Flash的VCC与PSRAM之间加一个磁珠隔离避免擦除时的电流冲击互相干扰。USB的电源引脚要加足够大的去耦电容至少10uF加0.1uF组合。GND平面要完整千万不要在三外设之间开槽分离地平面否则回流路径被切断信号质量会急剧下降。这四条看起来简单但真按要求做的人不多。大多数“偶发问题”在布板阶段改一版就能解决比后期写一万行补丁都有效。7. 写在最后关于不确定性的一点个人体会这个项目前前后后调了两个月最后稳定跑了一个多月没复现问题。如果让我把整个过程浓缩成一句话PSRAM、NOR Flash、USB这三种外设单拎出来每一个都不复杂但它们放在一起时互相之间的时序耦合、电源耦合、总线竞争才是真正的不确定性来源。我现在的习惯是任何新板子回来的第一周不做功能开发专做外设压力测试和时序边界测试。把每个外设的时钟频率、时序参数、电源电压、温度都做成可配置的然后穷举边界组合跑测试。这样虽然前期慢但后期联调会非常顺。最后再分享一个小技巧在代码里维护一个“外设健康计数器”每次读写成功后加1失败时清零并打印在串口日志上。现场维护人员只要截图告诉我哪个计数器的值不对我基本就能猜到是哪个外设出问题了。这个技巧帮我远程解决过很多次“客户说设备又不行了”的模糊反馈。如果你正在被某个“时好时坏”的问题折磨不妨也试着把这个不确定性拆成几个可测量的维度先别急着改代码多观察一会儿它总会露出破绽的。
返回列表