☰
工业数据采集存储方案:MRAM与PIC24的SPI驱动实战
2026/10/5 3:09:52 网站建设 项目流程

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

1.1 一个真实的需求场景

前阵子接了个工业数据采集终端的活儿,设备装在配电柜里,环境温度夏天能到 70 度,冬天户外柜内能到零下 30 度。需求说起来简单:每 100ms 采集一次三相电流电压,把关键数据存下来,断电不能丢,上电要能立刻读出来继续用。但真做起来,存储这块把我折腾得够呛。

一开始想用 EEPROM,比如 24C 系列,便宜、简单、I2C 两根线。但算了一下写入频率就放弃了:100ms 一次,一天就是 86 万次,EEPROM 的擦写寿命撑死 100 万次,一天多就写废了。换 FRAM 吧,寿命够,但容量小、价格高,而且供货周期不稳定。后来想到 SPI Flash,W25Q 系列,容量大、便宜,但 Flash 有个致命问题——写入前必须擦除整个扇区,而且擦除时间长,掉电时如果正在擦除,整个扇区数据都可能丢。工业现场掉电是家常便饭,这个风险不能接受。

最后选定了MR25H40CDF这颗 MRAM(磁性随机存储器)。它的特点很鲜明:写入不需要擦除、字节级随机写、写入速度接近 SRAM、擦写寿命几乎无限(10^14 次以上)、断电数据保持 20 年以上。价格比 EEPROM 贵,但比 FRAM 便宜,容量 512KB,对于存采集数据绰绰有余。主控选了PIC24EP512GU810,理由后面细说。

1.2 为什么主控选 PIC24EP512GU810

PIC24EP 系列是 Microchip 家的 16 位 MCU,GU 后缀带 USB、DMA、图形接口,810 代表 100 引脚。选它有几个实际考量:

第一,SPI 外设资源丰富。PIC24EP512GU810 有 4 个 SPI 模块,可以同时挂 MRAM、显示屏、无线模块,互不抢总线。工业设备经常要接多个外设,SPI 口不够用是很烦的事。

第二,DMA 支持。采集数据要连续写入 MRAM,如果用 CPU 轮询 SPI 发送,会占用大量算力。PIC24EP 的 DMA 可以直接把内存数据搬到 SPI 发送寄存器,CPU 腾出来做算法处理。这一点在 100ms 周期的实时采集里很关键。

第三,宽温范围。PIC24EP 系列有工业级和扩展级温度选项,-40 到 +125 度,和 MRAM 的宽温特性匹配。配电柜里的温度波动大,商业级芯片扛不住。

第四,生态成熟。Microchip 的 XC16 编译器、Harmony 框架、代码配置器(MCC)都很完善,开发效率高。虽然 PIC24 内核不算新,但工业场景要的是稳定可靠,不是跑分。

1.3 MRAM 与 SPI Flash、EEPROM、FRAM 的对比

为了让你直观理解选型逻辑,我把几种常见非易失存储拉出来对比一下:

特性MR25H40CDF (MRAM)W25Q64 (SPI Flash)24C512 (EEPROM)FM25V05 (FRAM)
容量512KB8MB64KB64KB
接口SPISPII2CSPI
写入前擦除不需要必须整扇区擦除不需要不需要
写入寿命10^14 次10 万次100 万次10^14 次
写入速度字节级,无延迟页写入 0.7ms,擦除 45ms页写入 5ms字节级,无延迟
掉电风险极低擦除时掉电丢整扇区低极低
单价(参考)中高低低高
供货稳定稳定稳定波动

从表里能看出来,MRAM 的核心优势就是写入不用等、不用擦、不怕掉电。对于工业采集这种高频写入场景,这三点直接决定了方案能不能落地。Flash 虽然便宜量大,但擦除窗口期的掉电风险是硬伤;EEPROM 寿命不够;FRAM 什么都好,就是贵且供货不稳。MRAM 卡在中间,性价比最合适。

