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

资讯详情

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

MR25H40CDF MRAM与PIC18F45K42的工业掉电安全存储方案详解

MR25H40CDF MRAM与PIC18F45K42的工业掉电安全存储方案详解 凌晨两点控制柜里一阵电压闪断第二天早上操作工发现昨天标定的几十个配方参数全部回归出厂值生产计划整个被打乱。这种事故我碰到过不止一次根子往往不在MCU而在存储介质电池备份SRAM电池耗尽、Flash被反复擦写磨穿、EEPROM写指令还没完成就掉了电。要解决工业现场这些存储和读取数据的问题我现在的主力方案是MR25H40CDF加PIC18F45K42这对组合。MR25H40CDF是一颗4Mbit的SPI接口MRAM掉电不丢数据、写入不需要擦除、擦写寿命几乎无限PIC18F45K42则是Microchip的中端8位MCUSPI、掉电检测、CRC这些外设齐全非常适合做工业嵌入式控制板。这篇文章我会从选型原理、硬件连接、SPI时序、数据可靠性设计到实测踩坑完整梳理这套方案给正在做选型和写驱动的同行一个可以照抄的参考。1. 为什么工业数据存储会选用MR25H40CDF这类MRAM而不是Flash或EEPROM1.1 工业现场最常见的存储阵亡方式先说我在现场见过最多的三种存储失效。第一种是电池备份SRAM没电。很多老一代控制板用SRAM加锂电池保存配方和标定值平时一切正常但电池总有过放、接触不良、寿命耗尽的一天。问题是这类电池失效往往是渐进的设备可能在低电压下继续运行几个月数据时好时坏最坑人。等操作工发现参数总是存不住生产已经受影响了。第二种是EEPROM寿命耗尽。市面上常见的I2C/SPI EEPROM擦写寿命标称在10万到100万次之间。听起来不少但工业设备如果每个工单都要更新一批参数再加上故障日志频繁追加几年时间就可能磨到寿命边缘。而且EEPROM写一个字节背后是充电陷阱的电荷进出写多了会物理损坏没有任何 wear leveling 能救回来。第三种是SPI NOR Flash被写穿。Flash的寿命通常也是10万次左右但真正麻烦的是它的块擦除机制。要改一个字节必须先读整个扇区、在内存里改、整块擦除、再整块写回这中间任何一个环节掉电都可能丢数据。FLash还要做坏块管理和擦写均衡一个八位MCU项目里维护这套逻辑代码量不小。MR25H40CDF这类MRAM就是冲着这三个痛点来的它非易失不需要电池它的写寿命高达10的14次方比EEPROM高七八个数量级它写入时不需要擦除没有块擦除这个概念改哪个地址就写哪个地址逻辑大幅简化。1.2 MRAM的存储原理和它与Flash的本质差异MRAM的核心是磁性隧道结结构可以简化理解为两层磁性材料夹一层极薄的绝缘层。其中一层磁性方向固定另一层方向可以通过写电流翻转。两层磁性方向一致时隧道结电阻低可以定义为0方向相反时电阻高定义为1。读取时通过检测电阻大小判断存储的是0还是1。这里有个很关键的点它存的是磁化方向不是电子电荷。电荷会漏、会泄放所以Flash需要复杂的擦写机制来补偿电荷衰减而磁化方向在常温下非常稳定断电后依然保持。这也决定了MRAM没有擦除这个动作——写0还是写1都是直接翻转自由层磁化方向而不是在绝缘层里注入或拉出电荷所以它的寿命才这么长。打个比方Flash和EEPROM像是用橡皮不断擦改一张纸上的铅笔字纸擦多了会起毛、会破MRAM则像翻转一排小磁铁不管翻多少次磁铁的磁性不会因此变弱。这个基本原理差异决定了工业存储选型时的所有后续成本。1.3 一张表看懂MR25H40CDF和其他方案的关键指标我用实际选型时关心的维度列了一张对比表特性SPI EEPROMSPI NOR Flash电池SRAMMR25H40CDF容量范围通常小Kbit级大Mbit级中大4Mbit(512KB)非易失方式电荷存储电荷存储依赖外部电池磁化方向写寿命约10万~100万次约10万次无限但不持久10的14次方次写前是否擦除否但写慢需整块擦除不需要不需要单字节随机写支持但ms级不直接支持支持ns级支持命令级写操作掉电风险可能写坏字节整页/整块风险有数据丢失风险几乎无风险坏块管理不需要必须不需要不需要数据保持约10年约20年电池寿命决定超过20年工业温度范围有有一般有MR25H40CDF在8引脚DFN封装里塞进了512KB这个容量不大但对于参数表、配方、日志、标定值这些工业数据足够了。它采用标准SPI接口支持Mode 0和Mode 3官方标称最高时钟可以跑到40MHz工作温度覆盖工业级范围。SPI接口的好处是和大多数MCU都能直接对接不需要额外的地址线和数据线。1.4 PIC18F45K42在这个组合里的角色存储芯片再好也要MCU会指挥。选PIC18F45K42不是因为它性能有多炸裂而是它在工业场景里几个关键点都踩对了。第一它有灵活的外设引脚选择也就是PPS机制。SPI的SCK、SDO、SDI可以映射到一组引脚中的任意GPIO画PCB时不用为固定引脚绕线布局自由度大很多。第二它的片上外设很全。除了两个串口和SPI/I2C还有高精度振荡器、掉电复位BOR、窗口看门狗、CRC模块、过零检测等。工业设备最怕程序跑飞和电压跌落这两个坑正好被BOR和看门狗兜住。第三它的温度范围和供货稳定。8位MCU在工业控制领域积累了几十年工具链成熟一个工程师今天写驱动明天就能交付产线。不是所有项目都需要上Cortex-M存储类应用对算力要求不高更多是要求时序稳定、逻辑简单。我自己常和它搭档的场景包括智能传感器标定参数保存、工业相机参数掉电保存、机器人示教数据的备份、边缘控制器里的配方管理。核心逻辑都一样MCU通过SPI把运行参数、工艺数据、故障记录写进MRAM掉电后不丢下次上电再读出来。2. 硬件连接把PIC18F45K42和MR25H40CDF正确接在一起2.1 MR25H40CDF的引脚信号与工业设计建议MR25H40CDF是8引脚DFN封装内部信号其实非常简练。除了电源和地核心引脚就6个CS#、SCK、SI、SO、WP#、HOLD#。CS#片选低有效。所有SPI指令都是靠CS#拉低启动、拉高结束的这个引脚必须由MCU的GPIO精确控制。SCKSPI时钟输入由主设备提供。SI串行数据输入接MCU的SDO输出引脚。SO串行数据输出接MCU的SDI输入引脚。WP#写保护输入低电平时启用块保护。这个引脚如果悬空受到噪声干扰拉低会让你的写指令全部失效。HOLD#暂停输入低电平时暂停SPI通信。不用的话也必须处理不能悬空。这里的设计建议很明确WP#和HOLD#各接一个10k电阻上拉到VDD不要偷懒省掉。我见过太多板子把这两个引脚空着结果现场出现偶尔写不进、偶发通信卡死的疑难故障查到最后都是悬空引脚被干扰。MR25H40CDF的供电是3.3VPIC18F45K42同样是3.3V供电所以所有信号线可以直接相连不需要电平转换。电源去耦上VDD引脚就近放一个100nF陶瓷电容如果再放一个4.7uF的钽电容做低频去耦会更好特别是板上还有其他大电流负载的时候。2.2 与PIC18F45K42的SPI引脚连接和PPS映射建议PIC18F45K42的SPI通过MSSP外设实现在K42系列里SPI信号可以通过PPS寄存器灵活映射到不同的引脚上。手动配置PPS的数值需要查数据手册我用MCC生成工程的时候就直接在图形界面里勾选。一个我常用的映射示例PIC18F45K42引脚方向MR25H40CDF引脚RC3 (SCK)输出SCKRC5 (SDO)输出SIRC4 (SDI)输入SORC2 (GPIO)输出CS#这里特别提醒CS#不要接到MSSP的硬件SS引脚上。硬件SS有自己的自动片选逻辑触发时机和我们想要的整条指令期间CS保持低不完全一致。我都是用一个普通GPIO来拉CS软件想什么时候拉低就什么时候拉低指令传完想什么时候拉高就什么时候拉高可控性最好。另外一个容易被忽略的点是SPI总线的共享。如果板上还有其他SPI从设备比如ADC或者显示驱动它们可以共用SCK、SI、SO但CS#必须各用各的GPIO。切换设备时要确保前一个设备的SCK时钟已经完全结束CS拉高之后再拉低下一个设备的CS否则容易把伪时钟灌进某个从设备。2.3 PCB布局和信号完整性要点MRAM是存储芯片但它在高速SPI下同样受信号完整性影响。以下几个细节在画板阶段就定了生死。芯片位置越靠近MCU越好。8引脚DFN封装本身很小完全可以直接放在MCU附近让SPI走线长度控制在10mm以内。如果必须通过排线或长距离走线连接每根信号线上串联22到33欧姆的电阻能明显抑制振铃。SCK信号不要和继电器驱动线、电机驱动线、开关电源的反馈走线平行长距离布线。这些高频高压噪声很容易耦合进时钟线导致SPI误码。如果实在绕不开中间加一条地线隔离。电源走线也很关键。MRAM和MCU共用一个3.3V轨时去耦电容的地要直接回到芯片的地焊盘不要和MCU的数字地绕一大圈。高速翻转的时候地弹电压会在SPI信号上产生毛刺这些毛刺最终表现为随机的一位翻转错误很难排查。还有一个经验值SPI时钟先不要一上来就冲刺40MHz。工业第一优先是稳定我一般先配成1MHz到5MHz把读写跑通再用示波器看波形没问题再逐步往上提。实际项目里10MHz左右已经能满足绝大多数日志记录和参数保存需求。3. PIC18F45K42的SPI初始化从MCC到寄存器级的理解3.1 用MCC生成工程后还要改什么Microchip的MCC也就是MPLAB Code Configurator是配置PIC18F45K42最快的方式。大多数情况下我不建议手写寄存器初始化而是让MCC把时钟、SPI、PPS、GPIO这些基础配置一次性生成好再把注意力放在MRAM的指令流程上。在MCC里操作大概是这样新建PIC18F45K42工程选择内部振荡器配置系统时钟然后添加MSSP1模块作为SPI主模式设置成8位数据长度、SPI Mode 0、主模式分频系数根据总线时钟算出目标波特率。PPS映射在Pin Module里勾选MCC会自动把SCK、SDO、SDI分配到引脚上同时生成对应的TRIS寄存器配置。MCC生成的底层函数通常叫SPI1_Initialize、SPI1_ExchangeByte这类名字。不同版本函数名可能有差异但本质就是一个字节交换函数发送一个字节的同时接收一个字节。后续所有MRAM操作都是基于这个字节交换函数搭起来的。如果你不用MCC也可以手动配置寄存器但要小心核对数据手册的寄存器位定义。3.2 SPI Mode 0的配置本质CKP、CKE和CPOL、CPHA的映射关系很多人在SPI模式上栽跟头就是因为把MCU的CKP/CKE位和通用的CPOL/CPHA搞混了。SPI Mode 0对应的通用定义是CPOL0、CPHA0。含义是SCK空闲时为低电平数据在第一个时钟边沿也就是上升沿被采样。对MR25H40CDF这类存储器来说Mode 0和Mode 3都支持选Mode 0最省心因为它和大多数MCU的默认配置一致。PIC的MSSP模块把极性放在CKP位把相位放在CKE位。问题在于不同PIC系列里CKE位的1和0对应的相位正好相反照抄别的芯片代码很容易翻车。我的建议是不要凭记忆写用逻辑分析仪或者示波器抓一下波形。正确的Mode 0波形是这样的SCK空闲是低电平SI线上的数据在SCK上升沿之前稳定下来在上升沿被采样然后在下降沿之后变化SO线上的数据同样遵循这个时序。如果你读MRAM读出来全0xFF或者全0x00先别怀疑芯片坏了用示波器看SCK空闲电平。如果空闲是高电平说明你配成了Mode 3也就是CPOL1如果数据错位一位多半是CKE相位配反了。这两个问题占了SPI通信故障的大头。3.3 用于MRAM读写的底层字节交换函数无论MCC生成的函数叫什么底层逻辑都是同一个uint8_t mram_spi_transfer(uint8_t byte) { // 把要发送的字节写入SPI发送缓冲 // 等待SPI接收缓冲满说明一个字节传输完成 // 读回接收缓冲这个字节实际上是从机在这8个时钟里移出来的数据 // 如果使用MCC生成代码直接调用 return SPI1_ExchangeByte(byte); }SPI是全双工协议MCU发一个字节的同时必然收到一个字节。所以读MRAM数据时主机不能停下来等而是要继续发0x00把SCK时钟送出去SO线上的数据才会被一位一位移出来。这个发送垃圾字节换取目标数据的套路是所有SPI从设备读取的标准玩法。如果你要看寄存器级的实现关键就两个寄存器发送缓冲寄存器SPI1TXB接收缓冲寄存器SPI1RXB。写SPI1TXB触发传输等接收标志位置位后读SPI1RXB。我在实际代码里通常不直接操作寄存器位而是统一封装成mram_spi_transfer这个函数这样后续读写指令的代码可以写得很干净。3.4 为什么要用普通IO控制CS#而不是硬件SS前面提到过CS#要用GPIO这里把原因讲透一点。硬件SS引脚在MSSP模块里有一套自动逻辑比如在多主模式下检测总线冲突或者在特定配置下随每次数据传输自动翻转。这个自动行为对读取状态寄存器、连续写缓冲区这些多字节命令非常不友好。你希望的是一个完整命令包传输期间CS保持低电平传完再拉高中间不允许任何意外。用普通GPIO后整个片选时序完全由我掌控// 发送一个完整命令的模板 MRAM_CS_LOW(); mram_spi_transfer(CMD); // 命令字节 mram_spi_transfer(addr_high); // 地址高字节 mram_spi_transfer(addr_mid); // 地址中字节 mram_spi_transfer(addr_low); // 地址低字节 mram_spi_transfer(data); // 数据字节 MRAM_CS_HIGH();只要CS拉低和拉高之间不混入其他SPI设备的操作这条指令就是原子的。中断来了也无所谓SPI时钟停了MRAM的状态不会因为CS仍然为低而损坏等中断处理完继续把剩余字节发完就行。4. MR25H40CDF读写时序指令集与实际代码4.1 必须先写使能WREN指令与状态寄存器MR25H40CDF和大多数SPI存储器一样写入数据之前必须先发写使能指令。这是存储器的写保护机制之一防止系统上电瞬间或者程序跑飞时乱发的SPI信号把存储区冲掉。写使能命令是0x06。用法很简单拉低CS发0x06拉高CS。之后才能发写指令。写操作完成后MRAM内部的写使能锁存器会自动复位下一次写之前又要重新发0x06。判断写使能是否生效可以读状态寄存器命令是0x05。状态寄存器里有写使能锁存位WEL正常发完WREN后读回来这个位应该是1。第一次调试时我建议把这个逻辑写成调试函数在写操作之前打印一下状态值能省很多排查时间。对应地写禁用指令是0x04可以在不需要写操作时主动关闭写使能防止意外改写。工业产品出货前如果不需要在线升级参数可以考虑在出厂校验完成后发一条写禁用指令。4.2 单字节读写READ与WRITE全流程单字节读取使用0x03命令。CS拉低后依次发送0x03、三字节地址然后发一个0x00产生时钟从SO线上读回数据最后拉高CS。#define MRAM_CMD_WREN 0x06 #define MRAM_CMD_WRDI 0x04 #define MRAM_CMD_RDSR 0x05 #define MRAM_CMD_READ 0x03 #define MRAM_CMD_FAST_READ 0x0B #define MRAM_CMD_WRITE 0x02 uint8_t mram_read_byte(uint32_t addr) { MRAM_CS_LOW(); mram_spi_transfer(MRAM_CMD_READ); mram_spi_transfer((addr 16) 0xFF); mram_spi_transfer((addr 8) 0xFF); mram_spi_transfer(addr 0xFF); uint8_t val mram_spi_transfer(0x00); MRAM_CS_HIGH(); return val; }单字节写入使用0x02命令。区别在于写之前要先发WREN。void mram_write_byte(uint32_t addr, uint8_t val) { mram_write_enable(); MRAM_CS_LOW(); mram_spi_transfer(MRAM_CMD_WRITE); mram_spi_transfer((addr 16) 0xFF); mram_spi_transfer((addr 8) 0xFF); mram_spi_transfer(addr 0xFF); mram_spi_transfer(val); MRAM_CS_HIGH(); } void mram_write_enable(void) { MRAM_CS_LOW(); mram_spi_transfer(MRAM_CMD_WREN); MRAM_CS_HIGH(); }注意WREN和WRITE之间CS必须经历一次完整的拉高。如果发完WREN不拉高CS直接发WRITE很多存储器会当作同一条命令的一部分导致写使能没生效。这个细节是SPI存储操作的通用要求。和Flash不同MRAM的写操作不会动不动就卡几十毫秒。它没有页编程也不需要擦除0x02命令内部就是直接改磁化方向物理动作是纳秒级的。在一个CS低电平窗口内0x02命令后面可以连续跟多个数据字节MRAM会按地址自增连续写入而且没有页面边界限制。这一点在做日志存储时特别好用。4.3 用FAST_READ提高连续采集吞吐MR25H40CDF还支持快速读指令0x0B。相比普通读0x03它多了一个dummy字节也就是在地址之后、数据之前主机需要额外发一个字节制造延迟然后才能开始读数据。uint8_t mram_fast_read_byte(uint32_t addr) { MRAM_CS_LOW(); mram_spi_transfer(MRAM_CMD_FAST_READ); mram_spi_transfer((addr 16) 0xFF); mram_spi_transfer((addr 8) 0xFF); mram_spi_transfer(addr 0xFF); mram_spi_transfer(0x00); // dummy uint8_t val mram_spi_transfer(0x00); MRAM_CS_HIGH(); return val; }如果要连续读一大段日志比如采集系统掉电前记录的几百个数据点就不需要每读一个字节拉一次CS。可以一个CS窗口内用FAST_READ命令加起始地址然后连续发0x00SO线上会源源不断地把后续地址的数据移出来。这样可以大幅减少命令开销让SPI总线利用率接近理论峰值。我在实际项目里还碰到过一种情况上位机通过串口请求一大包历史数据MCU要把MRAM里512字节日志一次性读出来回传。这时候连续读的优势非常明显把SCK配到10MHz512字节也就零点几毫秒的事。4.4 连续读写下的CS时序设计CS时序是整个SPI通信最容易出错的地方。所有指令都必须满足一个规则CS下降沿表示指令开始CS上升沿表示指令结束。中间无论传多少个字节CS都不能拉高。在MCU代码里只要保证每个mram_spi_transfer调用之间没有其他器件抢占SPI总线这个时序天然成立。但要注意一种情况如果CPU时钟很高SPI时钟也很快两个字节之间的GPIO翻转产生的延迟反而可能不够导致CS和SCK之间的时序竞争。解决方案是在CS拉低后、发第一个字节之前加一个极短的延时比如几条NOP。我自己的代码里经常这样处理MRAM_CS_LOW(); NOP(); NOP(); NOP(); // 建立时间余量 mram_spi_transfer(...); // ... MRAM_CS_HIGH();这个小细节在高时钟下能避免不少偶发问题。CS建立时间虽然不长但现场电磁环境复杂多留一点余量总没错。5. 工业级数据管理的进阶协议、校验、掉电安全5.1 在MRAM上组织参数区和日志区存储芯片能用只是一方面怎么组织数据才见功力。512KB空间对参数保存来说很宽裕但也不能乱用。我一般会把存储空间划分成几个明确区域。#define PARAM_BLOCK_1_ADDR 0x00000 #define PARAM_BLOCK_2_ADDR 0x00100 #define APP_LOG_ADDR 0x100000x00000到0x001FF这块放参数双备份区每个参数块256字节。0x10000以后放环形日志区每条日志记录32字节按顺序一直写写到底部回绕到起始地址。参数区为什么要双备份因为工业现场存在写入中途掉电这个现实。MRAM写一两个字节几乎是瞬时的但一个参数块可能有几十字节如果写到一半电源断了这一块的数据就处于半新半旧状态。双备份区能在上电时挑选出一份完整有效的副本。MRAM高耐久的好处在这里也体现出来了。日志区用环形覆盖写不需要像Flash那样考虑擦除次数和回收策略。日志写满一圈后直接覆盖最旧记录这个对MRAM来说是基本操作完全不需要怕磨损。5.2 双备份与版本号避免写入中断导致的半新半旧双备份的原子更新协议可以很简单核心思路是先写数据和校验最后写提交标记。定义一个参数块typedef struct { uint32_t magic; // 固定魔数用来判断块是否有有效数据 uint16_t seq; // 版本号写入递增 uint16_t crc; // 数据区CRC16校验 uint8_t data[240]; // 实际参数区 } TAppParamBlock;写一个新参数块的流程读出当前有效的参数块确定哪个区是旧区。在另一个区里先写入data和crc字段。计算新的seq 旧seq 1。最后写入seq和magic。关键就在最后一步。如果掉电发生在第2步那么新区的magic还是旧的CRC也是旧的上电时它不会通过校验系统会回退到另一个区继续用旧参数。也就是说掉电窗口从可能留下无法判断的脏数据缩小成了最多留下一个不完整的备用区主区不受影响。这个设计方案对Flash也成立但Flash因为有块擦除实现复杂度高得多。MRAM直接按字节改不需要擦除双份备份的代码量很少所以我非常推荐在这个组合上做双备份。5.3 CRC16校验当工业现场出现总线误码时SPI通信在实验室里跑得很顺不代表现场没问题。继电器吸合、电机启停带来的瞬态干扰完全可能在总线传输过程中让某一位翻转。所以数据经过CRC校验再落盘是工业存储的底线要求。我用的CRC16实现很常规多项式0x1021初始值0xFFFFuint16_t crc16_update(uint16_t crc, uint8_t byte) { crc ^ (uint16_t)byte 8; for (uint8_t i 0; i 8; i) { if (crc 0x8000) crc (crc 1) ^ 0x1021; else crc 1; } return crc; } uint16_t crc16_buf(const uint8_t *buf, uint16_t len) { uint16_t crc 0xFFFF; while (len--) crc crc16_update(crc, *buf); return crc; }这个函数在8位MCU上跑240字节的数据耗时也就几百微秒能接受。它验证的不只是MRAM芯片本身还包括SPI总线传输过程、MCU内部RAM到外设的数据链路。哪怕MCU读回的数据在总线上被干扰CRC也能把它揪出来。如果应用场景对安全要求更高可以改成CRC32或者加一个地址掩码让每条记录绑定自己的存储地址防止错位写入。不过大多数工业场景CRC16已经够用。5.4 掉电瞬间的保护BOR、写时序余量MRAM写入不需要擦除这个特性在掉电保护上天然有优势。Flash写一个扇区掉电整个扇区可能处于未知状态MRAM写一个字节掉电最多这个字节不确定不会波及其他地址。但工程上不能只依赖这个优势还要做主动防护。PIC18F45K42内置BOR掉电复位我建议把BOR电压设置得略高于MRAM的工作下限。这样电源跌落时MCU会在MRAM还没进入不稳定工作区之前先复位避免在临界电压下发指令。我在一个需要紧急保存参数的项目里做过这样的设计电源输入端放一个大电容蓄能MCU用外部中断监控电源电压检测到掉电后马上把最关键的几个字节写入MRAM的固定地址。因为MRAM写入是微秒级这批数据往往能在电压跌出工作范围之前写进去。而同样的需求如果放在Flash上等块擦除做完电压早就没了。还有一点经验不要在掉电中断服务函数里做复杂操作。掉电打断正在进行的SPI传输会留下半截命令虽然MRAM不会损坏但数据可能写了一半。我的原则是掉电中断里只写几个关键字节其他参数靠双备份机制靠下一次上电自动恢复。6. 实测踩坑清单这些细节会让数据从能读变成可靠6.1 SPI模式配置反了最常见的读全FF我第一次在新板子上调MR25H40CDF读出来的数据全是0xFF当时第一反应是芯片虚焊。后来用示波器一抓SCK波形发现SCK空闲电平是高MCU默认被配成了Mode 3而MRAM匹配的是Mode 0。把MCC里的SPI模式改成Mode 0数据立刻正常。如果你手边没有示波器也可以这样判断让程序发一条比较规则的波形比如循环发0x55然后观察SI线上数据是否和时钟沿对齐。SI数据在SCK上升沿时已经稳定说明相位对的如果在下降沿才稳定说明相位反了。这类问题的特征非常明显往往板子所有地方看起来都对但就是读不到数据。排查顺序我建议是电源、CS信号、SPI模式、SCK波形、SI/SO方向最后再怀疑芯片。6.2 WP#和HOLD#悬空导致的诡异写不进有个项目调试了足足两天现象是这样的能读不能写。发WREN后再发WRITE读回来还是旧数据。用逻辑分析仪看指令字节完全正确状态寄存器里的WEL位也置位了没有任何异常。最后发现MR25H40CDF的WP#引脚悬空。MRAM的块保护有一个默认状态当WP#被意外拉低时整个存储区处于保护状态写指令会被忽略。实验室里悬空引脚可能侥幸没被干扰但现场环境下一旦有毛刺写保护就被随机触发。从那以后我对所有带使能、保护、暂停这类控制引脚的芯片规定一律接上拉或下拉电阻绝不悬空。MR25H40CDF的WP#和HOLD#全都10k上拉到VDD这样不管上电瞬间还是运行中都不会进入意外保护或者暂停状态。6.3 高速SPI下的振铃与误码把SPI时钟调到30MHz以上的时候我开始遇到偶发误码。同样的程序跑100遍可能有一遍读到某个字节错了。用示波器在MRAM引脚旁边看SI和SCK波形能看到明显的过冲和振铃。这是典型的信号完整性问题。解决方法很朴素SPI时钟降到10MHzSCK和SI线上各串一个33欧姆电阻问题消失。工业应用不是跑分现场10MHz读512KB数据只需要零点几毫秒速度完全够用但稳定性差一个数量级。我也试过用逻辑分析仪去抓偶发错误但逻辑分析仪本身探头的寄生电容会影响波形抓到的东西参考意义有限。还是示波器在芯片引脚上直接量最靠谱。6.4 DFN8封装的手工焊接与验证MR25H40CDF是8引脚DFN封装底部还有散热焊盘手工焊接比普通SOP要小心。我实验室的做法是先给PCB焊盘均匀上一层薄锡放上芯片对准用热风枪280到300摄氏度、低风速加热焊锡熔化后轻压芯片几秒钟。温度不要过高时间不要过长否则容易把内部芯片烤出隐患。焊完之后不要急着上电先做两个检查。第一万用表量VDD和GND之间有没有短路第二用二极管档量每个引脚对地的压降对比正常板子虚焊的引脚因为开路压降数据对不上。上电后我习惯跑一轮快速验证程序对全片依次写0x55、0xAA再读回来比对最后随机写几个地址做CRC校验。这个测试对Flash是奢侈的因为会消耗擦写寿命对MRAM来说10的14次方次写寿命面前完全不值一提跑多少次都不心疼。验证程序跑完我还会再把板子放到高温环境里连续读写一晚上第二天回来对比MRAM里的数据和上位机记录的基准值。MRAM在这种场景下表现确实稳至少到目前为止我在现场还没遇到过因为存储介质本身导致的返修。如果你也在用这套组合建议把上面几个坑提前踩一遍能省下不少现场时间。
返回列表