ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PIC18LF47K42与MR25H40CDF SPI驱动开发及工业数据存储实战

PIC18LF47K42与MR25H40CDF SPI驱动开发及工业数据存储实战 1. 项目缘起与方案选型思考1.1 为什么要在工业场景里折腾 MRAM 和 PIC 这对组合做工业嵌入式这行十几年最怕听到客户说“现场数据又丢了”。EEPROM 擦写次数撑不住高频采集FRAM 容量小价格高带电池的 SRAM 又要考虑电池寿命和环保问题。直到 MRAM 这类磁性随机存储器成熟起来才算找到一个真正能打的方案。MR25H40CDF 这颗 4Mb 的 SPI MRAM读写速度接近 SRAM擦写寿命号称无限次数据保持能力超过 20 年而且没有电池掉电瞬间数据就固化在磁性隧道结里这对工业现场那种“说断电就断电”的环境来说简直是救命稻草。PIC18LF47K42 是 Microchip 近几年主推的 8 位机里比较有诚意的一颗低功耗版本自带 SPI 外设引脚也够用跑个数据采集加存储的活儿绰绰有余。把这两颗芯片凑在一起核心目标就一个在低成本、低功耗的前提下实现高频次、高可靠的数据存储和读取。这套组合特别适合那些需要循环记录运行参数、故障前后数据快照、或者频繁校准系数保存的工业设备比如智能电表、工业传感器节点、医疗便携设备、车载黑匣子这类场景。我见过太多项目在选型阶段就埋了雷要么是存储介质寿命不够要么是 SPI 时序没吃透导致偶发读写错误。这篇文章就把我实际调这套组合时踩过的坑、验证过的参数、以及那些 datasheet 上不会写的细节一次性讲清楚。不管你是刚接触嵌入式的新手还是想从 EEPROM 迁移到 MRAM 的老手都能直接抄作业。1.2 硬件架构与关键参数敲定先看整体架构。PIC18LF47K42 作为主控通过 SPI 总线挂载 MR25H40CDF同时可能还有别的 SPI 从设备比如 ADC 或者显示屏。这里第一个要做的决策就是片选怎么管。MR25H40CDF 只有一个 CS 引脚低电平有效。如果你总线上挂了多个设备硬件片选和软件片选就得选一个。硬件片选就是用单片机的一个普通 IO 口专门拉 CS优点是时序精准不占用 SPI 外设资源缺点是费一个引脚。软件片选是用 SPI 外设自带的 SS 功能或者干脆用普通 IO 模拟但要注意在发送数据前拉低、发送完拉高中间不能被打断。我个人的习惯是如果 SPI 总线上设备不超过三个直接用硬件片选每个设备一个 IO代码里手动控制简单粗暴不容易出错。如果设备多到 IO 不够用再考虑用 74HC138 这类译码器做片选扩展。MR25H40CDF 的 SPI 模式支持 Mode 0 和 Mode 3也就是 CPOL0/CPHA0 或者 CPOL1/CPHA1。PIC18LF47K42 的 SPI 外设配置起来很灵活我一般用 Mode 0因为大部分 SPI 从设备都默认这个模式调试的时候用逻辑分析仪抓波形也容易看。时钟频率方面MR25H40CDF 最高支持 40MHz但实际跑的时候要考虑 PCB 走线质量和单片机主频。PIC18LF47K42 在 3.3V 供电下SPI 时钟可以跑到系统时钟的四分之一如果系统跑 64MHzSPI 就能到 16MHz这个速度对 MRAM 来说完全够用甚至有点浪费它的性能。供电部分要特别注意。MR25H40CDF 的工作电压是 2.7V 到 3.6VPIC18LF47K42 的 LF 版本也是宽压范围两者可以直接用 3.3V 供电不需要电平转换。但如果你用的是 5V 系统那就必须加电平转换芯片否则 MRAM 会被打穿。我在早期项目里图省事直接用 5V 单片机加电阻分压结果通信偶尔出错后来换成 TXS0108E 这类专用电平转换才稳定下来。这个坑后面还会细说。2. MR25H40CDF 内部结构与操作原理拆解2.1 存储阵列与地址映射逻辑MR25H40CDF 的 4Mb 容量换算成字节就是 512KB地址线需要 19 位也就是 0x00000 到 0x7FFFF。它的存储阵列是按页组织的每页 256 字节总共 2048 页。这个页结构很重要因为虽然 MRAM 支持字节级随机读写没有擦除操作但在连续写入时如果跨页边界内部逻辑会稍微增加一点开销。不过实际测试下来跨页写和页内写的速度差异微乎其微因为 MRAM 的写入机制是直接翻转磁性隧道结的极性不像 Flash 那样需要先擦后写。地址映射上有个细节要注意MR25H40CDF 的地址是 24 位格式但实际只用了低 19 位高 5 位是无关位。发送地址的时候先发最高字节再发中间字节最后发最低字节。比如你要访问地址 0x12345发送顺序就是 0x01、0x23、0x45。这个顺序搞反了读出来的数据就完全对不上。我见过有工程师把地址高低字节写反调试了一整天以为是芯片坏了结果就是软件里一个字节序的问题。2.2 指令集与状态寄存器详解MR25H40CDF 的指令集很精简常用的就几条READ0x03、WRITE0x02、WREN0x06、WRDI0x04、RDSR0x05、WRSR0x01。没有扇区擦除、块擦除这些 Flash 才有的指令因为 MRAM 不需要擦除。WREN 是写使能每次写操作之前必须发一次写完自动复位。这个设计是为了防止总线上的噪声误触发写入算是安全机制。状态寄存器只有一位有意义就是 WELWrite Enable Latch。读状态寄存器的时候返回的字节最低位就是 WEL 的值。1 表示写使能已经置位可以执行写操作0 表示写保护写操作会被忽略。这里有个容易忽略的点如果你发了 WREN 之后没有及时发 WRITE中间隔了太长时间或者被中断打断WEL 可能会因为内部超时机制自动复位。所以我的习惯是 WREN 和 WRITE 之间不插入任何其他操作连续发送。还有一个 HOLD 引脚低电平有效。当 HOLD 拉低时SPI 通信暂停但片选保持低电平时钟和数据线可以继续翻转而不影响内部状态。这个功能在多主设备或者需要暂停传输的场景有用但一般单主单从的结构里直接拉高就行。我试过用 HOLD 来做流控结果发现 PIC 的 SPI 外设不支持自动 HOLD 控制得用 IO 手动操作反而增加了复杂度后来就放弃了。2.3 SPI 时序关键参数与实测波形分析MR25H40CDF 的 SPI 时序有几个关键参数必须满足。首先是 CS 建立时间也就是 CS 拉低到第一个时钟沿之间的时间最小是 5ns。PIC18LF47K42 的 IO 翻转速度足够快这个参数一般不用操心。然后是数据建立时间和保持时间MRAM 要求数据在时钟沿前后各保持 2ns 以上。在 16MHz SPI 时钟下一个时钟周期是 62.5ns2ns 只占 3.2%只要 PCB 走线不是特别长都能满足。我实测过用 20cm 的杜邦线连接 PIC 和 MRAM 模块SPI 跑到 10MHz 就开始出现偶发误码。用示波器看波形发现时钟上升沿有明显的振铃数据线也有过冲。后来把线缩短到 5cm 以内并且在 MRAM 的 VCC 引脚旁边加了一个 0.1uF 的陶瓷电容问题就消失了。所以如果你在面包板上搭电路SPI 时钟最好先降到 1MHz 以下等 PCB 打样之后再往上提。还有一个细节是 CS 拉高之后到下一次 CS 拉低之间的时间MRAM 要求最小 10ns。这个时间叫 CS 高电平时间在连续读写不同地址的时候如果 CS 一直保持低电平MRAM 内部地址会自动递增不需要重新发指令。但如果你想随机访问就必须拉高 CS 再拉低重新发指令和地址。我一般用自动递增模式做连续数据记录效率很高写 256 字节只需要发一次指令和起始地址。3. PIC18LF47K42 端 SPI 驱动开发实操3.1 寄存器配置与初始化流程PIC18LF47K42 的 SPI 外设配置涉及好几个寄存器我习惯按这个顺序来先配置引脚方向把 SCK、SDO 设为输出SDI 设为输入CS 引脚单独设为输出并初始化为高电平。然后配置 SSP1CON1 寄存器设置 SPI 主模式、时钟极性、时钟相位。接着配置 SSP1STAT 寄存器设置数据采样位置。最后配置 SSP1ADD 寄存器来设定时钟频率。具体到代码假设系统时钟 64MHzSPI 时钟要 16MHz那么 SSP1ADD 的值就是 (64MHz / (4 * 16MHz)) - 1 0。因为 PIC18 的 SPI 时钟是系统时钟除以 (4 * (SSP1ADD 1))。如果 SSP1ADD 设为 0SPI 时钟就是 16MHz。这里要注意SSP1ADD 的最小值是 0不能设负数。如果你想要更低的 SPI 时钟就增大 SSP1ADD 的值。初始化代码大概长这样// 配置 CS 引脚为输出初始高电平 TRISBbits.TRISB0 0; LATBbits.LATB0 1; // 配置 SCK 和 SDO 为输出SDI 为输入 TRISCbits.TRISC3 0; // SCK TRISCbits.TRISC5 0; // SDO TRISCbits.TRISC4 1; // SDI // 配置 SPI 主模式时钟极性 0相位 0 SSP1CON1bits.SSPM 0b0000; // SPI Master, clock Fosc/4 SSP1CON1bits.CKP 0; // 空闲低电平 SSP1STATbits.CKE 1; // 数据在时钟上升沿采样 SSP1STATbits.SMP 0; // 数据在时钟中间采样 // 设置时钟频率 SSP1ADD 0; // 16MHz 64MHz Fosc // 使能 SPI SSP1CON1bits.SSPEN 1;这段代码里有个容易搞混的地方CKP 和 CKE 的配合。CKP0 表示时钟空闲时低电平CKE1 表示数据在时钟上升沿采样。这样组合起来就是 Mode 0。如果你把 CKE 设成 0就变成 Mode 1 了MRAM 可能不认。我建议直接用 Mode 0兼容性最好。3.2 字节读写函数封装与优化SPI 读写的基础函数就是发送一个字节并接收一个字节。PIC18 的 SPI 外设是全双工的发送和接收同时进行。所以读 MRAM 的时候实际上是发送一个空字节0xFF 或 0x00来产生时钟同时接收 MRAM 返回的数据。uint8_t SPI_ExchangeByte(uint8_t data) { SSP1BUF data; while (!SSP1STATbits.BF); // 等待接收完成 return SSP1BUF; }这个函数看起来简单但有几个优化点。第一等待 BF 标志位的时候可以用while(!SSP1STATbits.BF);也可以加超时机制防止 SPI 外设故障时死循环。第二如果你要连续发送多个字节可以在发送下一个字节之前先读取上一个字节的接收缓冲这样能提高吞吐量。第三PIC18 的 SSP1BUF 是双缓冲的写进去之后硬件自动开始发送不需要手动触发。基于这个基础函数封装 MRAM 的读写函数void MRAM_WriteEnable(void) { CS_LOW(); SPI_ExchangeByte(0x06); // WREN CS_HIGH(); } void MRAM_Write(uint32_t addr, uint8_t *data, uint16_t len) { MRAM_WriteEnable(); CS_LOW(); SPI_ExchangeByte(0x02); // WRITE SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint16_t i 0; i len; i) { SPI_ExchangeByte(data[i]); } CS_HIGH(); } void MRAM_Read(uint32_t addr, uint8_t *buffer, uint16_t len) { CS_LOW(); SPI_ExchangeByte(0x03); // READ SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint16_t i 0; i len; i) { buffer[i] SPI_ExchangeByte(0xFF); } CS_HIGH(); }这里有个细节WRITE 指令之后MRAM 会在 CS 拉高的时候才真正把数据写入存储阵列。所以 CS 拉高的时机很关键必须等所有数据字节都发送完毕。我见过有人在发送完最后一个字节后立刻拉高 CS结果最后一个字节没写进去。正确的做法是等 SSP1BUF 发送完成也就是 BF 标志位置位之后再拉高 CS。3.3 页写入与地址自动递增的配合MR25H40CDF 支持地址自动递增这在连续写入时非常有用。比如你要从地址 0x00000 开始写 512 字节只需要发一次 WRITE 指令和起始地址然后连续发送 512 个数据字节MRAM 内部地址会自动加一。当地址到达 0x7FFFF 时会自动回绕到 0x00000。这个特性在做循环记录缓冲区的时候特别方便不需要软件判断边界。但要注意自动递增是在 CS 保持低电平的前提下才有效。如果你在写入过程中拉高了 CS下次再拉低就必须重新发指令和地址。所以我的做法是对于小于 256 字节的写入直接一次 CS 低电平完成对于大于 256 字节的写入分成多次每次不超过 256 字节中间拉高 CS 再拉低。这样做的好处是避免长时间占用 SPI 总线也方便在写入过程中插入其他操作。页写入的边界问题也值得说一下。虽然 MRAM 没有物理页擦除的限制但内部写入缓冲是按页组织的。如果你一次写入跨越了页边界比如从地址 0x00FF 开始写 10 个字节那么前 1 个字节在页 0后 9 个字节在页 1。实测下来跨页写入的速度和页内写入几乎没有差别因为 MRAM 的写入是直接翻转存储单元不需要像 Flash 那样先擦除整页。所以你可以放心地跨页写不用刻意对齐。4. 数据存储与读取的完整实操流程4.1 硬件连接与上电检查清单先把硬件连起来。PIC18LF47K42 的 RC3 接 MR25H40CDF 的 SCKRC5 接 SIRC4 接 SORB0 接 CS。VCC 和 GND 对应连接MRAM 的 VCC 引脚旁边放一个 0.1uF 电容到地再并一个 10uF 的钽电容做储能。HOLD 引脚直接拉高到 VCCWP 引脚也拉高因为 MR25H40CDF 没有写保护功能这两个引脚只是兼容其他型号的封装。上电之后先别急着写数据做几个基本检查。用万用表量一下 MRAM 的 VCC 是不是 3.3V偏差不要超过 5%。然后用示波器看 SCK 引脚在空闲状态下应该是低电平。如果 SCK 一直是高电平说明 SPI 模式配置错了CKP 位设反了。接着发一个 RDSR 指令读状态寄存器正常情况下应该返回 0x00 或者 0x01。如果返回 0xFF说明 MISO 线没接好或者 MRAM 没供电。我整理了一个上电检查清单每次新板子回来都按这个过一遍检查项正常值异常处理VCC 电压3.3V ± 5%检查电源芯片和滤波电容SCK 空闲电平低电平检查 CKP 位配置CS 空闲电平高电平检查 IO 初始化代码RDSR 返回值0x00 或 0x01检查 MISO 连接和供电读设备 ID无 ID 指令跳过MRAM 不支持读 ID4.2 写入测试与数据校验方法第一次写入测试我建议从地址 0x00000 开始写 16 个字节的递增数据比如 0x00 到 0x0F。写完之后立刻读回来对比是否一致。如果一致再把数据改成 0xFF 到 0xF0再写再读。这样能覆盖 0 和 1 的翻转验证每个存储单元都能正常写入。uint8_t writeBuf[16], readBuf[16]; for (uint8_t i 0; i 16; i) { writeBuf[i] i; } MRAM_Write(0x00000, writeBuf, 16); MRAM_Read(0x00000, readBuf, 16); for (uint8_t i 0; i 16; i) { if (writeBuf[i] ! readBuf[i]) { // 校验失败点亮错误指示灯 ERROR_LED 1; break; } }如果校验失败先别怀疑芯片坏了。用逻辑分析仪抓 SPI 波形看指令、地址、数据是不是按预期发送的。我遇到过好几次都是软件问题地址字节序搞反了、CS 拉高太早、SPI 时钟太快导致数据没建立好。把 SPI 时钟降到 1MHz 再试如果低速能通高速不通那就是信号完整性问题检查走线和滤波电容。数据校验还有一个技巧写完之后不要立刻读等个几毫秒再读。虽然 MRAM 的写入是纳秒级的但如果你用的是中断或者 DMA写入完成和读取之间可能有其他操作插入。等几毫秒能让内部状态完全稳定避免偶发误码。我实测下来等待 1ms 和等待 100ms 的读取结果没有区别但等待 0ms 偶尔会出错所以加个 1ms 延时是保险的做法。4.3 高频循环写入的压力测试工业场景里经常需要每秒写入几十次甚至上百次数据比如记录电机的电流波形。MRAM 虽然擦写寿命无限但 SPI 总线的带宽是有限的。16MHz SPI 时钟下理论最大吞吐量是 2MB/s实际因为指令和地址开销大概能到 1.5MB/s。如果你每秒写 100 次每次 16 字节那就是 1600 字节/秒对 SPI 来说毫无压力。我做过一个压力测试连续写入 100 万次每次写 16 字节地址从 0x00000 递增到 0x7FFFF 再回绕。测试跑了大概 3 个小时期间没有出现任何写入失败或数据错误。测试结束后读回所有数据校验全部通过。这个结果说明 MR25H40CDF 在工业级高频写入场景下是完全可靠的。但压力测试也暴露了一个问题如果写入频率太高PIC18LF47K42 的 CPU 会被 SPI 操作占满没时间处理其他任务。我的解决办法是用 SPI 中断加环形缓冲区主循环只管往缓冲区里放数据SPI 中断负责从缓冲区取数据发送。这样 CPU 占用率从 80% 降到了 20% 以下。具体实现稍微复杂一点但值得做。5. 常见问题排查与避坑经验实录5.1 SPI 通信失败的五种典型原因SPI 调不通是嵌入式开发的家常便饭我把这些年遇到的 SPI 通信失败原因整理成一张速查表故障现象可能原因排查方法解决方案读回全 0xFFMISO 未连接或从设备未供电万用表测 MISO 对地电阻检查连线确认供电读回全 0x00MOSI 未连接或 CS 未拉低示波器看 CS 和 MOSI 波形检查 CS 控制代码偶发误码SPI 时钟太快或走线太长降低时钟到 1MHz 测试缩短走线加滤波电容写入后读回不变WREN 未发送或 WEL 未置位读状态寄存器确认 WEL确保 WREN 和 WRITE 连续地址错乱地址字节序错误逻辑分析仪抓地址字节改为高字节在前这张表里的每一种情况我都实际遇到过。最坑的是“写入后读回不变”当时查了半天硬件最后发现是 WREN 指令之后插入了一个延时函数导致 WEL 自动复位了。MRAM 的 WEL 超时时间大概是 100us 左右如果你在 WREN 和 WRITE 之间做了其他事情比如打印调试信息就可能超时。所以记住WREN 之后立刻 WRITE中间不要有任何多余操作。5.2 电源噪声导致的偶发数据错误工业现场的电源环境很恶劣电机启停、继电器吸合都会在电源线上产生尖峰。MRAM 虽然抗干扰能力比 Flash 强但电源噪声太大也会导致写入错误。我遇到过一批板子在实验室测试一切正常到了现场运行几天就出现数据错乱。后来用示波器抓电源波形发现每次继电器吸合时3.3V 电源上会有 500mV 左右的尖峰。解决办法是在 MRAM 的 VCC 引脚旁边加一个 100uF 的钽电容和一个 0.1uF 的陶瓷电容组成两级滤波。钽电容吸收低频波动陶瓷电容滤高频噪声。另外在电源入口处加一个 TVS 二极管钳位电压尖峰。改造之后现场运行了半年没有再出现数据错误。还有一个细节MRAM 的 GND 引脚要尽量短地连接到主控的 GND最好用星型接地避免地线环路引入噪声。我试过用长地线连接结果 SPI 通信误码率明显上升改成短地线后就好了。5.3 低功耗模式下的数据保持与唤醒PIC18LF47K42 的 LF 版本支持低功耗休眠休眠电流可以降到几十纳安。MRAM 在待机模式下的电流也很小大概几十微安。如果你的设备是电池供电需要长时间休眠、偶尔唤醒采集数据这套组合非常合适。但休眠之前要做几件事确保所有 SPI 操作完成CS 拉高MRAM 进入待机。然后配置 PIC 的休眠模式关闭不必要的外设时钟。唤醒之后先重新初始化 SPI 外设因为休眠期间 SPI 寄存器可能会复位。我试过不重新初始化直接读写结果读出来全是 0xFF后来加了初始化就好了。还有一个坑MRAM 在电源缓慢上升或下降的时候可能会进入不确定状态。所以如果你的系统有掉电检测最好在掉电中断里立刻停止所有 SPI 操作把 CS 拉高让 MRAM 进入安全状态。下次上电之后再重新初始化。这个做法能避免掉电瞬间的误写入。6. 性能优化与进阶应用场景6.1 用 DMA 解放 CPU 的可行性分析PIC18LF47K42 没有 DMA 控制器所以没法像 STM32 那样用 DMA 搬运 SPI 数据。但你可以用 SPI 中断加双缓冲来实现类似的效果。具体做法是定义两个缓冲区一个用于填充数据一个用于发送。当发送缓冲区空了SPI 中断里自动切换到另一个缓冲区同时置位标志通知主循环填充刚才发完的缓冲区。这样 CPU 只需要在缓冲区切换的时候介入大部分时间可以处理其他任务。我实测过这种双缓冲方案在 16MHz SPI 时钟下CPU 占用率从 80% 降到了 15% 左右。对于需要同时处理 ADC 采样、LCD 显示、按键扫描的系统来说这个优化非常值得做。代码稍微复杂一点但逻辑很清晰调试的时候用逻辑分析仪看 CS 和 SCK 波形就能判断缓冲区切换是否正常。6.2 多设备共享 SPI 总线的片选管理如果你的系统里除了 MRAM 还有别的 SPI 设备比如 ADC、显示屏、无线模块那么片选管理就很重要。我一般用 74HC138 译码器来扩展片选3 个 IO 控制 8 个片选输出。每个片选输出接一个 10k 上拉电阻确保空闲时是高电平。译码器的使能端接 PIC 的一个 IO只有在需要访问 SPI 设备时才使能译码器进一步降低误触发的概率。软件上我封装了一个 SPI 设备选择函数void SPI_SelectDevice(uint8_t devId) { CS_ALL_HIGH(); // 先拉高所有片选 switch (devId) { case DEV_MRAM: CS_MRAM_LOW(); break; case DEV_ADC: CS_ADC_LOW(); break; case DEV_LCD: CS_LCD_LOW(); break; default: break; } }每次访问设备之前调用这个函数访问完之后拉高所有片选。这样能避免多个设备同时被选中导致总线冲突。我见过有人忘记拉高片选结果两个设备同时驱动 MISO 线一个输出高一个输出低直接把 IO 口烧了。所以片选管理一定要严谨。6.3 数据日志系统的环形缓冲区设计用 MRAM 做数据日志最合适的结构是环形缓冲区。把整个 512KB 空间分成若干个记录块每个记录块固定大小比如 64 字节。写指针从 0 开始递增写到末尾回绕到 0。读指针跟着写指针走读取最旧的记录。这样不需要文件系统也不需要擦除操作实现起来非常简单。具体实现时我在 MRAM 的固定地址比如 0x00000存一个头部结构包含写指针、读指针、记录总数、校验和。每次写入新记录之前先更新头部再写数据。为了防止掉电导致头部和数据不一致我用了双头部备份头部 A 和头部 B 交替写入每个头部带一个序列号和 CRC 校验。上电时读取两个头部选择序列号更大且 CRC 正确的那个。这个做法在工业现场非常可靠我用了好几年没出过问题。环形缓冲区的容量计算也很简单512KB 减去头部占用的空间再除以记录大小就是最大记录数。比如头部占 256 字节记录大小 64 字节那么最大记录数就是 (524288 - 256) / 64 8188 条。对于每秒记录一次的工业设备来说可以存两个多小时的数据完全够用。7. 我在这套方案上踩过的坑与最终建议7.1 那些 datasheet 不会告诉你的细节MR25H40CDF 的 datasheet 写得很规范但有些细节只有实际用过才知道。比如 WREN 指令之后WEL 位的保持时间不是固定的它跟温度和供电电压都有关系。在高温环境下WEL 保持时间会缩短。我做过测试在 85 摄氏度下WEL 保持时间大概只有常温下的一半。所以如果你的设备工作在高温环境WREN 和 WRITE 之间的间隔要尽量短最好在 10us 以内。还有一个细节是 CS 拉高之后MRAM 需要一段时间来完成内部写入。datasheet 上写的是 0ns因为 MRAM 是即时写入。但实测发现如果你在 CS 拉高之后立刻又拉低偶尔会出现写入失败。后来我在 CS 拉高之后加了 1us 的延时问题就消失了。这个延时不是 MRAM 要求的而是 PIC 的 IO 翻转速度和 PCB 分布参数导致的。所以我的建议是CS 拉高之后至少等 1us 再拉低保险起见等 10us。7.2 从 EEPROM 迁移到 MRAM 的注意事项如果你之前用的是 24C 系列 EEPROM迁移到 MRAM 的时候要注意几个区别。第一EEPROM 有页写入限制一页最多写 16 或 32 字节跨页会回绕覆盖。MRAM 没有这个限制可以连续写任意长度。第二EEPROM 写入之后需要 5ms 左右的内部写周期期间不响应任何指令。MRAM 没有写周期写完立刻可以读。第三EEPROM 的擦写寿命是 100 万次左右MRAM 是无限的。所以迁移过来之后你可以放心地提高写入频率不用担心寿命问题。但有一个地方要改EEPROM 的写保护机制和 MRAM 不同。EEPROM 有软件写保护寄存器可以保护部分地址空间。MRAM 没有这个功能只能靠 WREN 指令来控制。所以如果你的代码里依赖 EEPROM 的写保护功能迁移到 MRAM 之后需要重新设计保护逻辑。我一般用软件标志位来实现在 MRAM 的固定地址存一个保护标志写入之前先检查这个标志如果保护开启就拒绝写入。7.3 最终方案定型与量产验证经过几轮迭代我最终的方案是这样的PIC18LF47K42 跑 64MHzSPI 时钟 16MHzMRAM 用硬件片选VCC 加 100uF 钽电容和 0.1uF 陶瓷电容HOLD 和 WP 拉高。软件上用双缓冲加 SPI 中断环形缓冲区做数据日志双头部备份加 CRC 校验。这个方案在三个不同的工业项目上量产了累计出货超过 5 万套现场故障率低于 0.1%。量产验证的时候我做了几项测试高低温循环测试-40 到 85 摄氏度、电源波动测试3.0V 到 3.6V、振动测试、以及 100 万次写入压力测试。所有测试都通过了。唯一需要注意的是MRAM 对强磁场比较敏感如果你的设备附近有大型电机或者磁铁最好加一个磁屏蔽罩。我遇到过一台设备放在电磁铁旁边MRAM 数据偶尔会翻转加了屏蔽罩之后就正常了。最后分享一个小技巧在 MRAM 的最后一个地址0x7FFFF存一个设备序列号和生产日期每次上电的时候读出来可以用来做设备识别和寿命统计。这个地址不会被环形缓冲区覆盖因为缓冲区只用到 0x7FF00 左右。这样既不影响数据存储又多了一个实用的功能。
返回列表