提示:MR25H40CDF 是 Everspin 家的产品,型号里的 40 代表 4Mbit,也就是 512KB。注意别和 MR25H256(256Kbit)搞混,容量差 16 倍。

2. 硬件设计与 SPI 总线布局

2.1 MR25H40CDF 的引脚与电气特性

MR25H40CDF 是 8 引脚 SOIC 封装,引脚定义很标准:

  • CS:片选,低有效
  • SO:数据输出(MISO)
  • SI:数据输入(MOSI)
  • SCK:时钟
  • WP:写保护,低有效
  • HOLD:保持,低有效
  • VDD:电源,2.7V 到 3.6V
  • VSS:地

这里有两个容易踩坑的引脚:WP 和 HOLD。WP 拉低时芯片进入写保护,所有写操作被忽略;HOLD 拉低时暂停当前通信,释放后继续。很多新手直接把这两个脚悬空,结果发现写不进去数据,查半天查不出来。我的做法是:WP 通过 10K 电阻上拉到 VDD,HOLD 也上拉到 VDD,需要写保护时再用 GPIO 拉低 WP。

电气特性方面,MR25H40CDF 支持最高 40MHz 的 SPI 时钟。但实际用的时候要注意,时钟越快,PCB 走线要求越高。我在 20MHz 下跑得很稳,再往上就需要考虑走线阻抗和端接电阻了。

2.2 PIC24EP512GU810 的 SPI 模块配置

PIC24EP 的 SPI 模块配置有几个关键寄存器:

  • SPIxCON1:主控模式、时钟极性、时钟相位、时钟分频
  • SPIxCON2:帧模式、片选控制
  • SPIxSTAT:状态标志,发送/接收缓冲满空

配置成主控模式的步骤:

// 关闭 SPI 模块再配置 SPI1CON1bits.SPIEN = 0; // 主控模式,8 位数据,时钟空闲低,第二个边沿采样 SPI1CON1bits.MSTEN = 1; // 主控 SPI1CON1bits.MODE16 = 0; // 8 位模式 SPI1CON1bits.CKP = 1; // 时钟空闲高 SPI1CON1bits.CKE = 0; // 第一个边沿采样(对应模式 2) SPI1CON1bits.SMP = 0; // 中间采样 // 时钟分频:Fcy = 60MHz,目标 15MHz // 主控时钟 = Fcy / (2 * (SPI1BRG + 1)) // 15MHz = 60MHz / (2 * (SPI1BRG + 1)) => SPI1BRG = 1 SPI1BRG = 1; // 使能 SPI SPI1CON1bits.SPIEN = 1;

这里有个细节:时钟极性和相位要和 MRAM 匹配。MR25H40CDF 支持 SPI 模式 0 和模式 3。我选的是模式 2(CKP=1, CKE=0),实测下来在长走线时抗干扰更好。如果你用模式 0,把 CKP 改成 0 就行。

2.3 PCB 布局的实操经验

SPI 总线在工业环境里跑,PCB 布局比代码更重要。我踩过的坑:

第一,SCK 走线要短且直。SCK 是时钟线,频率高,容易辐射干扰。我第一版板子 SCK 绕了个弯,结果 20MHz 下误码率很高。后来改成直线走线,长度控制在 5cm 以内,问题消失。

第二,CS 线要单独走,不要和 SCK 平行。CS 是控制线,如果和 SCK 平行走很长距离,会耦合时钟噪声,导致误触发。我的做法是 CS 和 SCK 之间隔一条地线,或者垂直交叉。

第三,MRAM 电源脚要加去耦电容。0.1uF 陶瓷电容紧贴 VDD 引脚,再并一个 10uF 钽电容。工业现场电源纹波大,去耦不好会导致写入失败。

第四,WP 和 HOLD 不要悬空。前面说了,悬空时电平不确定,可能随机进入写保护或保持状态。上拉电阻是必须的。

