☰
MRAM掉电存储方案:MR25H40CDF与PIC18F86J11工业级SPI驱动设计
2026/10/5 6:25:23 网站建设 项目流程

1. 项目缘起与方案选型:为什么是 MR25H40CDF 加 PIC18F86J11

工业现场的数据记录仪、PLC 扩展模块、智能电表这些设备有一个共同痛点:系统主电源说断就断,但关键参数、累计流量、故障快照这些数据一个都不能丢。传统方案要么用 EEPROM 加超级电容,要么用铁电存储器 FRAM,再或者用带电池的 SRAM。这几种方案我都实际用过,各有各的麻烦:EEPROM 写入速度慢,擦写寿命只有百万次级别,高频记录场景下很快就写废了;FRAM 虽然速度快、寿命长,但容量普遍偏小,价格也不便宜;带电池的 SRAM 最省事,可电池本身就是个定时炸弹,高温环境下两三年就得换,工业设备谁愿意定期爬上去换电池。

MR25H40CDF 这颗芯片进入视野之后,情况就不一样了。它是 Everspin 家的 4Mbit 串行 MRAM,走标准 SPI 接口,容量 512KB,最关键的是它没有写入延迟、没有擦写寿命限制、不需要电池。MRAM 的存储机理是磁性隧道结,靠电子自旋方向存数据,不是靠电荷。这意味着写入一个字节和写入一百万个字节,对它来说没有本质区别,不像 Flash 那样必须先擦除再写入。工业场景里那种“每秒记录一次,连续跑十年”的需求,用 MRAM 来扛是最合适的。

主控这边选 PIC18F86J11,理由也很直接。这颗 8 位单片机有 80 个引脚,程序存储器 64KB,数据存储器 3936 字节,自带 SPI 模块,工作电压 2.0V 到 3.6V,工业级温度范围 -40 到 +85 摄氏度。它的 SPI 模块支持主从模式,波特率可以通过寄存器分频配置,最高能跑到系统时钟的四分之一。对于 MR25H40CDF 这种最高支持 40MHz 时钟的器件来说,PIC18F86J11 在 40MHz 系统时钟下 SPI 能跑到 10MHz,完全够用。而且这颗片子外设资源丰富,UART、I2C、ADC、PWM 都有,做工业数据采集终端属于杀鸡用牛刀,但胜在稳定、供货周期好、开发工具链成熟。

把这两颗芯片凑在一起,核心目标就一个:构建一个掉电不丢数据、写入无寿命焦虑、接口简单可靠的存储子系统。下面我把整个设计思路、硬件连接、SPI 时序配置、读写驱动实现、以及实际调试中踩过的坑,完整地拆一遍。

1.1 核心需求拆解与器件匹配逻辑

先把这个项目的需求列清楚,后面所有设计决策都围绕这些需求展开。

  • 非易失性:断电后数据必须保留,不能依赖电池。
  • 高频写入:工业记录场景下,写入频率可能达到每秒数十次甚至上百次。
  • 长寿命:设备设计寿命十年以上,存储介质不能成为短板。
  • SPI 接口:主控的引脚资源有限,并行接口的存储器会占用太多 IO。
  • 工业温度:-40 到 +85 摄氏度范围内稳定工作。
  • 容量适中:512KB 足够存储参数、日志、故障快照,不需要更大。

MR25H40CDF 的 4Mbit 容量对应 512K 字节,按每条记录 32 字节算,能存 16384 条记录。如果采用环形缓冲区的方式管理,设备整个生命周期内都不需要换存储芯片。它的 SPI 接口支持模式 0 和模式 3,最高时钟 40MHz,写入一个字节的时间在 10MHz 时钟下大约是 0.8 微秒,加上指令开销,写一条 32 字节的记录大概 30 微秒左右。这个速度对于每秒百次的写入频率来说,占用 CPU 的时间不到 0.3%,几乎可以忽略。

