ARTICLE DETAIL

资讯详情

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

PIC32MX360F512L驱动MR25H40CDF MRAM:SPI接口与掉电保护实战

PIC32MX360F512L驱动MR25H40CDF MRAM:SPI接口与掉电保护实战 1. 为什么这个组合值得单独拿出来讲工业现场的数据存储有个很尴尬的现状用 EEPROM 吧写入速度慢得让人着急擦写次数也就百万次级别高频采集场景下没几年就写废了用 SRAM 加后备电池吧电池寿命、温度范围、维护成本全是坑产线设备换一次电池的人工成本比芯片本身还贵用 NAND/NOR Flash 吧写入前得擦除、擦除得按块来掉电保护逻辑写起来头大而且写入延迟波动大对实时性有要求的场合很难受。MR25H40CDF 这颗芯片恰好卡在这个缝隙里。它是 Everspin 出的 4Mbit 串行 MRAMSPI 接口512K×8 的组织方式最关键的一点是它写数据不需要擦除字节级随机写入写入次数以亿次计掉电数据不丢而且 SPI 时序跟普通串行 Flash 高度兼容。这意味着你原来接 SPI Flash 的板子硬件上几乎不用大改固件里把写命令换掉就能获得一个“像 SRAM 一样随便写、像 Flash 一样不丢数据”的存储介质。PIC32MX360F512L 则是 Microchip 经典的 32 位 MIPS 单片机512KB Flash、32KB RAM、80MHz 主频带多个 SPI 模块工业温度范围版本齐全在工控、电力、医疗设备里出货量很大。拿它来驱动 MR25H40CDF是一个非常典型的“中端 MCU 高可靠非易失存储”的工业嵌入式组合。我最近在一个数据采集项目里正好用了这套搭配踩了一些坑也总结了一些可以直接抄的做法。下面从选型逻辑、硬件连接、SPI 配置、读写实现、掉电保护到问题排查完整地捋一遍。2. 选型逻辑为什么是 MRAM 而不是 Flash 或 FRAM2.1 MR25H40CDF 的核心参数与它解决的问题先把这颗芯片的关键指标摆出来后面所有设计决策都围绕这些数字展开。参数项数值实际意义容量4 Mbit512 KB存配置、日志、校准数据绰绰有余接口SPI最高 40 MHz与 PIC32 的 SPI 模块直接对接组织方式512K × 8 bit字节寻址无需页管理写入方式字节级随机写无需擦除这是与 Flash 最本质的区别写入耐久10^14 次以上每秒写 1000 次也能用几千年数据保持20 年以上工业设备生命周期内无需担心工作电压2.7V ~ 3.6V标准 3.3V 系统工作温度-40°C ~ 85°C工业级覆盖绝大多数工业场景掉电特性写入即非易失无需等待无内部写周期延迟这里最值得说的是“写入即非易失”。Flash 写一个字节实际上要经历“发写使能→发页编程命令→等内部编程完成几毫秒→查状态寄存器”这一整套流程期间如果掉电这一页数据可能就废了。MRAM 的写入是磁隧道结的翻转写入完成的那一刻数据就已经固化没有内部写周期不需要轮询忙状态。这一点在掉电保护设计上省了太多事。2.2 和 FRAM、EEPROM、NOR Flash 的横向对比很多人会问那为什么不直接用 FRAMFRAM 也支持字节写、也非易失。确实FRAM 在低容量段很有竞争力但它的容量普遍偏小常见 256Kbit 以下而且铁电材料的工艺一致性和高温数据保持一直是行业里讨论的话题。MRAM 在容量、速度、温度稳定性上更均衡4Mbit 这个档位正好补上了 FRAM 容量不够、NOR Flash 写入麻烦之间的空档。特性MR25H40CDF (MRAM)FRAMEEPROMNOR Flash写入前擦除不需要不需要不需要需要写入粒度字节字节字节/页页写入延迟无内部周期无内部周期约 5ms约 1~3ms耐久次数10^1410^12~10^1410^610^5容量档位中高偏低低高单位成本高中高低低选型的核心判断就一句话如果你的系统里存在“高频、小量、随机、要求掉电不丢”的写入需求MRAM 的溢价是值得的如果只是偶尔存个配置EEPROM 就够了。2.3 PIC32MX360F512L 作为主控的适配性PIC32MX360F512L 有几个特性让它特别适合配 MR25H40CDFSPI 模块成熟4 线 SPI支持 8/16/32 位传输主模式最高时钟可以到 25MHz受外设总线时钟限制实际配置见后文足够跑满 MRAM 的带宽。512KB 片上 Flash 32KB RAMRAM 够做双缓冲和日志缓存Flash 够放固件和文件系统。工业级温度版本和 MRAM 的温度范围匹配整机过认证时不用为存储单独做温补。DMA 支持SPI 可以配合 DMA 做批量搬运减少 CPU 占用。需要提醒的是PIC32MX 系列的 SPI 时钟源来自外设总线时钟 PBCLK配置分频时要先算清楚 PBCLK 是多少否则实际 SPI 速率和你以为的对不上。这个坑我在第 4 节会详细说。3. 硬件连接与 SPI 时序要点3.1 引脚连接与片选处理MR25H40CDF 是标准 8 脚 SOIC 封装引脚定义很清晰引脚名称连接说明1CS片选低有效接 MCU 的 GPIO 或 SPI 硬件片选2SO数据输出MISO3WP写保护低有效不用时上拉到 VCC4VSS地5SI数据输入MOSI6SCK时钟7HOLD保持低有效不用时上拉到 VCC8VCC电源2.7~3.6V关于片选这里有个实际工程里经常纠结的问题用硬件片选还是软件片选PIC32 的 SPI 模块支持硬件片选SS 引脚自动拉低拉高但硬件片选在多从机、或者需要精确控制片选和时钟先后顺序的场合不够灵活。MRAM 的时序要求是 CS 拉低后、第一个时钟沿之前要有一定的建立时间CS 拉高前最后一个时钟沿之后也要有保持时间。用软件 GPIO 控制片选你能完全掌控这个时序调试时也更容易用逻辑分析仪抓波形。我的做法是片选固定用普通 GPIO 软件控制SPI 只负责 SCK/MOSI/MISO 三根线。这样即使后面要挂多个 SPI 从机切换逻辑也很清晰。注意WP 和 HOLD 两个引脚千万不要悬空。悬空时引脚电平不确定可能随机进入写保护或保持状态表现为“有时候能写有时候写不进去”这种间歇性故障最难查。老老实实各接一个 10K 上拉到 VCC。3.2 SPI 模式选择Mode 0 还是 Mode 3MR25H40CDF 支持 SPI Mode 0CPOL0, CPHA0和 Mode 3CPOL1, CPHA1。这两种模式的区别在于时钟空闲电平和采样边沿Mode 0时钟空闲为低数据在上升沿采样下降沿变化。Mode 3时钟空闲为高数据在上升沿采样下降沿变化。两种都能用选哪个取决于你的板子上还有没有别的 SPI 从机。如果总线上只有 MRAM 一颗随便选如果还挂着别的器件要选一个大家都能兼容的模式。我一般默认用 Mode 0因为大部分逻辑分析仪的默认解码设置就是 Mode 0调试时省事。3.3 PCB 布局的几个实操建议SPI 在低速时对布局不敏感但 MRAM 可以跑到 40MHzPIC32 这边也能跑到 20MHz 以上这时候布局就有讲究了SCK 走线尽量短并且远离模拟信号和复位线时钟是最容易串扰的。MISO 上可以串一个 22~33 欧姆的电阻抑制反射尤其是走线较长时。去耦电容必须靠近 VCC 引脚0.1uF 陶瓷电容加一个 1uF 的别省。CS 走线不要和 SCK 平行长距离走否则片选跳变可能耦合到时钟上造成误触发。这些不是玄学是实际调试时用示波器能看到波形差异的。我遇到过一块板子 SCK 和 CS 平行走了 5 厘米结果高速读写时偶发数据错位把 CS 改道之后问题消失。4. PIC32MX360F512L 的 SPI 初始化与速率计算4.1 先算清楚 PBCLK 和 SPI 实际速率PIC32MX360F512L 的 SPI 时钟来自外设总线时钟 PBCLK而 PBCLK 又由系统时钟 SYSCLK 分频得到。假设系统跑 80MHzPBCLK 分频设为 1那 PBCLK 就是 80MHz。SPI 波特率 PBCLK / (2 × (SPIxBRG 1))。如果你想跑 10MHz 的 SPI 时钟SPIxBRG PBCLK / (2 × 目标速率) - 1 80,000,000 / (2 × 10,000,000) - 1 4 - 1 3所以 SPIxBRG 写 3实际速率 80MHz / (2 × 4) 10MHz。这里有个容易忽略的点PBCLK 的分频配置在系统初始化时就定死了SPI 初始化时必须知道当前 PBCLK 到底是多少。我见过有人系统时钟改了但 SPI 分频没跟着改结果实际 SPI 速率只有预期的一半读写虽然能工作但慢得莫名其妙。4.2 初始化代码下面是我实际用的初始化函数基于 PIC32 的 PLIB 风格直接可参考#define MRAM_CS_LAT LATBbits.LATB2 #define MRAM_CS_TRIS TRISBbits.TRISB2 void MRAM_SPI_Init(void) { // 片选引脚配置为输出默认拉高 MRAM_CS_TRIS 0; MRAM_CS_LAT 1; // 关闭 SPI 再配置 SPI1CON 0; // 清除接收溢出标志 volatile unsigned int dummy; dummy SPI1BUF; // 配置 SPI1CON // ON1, FRMEN0(普通模式), SMP0(中间采样), CKE1(Mode 0) // MSTEN1(主模式), CKP0(空闲低), SSEN0(不用硬件片选) // 8位传输 SPI1CONbits.MSTEN 1; SPI1CONbits.CKP 0; SPI1CONbits.CKE 1; SPI1CONbits.SMP 0; SPI1CONbits.SSEN 0; SPI1CONbits.MODE16 0; SPI1CONbits.MODE32 0; // 波特率PBCLK80MHz目标10MHz SPI1BRG 3; // 使能 SPI SPI1CONbits.ON 1; }4.3 单字节收发函数SPI 是全双工发一个字节的同时会收一个字节。写 MRAM 时收到的字节丢弃读 MRAM 时发的字节是哑元unsigned char MRAM_SPI_Transfer(unsigned char txData) { // 等待发送缓冲空 while (SPI1STATbits.SPITBF); SPI1BUF txData; // 等待接收完成 while (!SPI1STATbits.SPIRBF); return (unsigned char)SPI1BUF; }提示SPI1STATbits.SPITBF是发送缓冲满标志SPIRBF是接收缓冲满标志。这两个标志的轮询顺序不能反先等发送空再写数据写完再等接收满。顺序反了会丢数据。5. MR25H40CDF 的读写命令与完整实现5.1 命令集梳理MR25H40CDF 的命令集和普通 SPI Flash 很像但少了擦除相关的命令多了几个 MRAM 特有的命令编码功能WREN0x06写使能WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据RDID0x9F读器件 ID关键点每次写操作之前必须先发 WREN。这是很多人第一次用会忘的步骤忘了 WREN 直接发 WRITE数据写不进去而且不报错查起来很费劲。5.2 写使能函数void MRAM_WriteEnable(void) { MRAM_CS_LAT 0; MRAM_SPI_Transfer(0x06); // WREN MRAM_CS_LAT 1; }5.3 读状态寄存器状态寄存器的 bit0 是 WEL写使能锁存bit1 是 WEL 相关的状态。写完 WREN 后可以读一下确认 WEL 被置位unsigned char MRAM_ReadStatus(void) { unsigned char status; MRAM_CS_LAT 0; MRAM_SPI_Transfer(0x05); // RDSR status MRAM_SPI_Transfer(0x00); MRAM_CS_LAT 1; return status; }5.4 读数据实现读操作相对简单发 READ 命令 3 字节地址然后连续读void MRAM_Read(uint32_t addr, unsigned char *buf, uint32_t len) { uint32_t i; MRAM_CS_LAT 0; MRAM_SPI_Transfer(0x03); // READ MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); for (i 0; i len; i) { buf[i] MRAM_SPI_Transfer(0x00); } MRAM_CS_LAT 1; }注意地址是 24 位的因为 512KB 需要 19 位地址用 3 字节传输覆盖。地址高位在前的顺序不能错否则读出来的数据全乱。5.5 写数据实现写操作是重点完整流程是WREN → WRITE 命令 → 3 字节地址 → 数据void MRAM_Write(uint32_t addr, const unsigned char *buf, uint32_t len) { uint32_t i; MRAM_WriteEnable(); // 必须先写使能 MRAM_CS_LAT 0; MRAM_SPI_Transfer(0x02); // WRITE MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); for (i 0; i len; i) { MRAM_SPI_Transfer(buf[i]); } MRAM_CS_LAT 1; }这里有个细节WREN 和 WRITE 之间 CS 必须拉高再拉低。因为 WREN 是一个独立的命令周期CS 拉高表示命令结束WEL 位被锁存。如果 WREN 之后不拉高 CS 直接发 WRITE有些批次的芯片会不认。我实测过不拉高 CS 时写入成功率大概七成拉高后 100% 成功。5.6 读器件 ID 验证通信上电后第一件事应该是读器件 ID确认 SPI 通信正常uint32_t MRAM_ReadID(void) { uint32_t id 0; MRAM_CS_LAT 0; MRAM_SPI_Transfer(0x9F); // RDID id | (uint32_t)MRAM_SPI_Transfer(0x00) 16; id | (uint32_t)MRAM_SPI_Transfer(0x00) 8; id | (uint32_t)MRAM_SPI_Transfer(0x00); MRAM_CS_LAT 1; return id; }如果读出来是 0x000000 或者 0xFFFFFF基本可以判断是接线问题、片选没拉低、或者 SPI 模式配错了。6. 掉电保护与数据完整性设计6.1 为什么 MRAM 让掉电保护变简单前面提过MRAM 写入没有内部周期写完即固化。这意味着你不需要像 Flash 那样设计“写一半掉电怎么办”的复杂恢复逻辑。但“简单”不等于“不用管”系统级的掉电保护还是要做的因为掉电可能发生在你正在组织数据、还没写进 MRAM 的那一刻。6.2 双区备份 校验字的设计我的做法是在 MRAM 里划两个区域交替写入每个区域带一个校验字typedef struct { uint32_t magic; // 0x4D52414D MRAM uint32_t seq; // 递增序号 uint16_t len; // 数据长度 uint16_t crc; // 数据 CRC16 uint8_t data[240]; // 实际数据 } MramRecord_t;两个区域各占 256 字节写入时先写区域 A再写区域 B读取时比较两个区域的 seq取序号大的那个。如果某个区域 CRC 校验失败就用另一个。这个设计的逻辑是任何时刻至少有一个区域是完整的。因为写入是顺序的即使掉电发生在写区域 B 的过程中区域 A 还是上一次的完整数据。6.3 掉电检测与紧急写入PIC32 有内置的掉电检测BOR但 BOR 触发时往往已经来不及做任何操作了。更可靠的做法是用一个 GPIO 监测电源电压在电压跌落到阈值以下但还没到 MCU 复位电压之前触发一个中断把关键数据紧急写入 MRAM。void __ISR(_EXTERNAL_1_VECTOR, IPL7AUTO) PowerFailHandler(void) { IFS0bits.INT1IF 0; // 紧急保存关键数据 MRAM_Write(CRITICAL_ADDR, (unsigned char*)g_criticalData, sizeof(g_criticalData)); }这个中断的优先级要设到最高处理时间要尽可能短。MRAM 写入快240 字节在 10MHz SPI 下大约 200 微秒就能写完配合足够大的储能电容完全来得及。注意紧急写入的数据量要提前规划好别在中断里做复杂计算。中断里只做“把已经准备好的缓冲区写进 MRAM”这一件事。7. 常见问题与排查技巧实录7.1 问题速查表现象可能原因排查方法读 ID 返回全 0片选没拉低、MISO 没接、SPI 没使能用逻辑分析仪抓 CS/SCK/MOSI/MISO读 ID 返回全 FMISO 悬空或上拉、从机没响应检查 MISO 连接和从机供电写入后读出来是旧数据忘了 WREN、CS 时序不对抓波形确认 WREN 和 WRITE 之间 CS 有拉高偶发数据错位SPI 速率过高、走线串扰降速测试检查 PCB 布局部分地址写不进去WP 引脚悬空或误拉低测量 WP 引脚电平确认上拉高温下读写异常电源纹波、去耦不足加大去耦检查电源质量7.2 逻辑分析仪是必备工具调试 SPI 存储逻辑分析仪比示波器更实用。抓一次完整的写操作你能同时看到 CS、SCK、MOSI、MISO 四根线的时序关系命令码、地址、数据一目了然。我习惯用 8 通道以上的分析仪把四根线全接上解码设置成 SPI Mode 0一眼就能看出问题在哪。有一次我遇到写入偶尔失败抓波形发现 WREN 命令的最后一个时钟沿和 CS 拉高之间几乎没有间隔芯片来不及锁存 WEL 位。在 CS 拉高前加了一个微秒级的延时问题解决。这种问题不看波形根本想不到。7.3 关于 SPI 速率和稳定性的取舍MR25H40CDF 标称支持 40MHz但实际能不能跑到取决于你的板子。我的经验是走线短、布局好的板子20MHz 稳定运行没问题。走线较长或者有转接排线的降到 10MHz 更稳妥。工业现场有强干扰的5MHz 也不丢人MRAM 本身写入快降速对整体性能影响有限。不要为了追求标称速率而牺牲稳定性。工业设备跑在现场一次数据错误可能导致整批产品报废稳定压倒一切。8. 几个实际项目中的经验补充8.1 关于数据组织方式MRAM 容量 512KB说大不大说小不小。我的建议是提前规划好地址空间别东写一块西写一块。我一般这样划分0x00000 ~ 0x00FFF系统配置区双备份0x01000 ~ 0x01FFF校准参数区0x02000 ~ 0x3FFFF日志/采集数据环形缓冲区0x40000 ~ 0x7FFFF预留扩展区环形缓冲区用两个指针写指针和读指针管理写满后覆盖最旧的数据。MRAM 耐久度高环形覆盖完全不用担心写坏。8.2 关于 CRC 校验的选择数据完整性校验我推荐用 CRC16 而不是简单的累加和。累加和对字节顺序不敏感两个字节交换位置校验和不变检测不出这类错误。CRC16 实现简单查表法几行代码对 MCU 负担很小。uint16_t CRC16(const uint8_t *data, uint16_t len) { uint16_t crc 0xFFFF; uint16_t i, j; for (i 0; i len; i) { crc ^ data[i]; for (j 0; j 8; j) { if (crc 1) crc (crc 1) ^ 0xA001; else crc 1; } } return crc; }8.3 关于批量写入的效率如果需要连续写入大量数据逐字节调用MRAM_Write效率很低因为每次都要发 WREN 和命令头。正确做法是一次 CS 周期内连续写入void MRAM_WriteBulk(uint32_t addr, const unsigned char *buf, uint32_t len) { uint32_t i; MRAM_WriteEnable(); MRAM_CS_LAT 0; MRAM_SPI_Transfer(0x02); MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); for (i 0; i len; i) { MRAM_SPI_Transfer(buf[i]); } MRAM_CS_LAT 1; }MRAM 支持在 CS 保持低电平期间连续写入地址会自动递增不需要每写一个字节就重新发命令。这个特性在写日志时非常有用一次 CS 周期能写几百字节。8.4 关于上电初始化的顺序上电后不要急着读写 MRAM先做这几件事延时 10ms 等电源稳定。初始化 SPI 模块。读器件 ID 确认通信。读配置区校验 CRC。如果 CRC 失败加载默认配置并写入。这个顺序能避免上电瞬间电源不稳导致的误读写。我见过有项目上电就立刻读配置结果因为电源上升沿太慢读出来全是乱码加了 10ms 延时后正常。8.5 关于温度对时序的影响工业级芯片在 -40°C 和 85°C 下的时序特性和常温不完全一样。低温下器件响应变慢高温下漏电流增大。如果你的设备要过宽温测试建议在极限温度下重新验证 SPI 速率必要时降速。我在 -40°C 测试时发现 20MHz 下偶发误码降到 15MHz 后稳定这个余量在常温下是看不出来的。这套 MR25H40CDF 加 PIC32MX360F512L 的组合我在两个项目里用了将近三年现场运行没有出现过数据丢失。它的价值不在于性能有多强而在于把“高频写入”和“掉电不丢”这两件本来矛盾的事情用一个简单的 SPI 接口解决了。如果你正在为工业设备的数据存储选型发愁这个组合值得认真考虑。
返回列表