注意:如果你的设备要通过 EMC 测试,SPI 走线最好加 22 欧姆的串联端接电阻,位置靠近 MCU 输出端。这个电阻能抑制反射,改善信号完整性。

3. 软件驱动实现:从底层读写到数据管理

3.1 MRAM 的指令集与读写时序

MR25H40CDF 的指令集很简洁,常用的就几条:

指令编码功能
WREN0x06写使能
WRDI0x04写禁止
RDSR0x05读状态寄存器
WRSR0x01写状态寄存器
READ0x03读数据
WRITE0x02写数据

读写时序的核心逻辑:

写操作:拉低 CS -> 发送 WREN(0x06)-> 拉高 CS -> 拉低 CS -> 发送 WRITE(0x02)-> 发送 24 位地址 -> 发送数据 -> 拉高 CS。

读操作:拉低 CS -> 发送 READ(0x03)-> 发送 24 位地址 -> 读取数据 -> 拉高 CS。

注意 MRAM 的地址是 24 位的,因为 512KB 需要 19 位地址,但芯片按 24 位地址设计,高 5 位忽略。发送地址时要发 3 个字节,从高到低。

3.2 底层驱动代码实现

先写 SPI 字节收发函数:

uint8_t SPI1_ExchangeByte(uint8_t data) { SPI1BUF = data; while (!SPI1STATbits.SPIRBF); // 等待接收完成 return SPI1BUF; }

然后是 MRAM 写使能:

void MRAM_WriteEnable(void) { MRAM_CS_LOW(); SPI1_ExchangeByte(0x06); MRAM_CS_HIGH(); }

写数据函数:

void MRAM_Write(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_WriteEnable(); MRAM_CS_LOW(); SPI1_ExchangeByte(0x02); SPI1_ExchangeByte((addr >> 16) & 0xFF); SPI1_ExchangeByte((addr >> 8) & 0xFF); SPI1_ExchangeByte(addr & 0xFF); for (uint16_t i = 0; i < len; i++) { SPI1_ExchangeByte(buf[i]); } MRAM_CS_HIGH(); }

读数据函数:

void MRAM_Read(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_CS_LOW(); SPI1_ExchangeByte(0x03); SPI1_ExchangeByte((addr >> 16) & 0xFF); SPI1_ExchangeByte((addr >> 8) & 0xFF); SPI1_ExchangeByte(addr & 0xFF); for (uint16_t i = 0; i < len; i++) { buf[i] = SPI1_ExchangeByte(0xFF); } MRAM_CS_HIGH(); }

读的时候发送 0xFF 是空操作,因为 SPI 是全双工,要产生时钟才能收数据。

3.3 用 DMA 提升写入效率

100ms 采集一次,每次要写几百字节,如果 CPU 轮询 SPI,每次写入要几毫秒,累积起来占用不少算力。用 DMA 可以把这个过程交给硬件。

PIC24EP 的 DMA 配置思路:

// 配置 DMA 通道 0,源地址为数据缓冲区,目标地址为 SPI1BUF DMA0CONbits.SIZE = 0; // 字节模式 DMA0CONbits.DIR = 1; // 从内存到外设 DMA0CONbits.AMODE = 0; // 寄存器间接寻址 DMA0CONbits.MODE = 0; // 连续模式 DMA0STA = __builtin_dmaoffset(&txBuffer); DMA0PAD = (volatile unsigned int)&SPI1BUF; DMA0CNT = len - 1; DMA0CONbits.CHEN = 1; // 使能通道

DMA 启动后,CPU 可以去处理采集算法,等 DMA 传输完成中断再处理下一批。实测下来,CPU 占用从 15% 降到 3% 左右。

提示:DMA 传输期间不要修改源缓冲区,否则数据会错乱。我的做法是双缓冲,一个缓冲区在 DMA 发送时,另一个缓冲区在采集新数据。

3.4 数据存储结构设计

MRAM 512KB 空间,我把它分成几个区域:

  • 0x00000 - 0x00FFF:系统配置区,存设备参数、校准系数
  • 0x01000 - 0x7FFFF:采集数据区,循环存储
  • 0x80000 - 0xFFFFF:预留区,存日志和故障记录

采集数据区用循环队列管理。每条记录包含时间戳、三相电流电压、状态标志,共 32 字节。512KB 减去配置区,能存约 16000 条记录。按 100ms 一条算,能存 26 分钟。如果嫌短,可以降低存储频率,比如 1 秒存一条,能存 4.4 小时。

循环队列的写指针和读指针也存在 MRAM 里,每次写入后更新。这样掉电后重新上电,从 MRAM 读回指针,继续写,不会覆盖未读数据。

typedef struct { uint32_t timestamp; uint16_t current[3]; uint16_t voltage[3]; uint16_t status; uint16_t crc; } Record_t; // 共 32 字节

每条记录加 CRC 校验,读取时验证,防止数据损坏。

4. 常见问题与排查实录

4.1 写入失败或数据错乱

这是最常见的问题,排查思路按顺序来:

第一步,检查 WP 引脚。用万用表量 WP 对地电压,如果是低电平,说明写保护生效了。检查上拉电阻是否焊接,或者 GPIO 是否误拉低。

第二步,检查 SPI 模式。MRAM 支持模式 0 和模式 3,如果 MCU 配成模式 1 或 2,数据会错位。用示波器看 SCK 和 MOSI 的相位关系,确认采样边沿正确。

第三步,检查片选时序。CS 拉低到第一个 SCK 边沿之间要有足够建立时间,MRAM 要求至少 5ns。如果 MCU 太快,可以在 CS 拉低后加几个 NOP。

第四步,检查电源。用示波器看 VDD 纹波,如果超过 100mV,写入可能失败。加去耦电容,或者换 LDO 供电。

4.2 读取数据全为 0xFF 或 0x00

全 0xFF 通常是 MISO 没接好,或者 MRAM 没响应。全 0x00 可能是 CS 一直拉低,或者时钟没输出。

排查方法:用示波器看 MISO 在 CS 拉低后是否有跳变。如果没有,检查 MRAM 焊接是否虚焊,或者换一片芯片试试。

还有一种可能是地址超范围。MR25H40CDF 只有 512KB,地址 0x80000 以上是无效的,读出来可能是随机值。检查代码里的地址计算,别越界。

4.3 高温下写入不稳定

工业现场温度高,MRAM 虽然标称 -40 到 +125 度,但高温下时序余量会变小。我遇到过 70 度时 20MHz 时钟写入偶发失败,降到 10MHz 就稳了。

解决办法:高温环境下降额使用,时钟不要跑满。或者选扩展温度等级的芯片,MR25H40CDF 有工业级和汽车级可选。

4.4 常见问题速查表

现象可能原因排查方法解决措施
写入无效WP 拉低量 WP 电压上拉 WP 或释放 GPIO
数据错位SPI 模式不对示波器看时序改 CKP/CKE 配置
读全 0xFFMISO 断路量通断补焊或换芯片
读全 0x00CS 常低量 CS 电平检查 CS 控制代码
高温失败时序余量不足降温测试降低 SPI 时钟
偶发错误电源纹波大示波器看 VDD加去耦电容
DMA 数据错缓冲区被改检查代码用双缓冲

4.5 几个独家避坑技巧

技巧一:写入后立即回读验证。关键数据写入后,马上读回来对比,不一致就重写。这个习惯帮我抓到了好几次偶发故障。

技巧二:CS 线上加小电容。在 CS 和地之间并一个 100pF 电容,能滤掉高频毛刺,防止误触发。但电容不能太大,否则边沿变缓,影响时序。

技巧三:MRAM 初始化时先读设备 ID。虽然 MR25H40CDF 没有标准 ID 寄存器,但可以读已知地址的配置数据,验证通信正常。如果读出来全是 0xFF,说明硬件有问题,早点发现比运行时出错好。

技巧四:循环队列加磨损均衡。虽然 MRAM 寿命几乎无限,但养成好习惯没坏处。我的做法是写满一圈后,整个区域擦除重写(MRAM 不需要擦除,直接覆盖),避免长期只写同一区域。

技巧五:掉电检测配合写入。如果设备有掉电检测电路,检测到掉电时立即把关键数据写入 MRAM,然后进入低功耗模式。MRAM 写入快,几毫秒就能完成,比 Flash 靠谱得多。

5. 性能实测与优化建议

5.1 实测数据

我在实验室和现场都做了测试,数据如下:

测试项条件结果
单字节写入20MHz SPI1.2us
32 字节记录写入20MHz SPI38us
连续写入 1000 条20MHz SPI38ms
单字节读取20MHz SPI0.8us
32 字节记录读取20MHz SPI26us
高温 85 度写入20MHz SPI偶发失败
高温 85 度写入10MHz SPI稳定
低温 -40 度写入20MHz SPI稳定
掉电数据保持断电 24 小时数据完整

从数据看,20MHz 下写入一条 32 字节记录只要 38us,100ms 周期完全够用。高温下降频到 10MHz 也能满足需求。

5.2 优化建议

第一,合理设置 SPI 时钟。不要盲目追求高速,根据环境温度选频率。常温 20MHz,高温 10MHz,低温可以 20MHz。

第二,用 DMA 解放 CPU。批量写入时 DMA 优势明显,CPU 占用从 15% 降到 3%。

第三,数据压缩。如果采集数据有规律,可以压缩后再存。比如电流电压变化缓慢,可以存差值而不是绝对值,节省空间。

第四,定期自检。上电时读一遍配置区,验证 CRC,确保数据完整。如果 CRC 错,用默认配置恢复。

第五,预留扩展空间。MRAM 容量选大不选小,512KB 看着多,实际用起来很快。如果项目可能扩展,直接上 1MB 的 MR25H10,省得以后换板子。

5.3 与其他方案的对比验证

为了确认 MRAM 方案的优势,我做了对比测试:

用 W25Q64 Flash 存同样数据,100ms 一条,每条 32 字节。Flash 需要按扇区擦除,4KB 一个扇区,能存 128 条。写满 128 条后要擦除整个扇区,擦除时间 45ms。这 45ms 内如果掉电,整个扇区 128 条数据全丢。而 MRAM 没有这个问题,每条独立写入,掉电只影响当前这条。

用 24C512 EEPROM 测试,页写入 5ms,100ms 周期内写一条够用,但寿命只有 100 万次,一天 86 万次,一天多就废了。MRAM 寿命 10^14 次,按 100ms 一次算,能写 3 亿年。

这些对比数据让我更确信选型没错。工业场景要的是可靠,不是便宜。多花几块钱买 MRAM,省的是现场故障的麻烦。

6. 项目收尾与个人体会

这个项目从选型到量产花了三个月,中间踩了不少坑,也积累了一些经验。MRAM 加 PIC24 的组合,在工业采集场景里算是比较稳妥的方案。MRAM 的字节级写入和无限寿命,解决了高频写入和掉电风险;PIC24 的 SPI 和 DMA 资源,保证了实时性和低 CPU 占用。

如果你也在做类似的项目,我的建议是:存储选型别只看价格,算算写入频率和寿命,算算掉电风险,综合下来 MRAM 往往是最优解。SPI 配置别怕麻烦,示波器多看几眼时序,比事后调试省时间。PCB 布局别省事,去耦电容和端接电阻该加就加,工业环境的干扰比你想象的严重。

最后分享一个小技巧:MRAM 的 WP 引脚可以接一个 GPIO,正常运行时拉高,需要保护配置数据时拉低。这样既能写数据,又能防止误改配置,一举两得。我在现场调试时,这个设计帮我避免了好几次误操作导致的数据丢失。

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

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

立即咨询