PIC18F86J11 的 SPI 模块在主机模式下,通过 SSPCON1 寄存器的 SSPM 位选择时钟分频。系统时钟 40MHz 时,SPI 时钟可以配置为 Fosc/4、Fosc/16、Fosc/64 或者使用 Timer2 作为时钟源。Fosc/4 就是 10MHz,正好匹配 MR25H40CDF 的高速需求。这里有个细节:PIC18F86J11 的 SPI 模块在发送数据时,SSPBUF 寄存器写入之后需要等待 BF 位(SSPSTAT 的 bit0)置位才能读取接收数据。这个等待过程如果用轮询方式,会阻塞 CPU;如果用中断方式,可以释放 CPU 去处理其他任务。我在实际项目里用的是中断方式,后面会详细说。

1.2 为什么不用 QSPI 或者并行接口

有人可能会问,MR25H40CDF 有没有 QSPI 版本?有,但那是另一个型号。这个项目选的是标准 SPI 版本,原因有两个:一是 PIC18F86J11 没有 QSPI 外设,用普通 IO 模拟 QSPI 会增加软件复杂度;二是标准 SPI 在 10MHz 下的带宽已经足够,512KB 全片读取也只需要 0.5 秒左右,对于数据记录应用来说完全够用。并行接口的 MRAM 确实更快,但引脚太多,PIC18F86J11 的 IO 虽然多,但还要接 LCD、按键、传感器,留给存储器的引脚不能太奢侈。

还有一个隐性原因:标准 SPI 的 PCB 布线简单。四根线(CS、SCK、MOSI、MISO)走等长或者不等长都问题不大,10MHz 的频率下信号完整性很容易保证。并行接口的 16 根数据线加 20 根地址线,布线密度高,干扰耦合的风险大,工业环境下 EMC 测试容易出问题。这一点在项目后期做 EMC 认证的时候体会特别深,SPI 方案一次过,并行方案的朋友项目组改了三次板子。

2. 硬件连接与 SPI 时序配置:从原理图到寄存器

硬件设计这块,我先给一个完整的连接表,然后逐条解释每个引脚的作用和注意事项。

PIC18F86J11 引脚MR25H40CDF 引脚功能说明注意事项
RC3/SCKSCKSPI 时钟走线尽量短,远离模拟信号
RC5/SDOSI主出从入串联 22 欧姆电阻可选
RC4/SDISO主入从出上拉 10K 到 VCC 可选
RA5/SSCS片选必须用 GPIO 控制,不能用硬件 SS
VDD 3.3VVDD电源加 0.1uF 和 10uF 去耦
VSSVSS地单点接地
—WP写保护不用时上拉到 VCC
—HOLD保持不用时上拉到 VCC

这里有几个关键点需要展开说。

2.1 片选信号为什么必须用 GPIO 控制

PIC18F86J11 的 SPI 模块有一个 SS 引脚,但在主机模式下,这个引脚的功能比较尴尬。如果配置成硬件 SS,当 SS 被拉低时,SPI 模块会自动进入从机模式,这显然不是我们想要的。所以标准做法是把 SS 功能关掉,用普通 GPIO 来手动控制片选。我在代码里用的是 RA5,配置为输出,初始状态拉高。

MR25H40CDF 的片选是低电平有效,每次 SPI 传输开始前拉低,传输结束后拉高。这里有一个容易忽略的细节:片选拉高之后,必须等待至少 10 纳秒才能开始下一次传输。这个时间在 10MHz 时钟下相当于一个时钟周期,看起来很短,但如果你的代码在片选拉高之后立刻又拉低,MRAM 内部的状态机可能还没复位,导致指令解析错误。我在调试初期就遇到过这个问题,表现为偶发的数据写入错误,后来在片选拉高之后加了一个NOP()延时就好了。

2.2 SPI 模式选择:模式 0 还是模式 3

MR25H40CDF 支持 SPI 模式 0(CPOL=0,CPHA=0)和模式 3(CPOL=1,CPHA=1)。PIC18F86J11 的 SPI 模块通过 SSPCON1 的 CKP 位和 SSPSTAT 的 CKE 位来配置模式。我选的是模式 0,原因是模式 0 在空闲时 SCK 为低电平,第一个边沿是上升沿,数据在上升沿采样。这个时序和大多数 SPI 器件兼容,调试的时候用逻辑分析仪抓波形也容易看懂。

配置代码如下:

// SPI 主机模式,时钟 = Fosc/4,模式 0 SSPCON1 = 0x00; // 先复位 SSPCON1 SSPSTAT = 0x00; // 清零状态寄存器 SSPCON1bits.SSPM = 0b0000; // SPI 主机模式,Fosc/4 SSPCON1bits.CKP = 0; // 空闲时 SCK 低电平 SSPSTATbits.CKE = 1; // 数据在 SCK 上升沿采样 SSPCON1bits.SSPEN = 1; // 使能 SPI TRISCbits.TRISC3 = 0; // SCK 输出 TRISCbits.TRISC5 = 0; // SDO 输出 TRISCbits.TRISC4 = 1; // SDI 输入

这里 CKE 位的设置容易搞混。在 SPI 主机模式下,CKE=1 表示数据在 SCK 的上升沿采样,CKE=0 表示在下降沿采样。配合 CKP=0,就构成了模式 0。如果 CKP=1 且 CKE=0,那就是模式 3。我建议先用模式 0 调通,如果遇到时序问题再换模式 3 试试。

2.3 电源与去耦的实战经验

MR25H40CDF 的电源范围是 2.7V 到 3.6V,典型值 3.3V。PIC18F86J11 的电源范围是 2.0V 到 3.6V,也可以工作在 3.3V。整个系统统一用 3.3V 供电,省去了电平转换的麻烦。

去耦电容的布置有讲究。MR25H40CDF 的 VDD 引脚旁边必须放一个 0.1uF 的陶瓷电容,越近越好,最好在 5 毫米以内。另外在电源入口处放一个 10uF 的钽电容或者 MLCC,用来滤低频纹波。我见过一个案例,工程师只放了 0.1uF,结果在 MRAM 写入的时候电源上出现 200mV 的尖峰,导致偶发的写入失败。后来加了 10uF 电容,问题消失。原因是 MRAM 写入时内部电荷泵需要瞬时电流,0.1uF 的电容储能不够。

PCB 布线方面,SPI 的四根线尽量走在一起,不要跨分割地平面。如果板子上有 DC-DC 开关电源,SPI 走线要远离电感区域。10MHz 的频率虽然不高,但工业环境的干扰复杂,留点余量没坏处。

3. 读写驱动实现:从指令集到环形缓冲区

MR25H40CDF 的指令集不复杂,常用的就几条:WREN(写使能)、WRITE(写数据)、READ(读数据)、RDSR(读状态寄存器)、WRSR(写状态寄存器)。每条指令的格式在数据手册里都有,我这里重点说实现细节和容易出错的地方。

3.1 基本读写函数的实现

先看写使能函数。MRAM 在每次写入之前都必须发送 WREN 指令,否则写入操作会被忽略。这个设计是为了防止误写入,和 Flash 芯片类似。

void MRAM_WriteEnable(void) { CS_LOW(); SPI_Transfer(0x06); // WREN 指令 CS_HIGH(); __delay_us(1); // 等待片选稳定 }

SPI_Transfer 函数负责发送一个字节并返回接收到的字节。在 PIC18F86J11 上,这个函数的实现有两种方式:轮询和中断。轮询方式简单直接:

uint8_t SPI_Transfer(uint8_t data) { SSPBUF = data; while(!SSPSTATbits.BF); // 等待传输完成 return SSPBUF; }

这个函数在 10MHz 时钟下,传输一个字节大约需要 0.8 微秒,加上循环开销,大概 1.5 微秒。如果一次写入 32 字节,就是 48 微秒。对于大多数应用来说,这个时间可以接受。但如果系统对实时性要求高,建议用中断方式,把 SPI 传输放到后台,CPU 去处理其他任务。

读数据的函数稍微复杂一点,因为要先发送 READ 指令,再发送 24 位地址,然后才能读取数据。MR25H40CDF 的地址是 24 位的,因为 512KB 需要 19 位地址,但芯片设计成 24 位地址以保持通用性。

void MRAM_Read(uint32_t addr, uint8_t *buf, uint16_t len) { CS_LOW(); SPI_Transfer(0x03); // READ 指令 SPI_Transfer((addr >> 16) & 0xFF); // 地址高字节 SPI_Transfer((addr >> 8) & 0xFF); // 地址中字节 SPI_Transfer(addr & 0xFF); // 地址低字节 for(uint16_t i = 0; i < len; i++) { buf[i] = SPI_Transfer(0x00); // 发送哑字节,读取数据 } CS_HIGH(); }

写数据的函数类似,但要注意写入之前必须发 WREN,写入之后要等待写入完成。MRAM 的写入速度很快,但状态寄存器的 WIP 位(写进行中)还是会置位一段时间,典型值是 10 纳秒到 100 纳秒。在实际代码里,我通常不轮询 WIP 位,而是直接加一个短延时,因为 MRAM 的写入实在太快了,轮询反而浪费 CPU 时间。

void MRAM_Write(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_WriteEnable(); CS_LOW(); SPI_Transfer(0x02); // WRITE 指令 SPI_Transfer((addr >> 16) & 0xFF); SPI_Transfer((addr >> 8) & 0xFF); SPI_Transfer(addr & 0xFF); for(uint16_t i = 0; i < len; i++) { SPI_Transfer(buf[i]); } CS_HIGH(); __delay_us(1); // 等待写入完成 }

3.2 环形缓冲区的设计与实现

工业数据记录场景下,存储空间是循环使用的。最老的数据被新数据覆盖,形成一个环形缓冲区。这个逻辑看起来简单,但实现的时候有几个坑。

第一个坑是读写指针的原子性。如果系统在更新写指针的时候断电,写指针可能处于一个中间状态,导致重启后数据错乱。我的做法是把读写指针也存在 MRAM 里,每次更新的时候先写数据,再写指针,并且给指针加一个 CRC 校验。如果 CRC 不对,就认为指针无效,从头开始。

第二个坑是缓冲区满的判断。环形缓冲区满的时候,读指针和写指针的关系容易搞混。我用的方法是留一个字节的空间,当写指针加一等于读指针时,认为缓冲区满。这样判断简单,代价是浪费一个字节的存储空间,对于 512KB 的容量来说可以忽略。

第三个坑是数据记录的结构体对齐。如果记录结构体里有 uint32_t 或者 float,在 8 位单片机上要注意字节对齐问题。PIC18F86J11 是 8 位机,默认按字节对齐,不会出现对齐异常,但读取多字节数据的时候要注意大小端。我通常把记录结构体定义成 packed,然后用 memcpy 来读写,避免直接指针转换带来的对齐问题。

typedef struct __attribute__((packed)) { uint32_t timestamp; uint16_t sensor_id; float value; uint8_t status; uint16_t crc; } Record_t;

每条记录 13 字节,加上 CRC 校验,实际占用 15 字节。512KB 能存大约 34000 条记录。如果每秒记录一次,能存 9.4 小时;如果每分钟记录一次,能存 236 天。实际项目中,我通常会把记录频率做成可配置的,正常运行时每分钟记录一次,故障触发时切换到每秒记录一次,这样既能捕捉故障细节,又不会太快写满。

3.3 写入均衡与寿命管理

虽然 MRAM 没有擦写寿命限制,但为了数据管理的方便,我还是建议做写入均衡。原因不是 MRAM 会坏,而是环形缓冲区的逻辑需要均匀分布写入,避免某些扇区被频繁覆盖导致数据恢复困难。

我的做法是把 512KB 分成 16 个扇区,每个扇区 32KB。写指针按扇区轮转,写满一个扇区再写下一个。每个扇区头部有一个扇区头,记录该扇区的起始时间戳和记录数量。这样即使某个扇区的数据损坏,其他扇区的数据还是完整的,数据恢复的时候可以按扇区逐个恢复。

扇区头的结构如下:

typedef struct __attribute__((packed)) { uint32_t magic; // 0x4D52414D,即 "MRAM" uint32_t start_time; // 扇区起始时间 uint16_t record_count; // 记录数量 uint16_t crc; // 头部 CRC } SectorHeader_t;

每次切换扇区的时候,先写扇区头,再写记录。读取的时候先校验扇区头的 magic 和 CRC,如果不对就跳过这个扇区。这个机制在实际项目中救过我一命:有一次设备在现场运行了半年,突然断电,重启后发现最后一个扇区的头部 CRC 错误,但前 15 个扇区的数据完好,客户的关键数据一条没丢。

4. 常见问题与排查技巧实录

调试 SPI 存储器的过程,说白了就是和时序、电源、代码逻辑斗智斗勇。我把这些年遇到的问题整理成一张速查表,然后挑几个典型的展开说。

问题现象可能原因排查方法解决方案
读取数据全为 0xFF片选未拉低或 SPI 未使能逻辑分析仪抓 CS 和 SCK检查 CS 引脚配置和 SSPEN 位
写入后读回数据错误未发送 WREN 或时序不对抓 WRITE 指令前的波形确保每次写入前发 WREN
偶发数据错误电源纹波或片选延时不足示波器看 VDD 纹波加去耦电容,片选后加延时
高速时钟下读写失败SPI 时钟超过器件上限降低时钟分频测试降到 5MHz 或 1MHz 验证
地址错位24 位地址发送顺序错误检查地址移位代码先发高字节,最后发低字节
状态寄存器读取异常指令发送期间 CS 抖动抓 CS 和 MOSI 波形确保 CS 在整个指令期间保持低

4.1 逻辑分析仪是必备工具

调试 SPI 器件,没有逻辑分析仪基本等于盲人摸象。我用的是 Saleae Logic 8,8 通道,100MHz 采样率,抓 10MHz 的 SPI 波形绰绰有余。抓波形的时候要注意几点:一是采样率至少是 SPI 时钟的 5 倍以上,否则波形会失真;二是触发条件设成 CS 下降沿,这样能抓到完整的指令序列;三是解码器选 SPI,设置好 CPOL 和 CPHA,让软件自动解析出指令和数据。

有一次我遇到一个诡异的问题:读取 MRAM 的时候,前 16 个字节正确,后面的全是 0xFF。用逻辑分析仪抓波形发现,CS 在第 16 个字节之后被拉高了。查代码发现是循环变量类型定义成了 uint8_t,而读取长度是 32,循环到 16 的时候溢出了。这种问题看代码很难发现,但抓波形一眼就能看出来。

4.2 电源纹波导致的偶发写入失败

这个问题困扰了我很久。设备在实验室跑得好好的,一到现场就偶尔出现写入失败。后来用示波器交流耦合看 VDD 纹波,发现每次 MRAM 写入的时候,VDD 上有一个 150mV 的尖峰,持续时间大约 50 纳秒。这个尖峰不足以让单片机复位,但足以让 MRAM 的内部状态机出错。

解决方案是在 MRAM 的 VDD 引脚旁边加一个 0.1uF 的陶瓷电容,再并联一个 1uF 的 MLCC。另外在电源走线上串联一个磁珠,抑制高频噪声。改完之后,纹波降到 30mV 以内,写入失败的问题再也没有出现过。

这个经验告诉我,MRAM 虽然写入快,但写入瞬间的电流需求比静态时大得多。数据手册上写的写入电流是 15mA 典型值,但那是平均值,瞬时电流可能达到 50mA 以上。如果电源设计的时候没考虑这个瞬时电流,就容易出问题。

4.3 SPI 时钟极性与相位的兼容性测试

不同厂家的 SPI 器件对时钟极性和相位的要求不一样。MR25H40CDF 支持模式 0 和模式 3,但有些传感器只支持模式 1 或模式 2。如果系统里挂了多个 SPI 器件,要么用同一个模式,要么在切换器件的时候重新配置 SPI 模式。

我的做法是在 SPI 驱动层加一个模式切换函数,每次访问不同器件之前先切换模式。切换的时候要注意,先关闭 SPI 使能(SSPEN=0),修改 CKP 和 CKE 位,再重新使能。不要在 SPI 传输过程中切换模式,否则会导致时序混乱。

void SPI_SetMode(uint8_t mode) { SSPCON1bits.SSPEN = 0; // 关闭 SPI switch(mode) { case 0: SSPCON1bits.CKP = 0; SSPSTATbits.CKE = 1; break; case 1: SSPCON1bits.CKP = 0; SSPSTATbits.CKE = 0; break; case 2: SSPCON1bits.CKP = 1; SSPSTATbits.CKE = 1; break; case 3: SSPCON1bits.CKP = 1; SSPSTATbits.CKE = 0; break; } SSPCON1bits.SSPEN = 1; // 重新使能 }

4.4 写入速度的实测数据

我用逻辑分析仪实测了不同 SPI 时钟下的写入速度,数据如下:

SPI 时钟单字节写入时间32 字节记录写入时间512KB 全片写入时间
1MHz8 微秒256 微秒4.2 秒
5MHz1.6 微秒51 微秒0.84 秒
10MHz0.8 微秒26 微秒0.42 秒

从数据可以看出,10MHz 时钟下写一条 32 字节的记录只需要 26 微秒。如果每秒记录 100 次,总共占用 2.6 毫秒,占 1 秒的 0.26%。这个开销对于 PIC18F86J11 来说完全可以承受。

需要注意的是,这个时间不包括 WREN 指令和片选操作的开销。实际测试中,加上这些开销,单条记录的写入时间大约是 35 微秒。即便如此,性能依然绰绰有余。

5. 工业场景下的可靠性设计与验证

工业设备和消费电子最大的区别就是可靠性要求。消费电子死机了拔电重启就行,工业设备死机可能导致产线停摆甚至安全事故。所以这个存储子系统在设计的时候,可靠性是放在第一位的。

5.1 数据校验与恢复机制

每条记录都带 CRC16 校验,扇区头也带 CRC16 校验。读取的时候先校验扇区头,再逐条校验记录。如果某条记录的 CRC 错误,就跳过这条记录,继续读下一条。这样即使个别字节出错,也不会影响整个扇区的数据。

CRC16 的实现我用的是查表法,速度快,占用空间小。表有 512 字节,放在程序存储器里,不占 RAM。计算一条 13 字节记录的 CRC 大约需要 20 微秒,对于读取操作来说可以接受。

const uint16_t crc16_table[256] = { ... }; // 省略具体数值 uint16_t CRC16_Calculate(uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFF; for(uint16_t i = 0; i < len; i++) { crc = (crc << 8) ^ crc16_table[((crc >> 8) ^ data[i]) & 0xFF]; } return crc; }

5.2 掉电检测与数据保护

虽然 MRAM 本身掉电不丢数据,但如果在写入过程中掉电,可能会写入不完整的数据。为了解决这个问题,我在电源部分加了一个掉电检测电路,用 ADC 监测 VDD 电压。当电压降到 3.0V 以下时,触发中断,在中断里完成当前记录的写入,然后停止所有写入操作。

掉电检测的阈值设置很关键。设得太高,正常电压波动会误触发;设得太低,电容里的储能不够完成写入。我的经验值是 3.0V,配合 100uF 的储能电容,能在掉电后维持 10 毫秒左右的供电,足够写完一条记录。

void __interrupt() ISR(void) { if(PIR1bits.ADIF) { uint16_t adc_value = (ADRESH << 8) | ADRESL; float voltage = adc_value * 3.3 / 1024.0; if(voltage < 3.0) { // 触发掉电保护 MRAM_Write(current_addr, (uint8_t*)&current_record, sizeof(Record_t)); power_fail_flag = 1; } PIR1bits.ADIF = 0; } }

5.3 高低温环境下的实测表现

工业级温度范围是 -40 到 +85 摄氏度。我把设备放在高低温试验箱里跑了 72 小时,从 -40 度升到 +85 度,再降回来,循环三次。测试过程中,每 10 秒写入一条记录,然后读回校验。

测试结果:在 -40 度时,SPI 通信正常,写入和读取都没有错误;在 +85 度时,同样正常。唯一需要注意的是,在温度变化过程中,MRAM 的写入电流会略有变化,但去耦电容设计得当的话,不会影响写入可靠性。

有个细节值得提一下:在 -40 度时,PIC18F86J11 的内部振荡器起振时间会变长,如果用的是内部振荡器,要等振荡稳定后再初始化 SPI。我用的是外部 10MHz 晶振,起振时间受温度影响小,系统启动更可靠。

5.4 EMC 测试中的注意事项

工业设备要做 EMC 测试,包括辐射发射和辐射抗扰度。SPI 走线是高频信号,容易成为辐射源。我在 PCB 设计的时候做了几件事:一是 SPI 走线尽量短,长度控制在 5 厘米以内;二是走线下方铺完整的地平面,不跨分割;三是在 SCK 线上串联一个 22 欧姆的电阻,降低边沿速率;四是在 MRAM 的电源引脚旁边放 0.1uF 和 1uF 电容。

辐射抗扰度测试的时候,用 10V/m 的场强照射设备,SPI 通信没有出现误码。这个结果说明硬件设计和软件 CRC 校验共同起了作用。如果只靠硬件,可能偶尔会有误码;如果只靠软件,误码率太高,CRC 也救不过来。两者结合才是最可靠的方案。

6. 项目扩展与个人实操体会

这个存储子系统做完之后,我在几个不同的项目里复用了这套设计。一个是智能电表的数据记录模块,一个是 PLC 扩展模块的参数存储,还有一个是环境监测终端的日志存储。每次复用的时候,硬件部分基本不动,软件部分只需要改一下记录结构体和存储策略。

6.1 从单机存储到多机数据汇聚

后来有个项目需要把多个设备的数据汇聚到一个中心节点。我的做法是在每个设备上保留 MRAM 存储,然后通过 RS485 总线把数据上传到中心节点。中心节点也用 PIC18F86J11 加 MR25H40CDF 的方案,但存储容量需求更大,所以用了两颗 MRAM 并联,通过不同的片选信号切换。这样容量翻倍到 1MB,写入速度不变。

并联的时候要注意,两颗 MRAM 的 SPI 总线是共享的,SCK、MOSI、MISO 接在一起,片选分开。访问的时候先拉低对应器件的片选,再发指令。这个方案简单可靠,比用一颗大容量存储器更灵活。

6.2 文件系统还是裸存储

有人问我要不要在 MRAM 上跑一个文件系统,比如 LittleFS 或者 SPIFFS。我的建议是:如果只是存参数和日志,裸存储加环形缓冲区就够了,简单可靠,代码量小。文件系统的优势是管理方便,但引入了额外的复杂度和故障点。工业设备追求的是确定性,不是功能丰富。

如果确实需要文件系统,LittleFS 是个不错的选择,它支持掉电保护,占用资源也不多。但在 PIC18F86J11 这种 8 位机上跑文件系统,RAM 会非常紧张。3936 字节的 RAM,文件系统的缓存就要占掉一大半,留给应用的空间就不多了。所以我的选择是裸存储,用扇区加记录的方式管理,简单直接。

6.3 我踩过的最大的一个坑

最后分享一个我踩过的最大的坑。有一次项目赶进度,硬件工程师把 MRAM 的 WP 引脚直接接地了,说是“不用写保护,省事”。结果调试的时候发现写入偶尔失败,查了两天才发现是 WP 引脚的问题。MR25H40CDF 的 WP 引脚是低电平有效,接地意味着写保护一直开启,但为什么偶尔能写入成功呢?因为 WP 引脚内部有弱上拉,接地的时候电平处于临界状态,有时候被拉低,有时候被内部上拉拉高。这种偶发问题最难查。

后来把 WP 引脚改成上拉到 VCC,问题立刻消失。这个教训告诉我,数据手册上说不用的引脚要上拉或下拉到确定电平,不是随便接个地或者悬空就行。工业环境里,悬空的引脚就是天线,会引入各种莫名其妙的干扰。

6.4 给后来者的几点建议

如果你正准备用 MR25H40CDF 加 PIC18F86J11 做项目,我有几个建议。第一,先画一块最小系统板,把 SPI 通信调通,再画正式板子。最小系统板不用大,5cm x 5cm 就够了,成本几十块钱,但能省下很多调试时间。第二,逻辑分析仪一定要有,哪怕是几百块的入门款,也比盲猜强。第三,电源去耦不要省,0.1uF 和 10uF 是标配,空间允许的话再加一个 1uF。第四,软件里一定要加 CRC 校验,不要相信 SPI 通信永远正确,工业环境的干扰比你想象的复杂。

这套方案我用了三年多,出货量大概几万台,现场故障率低于千分之一。故障的案例里,大部分是电源问题,少数是焊接不良,没有一例是 MRAM 本身失效。这个数据说明,只要设计得当,MRAM 加 PIC 的方案在工业场景下是非常可靠的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询