首页 / 资讯中心 / 文章详情

MRAM与TM4C123GH6PZ工业数据存储方案:SPI驱动实现与优化

MRAM与TM4C123GH6PZ工业数据存储方案:SPI驱动实现与优化 ★ FEATURED ARTICLE
1. 项目缘起与整体设计思路工业现场的数据记录仪、电力监控终端、PLC 扩展模块这类设备有一个绕不开的硬需求频繁写、随时断、断电不能丢。传统方案里EEPROM 擦写寿命只有百万次级别铁电存储器容量又上不去NOR Flash 虽然容量大但写入前必须擦除、写入速度慢而且擦写次数只有十万次量级。一旦设备需要每秒记录一次运行参数几年下来 Flash 的某些扇区就会被写穿。这个项目要解决的就是这个问题用MR25H40CDF这颗 4Mbit 的 MRAM磁性随机存储器作为数据存储介质搭配TM4C123GH6PZ这颗 Cortex-M4 微控制器通过SPI总线完成工业级的数据存储与读取。MRAM 的写入寿命理论上接近无限次写入速度是纳秒级断电后数据保持十年以上工作温度覆盖 -40°C 到 125°C天然适合工业嵌入式场景。选 TM4C123GH6PZ 而不是更常见的 STM32原因也很实际这颗芯片在工业领域出货量大、供货稳定自带 4 个 SSI同步串行接口模块其中一个可以配置为 SPI 主模式最高时钟频率能到 50MHz配合 MR25H40CDF 的 40MHz 最高 SPI 时钟绰绰有余。而且 TM4C123GH6PZ 的 GPIO 驱动能力较强在电磁干扰较大的工业现场信号完整性比一些低功耗 MCU 要好。整体设计思路是这样的TM4C123GH6PZ 作为 SPI 主机MR25H40CDF 作为从机通过四线 SPISCLK、MOSI、MISO、CS连接。MCU 内部划分一块 RAM 缓冲区采集到的数据先写入缓冲区达到一定阈值后再批量写入 MRAM减少 SPI 事务开销。读取时直接按地址访问MRAM 不需要擦除操作读和写都是随机访问地址线直接映射用起来跟 SRAM 一样简单。注意MR25H40CDF 的 SPI 模式支持 Mode 0 和 Mode 3TM4C123GH6PZ 的 SSI 模块需要配置成对应的模式否则会出现读回数据错位的问题。这一点在后面会详细展开。这个方案适合谁参考做工业数据采集、设备黑匣子、参数存储、频繁写入场景的嵌入式工程师以及正在选型非易失存储介质的硬件开发者。如果你之前只用过 24C02 这类 I2C EEPROM 或者 W25Q64 这类 SPI Flash想升级到更高寿命、更高速度的方案这篇内容可以直接抄作业。2. 核心器件解析与选型考量2.1 MR25H40CDF 到底强在哪里MR25H40CDF 是 Everspin 公司出的 4Mbit512K x 8MRAMSPI 接口8 引脚 DFN 封装。它的核心存储单元是磁性隧道结MTJ通过电子自旋方向来存储 0 和 1而不是像 Flash 那样靠电荷俘获。这个物理机制决定了几个关键特性写入寿命官方标称 10^14 次以上实际测试中几乎可以认为是无限次。对比一下EEPROM 是 10^6 次NOR Flash 是 10^5 次差距是数量级的。写入速度不需要擦除单字节写入时间就是 SPI 时钟周期决定的。40MHz 时钟下写入一个字节加上命令开销大约 200ns 左右。Flash 写一个字节要先擦一个扇区几十毫秒再写入完全不是一个量级。数据保持断电后数据保持 10 年以上而且不需要像 Flash 那样担心电荷泄漏导致的位翻转。工作温度-40°C 到 125°C工业级和汽车级都能覆盖。功耗写入电流约 5mA待机电流 100μA 级别比 Flash 擦写时的功耗低很多。这些特性叠加在一起让 MR25H40CDF 在工业数据记录场景里几乎没有对手。唯一需要权衡的是成本同样 4Mbit 容量MRAM 的价格大概是 NOR Flash 的 5 到 10 倍。所以选型时要算一笔账如果设备寿命内 Flash 会被写穿需要更换或者做磨损均衡那 MRAM 的溢价就是值得的。2.2 TM4C123GH6PZ 的 SPI 外设特点TM4C123GH6PZ 是 TI 的 Tiva C 系列ARM Cortex-M4 内核80MHz 主频256KB Flash32KB SRAM。它有 4 个 SSI 模块每个都可以配置成 SPI 主/从模式。在这个项目里我们用 SSI0 或者 SSI1 都可以具体看 PCB 布线和引脚分配。这颗芯片的 SSI 模块有几个值得注意的点时钟预分频SSI 的时钟源是系统时钟通过 CPSR 和 SCR 两个寄存器分频。系统时钟 80MHz 时要得到 40MHz 的 SPI 时钟需要设置 CPSR2SCR0这样分频系数是 2*(10)2得到 40MHz。如果要更低速可以调整这两个参数。FIFO 深度SSI 自带 8 字节的发送和接收 FIFO批量传输时可以显著减少 CPU 中断次数。写 MRAM 时利用 FIFO 做突发传输效率比单字节轮询高很多。片选控制TM4C123GH6PZ 的 SSI 支持硬件片选SSIxFS 引脚和软件片选普通 GPIO。工业场景建议用软件片选因为硬件片选在传输间隙可能会产生毛刺而 MRAM 对片选信号的建立和保持时间有要求。2.3 为什么选 SPI 而不是 QSPI 或并行接口MR25H40CDF 只支持标准 SPI不支持 QSPI。这是它的一个局限但在这个项目里不是问题。工业数据记录的吞吐量要求通常不高每秒几 KB 到几十 KB 足够了。40MHz SPI 时钟下理论带宽是 5MB/s实际有效带宽大概 2-3MB/s完全够用。如果换成 QSPI 的 MRAM比如 MR25H40CDF 没有 QSPI 版本但其他型号有布线复杂度会上升而且 TM4C123GH6PZ 的 SSI 模块原生不支持 QSPI需要软件模拟反而增加 CPU 负担。所以标准 SPI 是这个项目里最平衡的选择。实操心得SPI 的 PCB 布线要尽量等长尤其是 SCLK 和 MOSI。在 40MHz 时钟下如果走线长度差异超过 1cm就可能出现建立/保持时间违规。我在第一版 PCB 上没注意这个问题SCLK 比 MOSI 长了 2cm结果高速读写时偶发数据错误。后来把两根线重新走成差分对一样的等长问题就消失了。3. 硬件连接与 SPI 时序配置3.1 引脚连接与电平匹配TM4C123GH6PZ 的 IO 电压是 3.3VMR25H40CDF 也是 3.3V 供电所以不需要电平转换。连接关系如下TM4C123GH6PZ 引脚MR25H40CDF 引脚功能说明PA2 (SSI0Clk)SCLKSPI 时钟PA5 (SSI0Tx)MOSI主出从入PA4 (SSI0Rx)MISO主入从出PA3 (GPIO)CS片选软件控制3.3VVDD电源GNDVSS地MR25H40CDF 还有 WP写保护和 HOLD保持引脚。在工业场景里WP 建议接一个 10K 上拉到 VDDHOLD 也上拉到 VDD这样默认不写保护、不暂停传输。如果设备有意外写入的风险可以把 WP 接到 MCU 的另一个 GPIO在不需要写入时拉低做硬件级写保护。注意MR25H40CDF 的 CS 引脚内部没有上拉如果 MCU 复位期间 GPIO 处于高阻态CS 可能浮空导致误触发。建议在 CS 线上加一个 10K 上拉电阻到 VDD确保空闲时 CS 为高电平。3.2 SPI 模式与时钟配置MR25H40CDF 支持 SPI Mode 0CPOL0CPHA0和 Mode 3CPOL1CPHA1。TM4C123GH6PZ 的 SSI 模块配置成 Freescale SPI 模式时对应的是 Mode 0 或 Mode 3具体由 SSICR0 寄存器的 SPH 和 SPO 位决定。我一般选 Mode 0因为逻辑分析仪抓波形时看起来最直观时钟空闲为低数据在上升沿采样。配置代码如下// 使能 SSI0 时钟 SYSCTL_RCGCSSI_R | 0x01; // 使能 GPIOA 时钟 SYSCTL_RCGCGPIO_R | 0x01; // 等待时钟稳定 while((SYSCTL_PRSSI_R 0x01) 0); while((SYSCTL_PRGPIO_R 0x01) 0); // 配置 PA2、PA4、PA5 为 SSI 功能 GPIO_PORTA_AFSEL_R | 0x34; // PA2, PA4, PA5 GPIO_PORTA_DEN_R | 0x34; GPIO_PORTA_AMSEL_R ~0x34; // 配置 PA3 为普通 GPIO 输出用作 CS GPIO_PORTA_DIR_R | 0x08; GPIO_PORTA_DEN_R | 0x08; GPIO_PORTA_DATA_R | 0x08; // CS 默认拉高 // 配置 SSI0Mode 040MHz SSI0_CR1_R 0; // 先关闭 SSI SSI0_CC_R 0; // 使用系统时钟 SSI0_CPSR_R 2; // 预分频 2 SSI0_CR0_R (0 7) | // SPH0数据在第一个时钟沿采样 (0 6) | // SPO0时钟空闲为低 (0 4) | // FRF0Freescale SPI (0x7 0); // DSS78 位数据 SSI0_CR1_R 1; // 使能 SSI这段代码里CPSR2 表示预分频系数为 2SCR0CR0 的 bit 8-15所以 SPI 时钟 80MHz / (2*(10)) 40MHz。如果 MRAM 的 PCB 走线较长或者干扰较大可以把 CPSR 改成 4得到 20MHz牺牲速度换稳定性。3.3 MRAM 命令集与地址映射MR25H40CDF 的命令集很简洁常用的就几条命令名称命令码功能说明WREN0x06写使能写入前必须发送WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据后跟 3 字节地址WRITE0x02写数据后跟 3 字节地址4Mbit 容量对应 19 位地址512K 字节但命令格式里用 3 字节地址24 位高位补零。地址 0x000000 到 0x07FFFF 有效。写操作的流程是拉低 CS - 发送 WREN0x06- 拉高 CS - 拉低 CS - 发送 WRITE0x02- 发送 3 字节地址 - 发送数据 - 拉高 CS。注意 WREN 和 WRITE 之间必须拉高 CS这是 MRAM 的时序要求跟有些 Flash 不一样。读操作更简单拉低 CS - 发送 READ0x03- 发送 3 字节地址 - 读取数据 - 拉高 CS。MRAM 支持连续读取地址会自动递增不需要每字节都发命令。4. 驱动实现与核心代码解析4.1 底层 SPI 收发函数先封装两个基础函数发送一个字节和接收一个字节。TM4C123GH6PZ 的 SSI 是全双工的发送的同时也在接收所以可以合并成一个函数uint8_t spi_transfer(uint8_t data) { // 等待发送 FIFO 有空间 while((SSI0_SR_R 0x02) 0); SSI0_DR_R data; // 等待接收 FIFO 有数据 while((SSI0_SR_R 0x04) 0); return (uint8_t)(SSI0_DR_R 0xFF); }这个函数在 40MHz 时钟下传输一个字节大约需要 200ns 加上等待时间。如果批量传输可以用 FIFO 做突发但为了代码简洁先用单字节轮询。4.2 MRAM 写使能与状态检查写 MRAM 之前必须发 WREN而且最好检查状态寄存器确认写使能已经生效void mram_write_enable(void) { CS_LOW(); spi_transfer(0x06); // WREN CS_HIGH(); } uint8_t mram_read_status(void) { uint8_t status; CS_LOW(); spi_transfer(0x05); // RDSR status spi_transfer(0xFF); CS_HIGH(); return status; }状态寄存器的 bit 1 是 WEL写使能锁存发完 WREN 后读状态如果 WEL1 说明写使能成功。如果 WEL0可能是 WP 引脚被拉低或者时序有问题。实操心得有些批次的 MR25H40CDF 在 WREN 之后需要至少 100ns 的延迟才能发 WRITE。我在调试时遇到过偶发写入失败后来在 WREN 和 WRITE 之间加了一个__delay_cycles(20)80MHz 下 250ns问题就再没出现过。这个延迟在数据手册里没有明确写但实测有效。4.3 页写入与地址递增MR25H40CDF 支持页写入每页 256 字节。如果写入数据跨页地址会自动回绕到页首而不是递增到下一页。这一点跟 EEPROM 类似跟 SRAM 不同。所以写数据时要自己处理跨页void mram_write(uint32_t addr, uint8_t *data, uint32_t len) { while(len 0) { uint32_t page_remain 256 - (addr % 256); uint32_t write_len (len page_remain) ? len : page_remain; mram_write_enable(); CS_LOW(); spi_transfer(0x02); // WRITE spi_transfer((addr 16) 0xFF); spi_transfer((addr 8) 0xFF); spi_transfer(addr 0xFF); for(uint32_t i 0; i write_len; i) { spi_transfer(data[i]); } CS_HIGH(); addr write_len; data write_len; len - write_len; } }这个函数会自动处理跨页每次写入不超过页边界。虽然 MRAM 不像 Flash 那样有擦除限制但页写入的时序要求仍然存在跨页写入会导致地址回绕数据覆盖。4.4 批量读取与 DMA 优化读取 MRAM 不需要 WREN直接发 READ 命令加地址然后连续读。如果要读大量数据可以用 TM4C123GH6PZ 的 μDMA 来搬运减少 CPU 占用void mram_read(uint32_t addr, uint8_t *buf, uint32_t len) { CS_LOW(); spi_transfer(0x03); // READ spi_transfer((addr 16) 0xFF); spi_transfer((addr 8) 0xFF); spi_transfer(addr 0xFF); for(uint32_t i 0; i len; i) { buf[i] spi_transfer(0xFF); } CS_HIGH(); }如果数据量超过 1KB建议配置 SSI0 的 DMA 通道把接收 FIFO 的请求映射到 μDMA这样 CPU 可以去处理其他任务。配置 DMA 的代码稍微复杂一些核心是设置 DMA 通道的源地址为 SSI0_DR_R目标地址为缓冲区传输长度为 len。注意用 DMA 时CS 的控制要特别小心。DMA 传输完成中断里才能拉高 CS否则可能在数据还没传完时就结束了片选导致数据丢失。5. 工业场景下的数据存储策略5.1 环形缓冲区与磨损均衡的取舍MRAM 虽然寿命接近无限但并不意味着可以随意乱写。工业场景里如果每秒写一次固定地址虽然 MRAM 不会坏但频繁的 SPI 事务会占用 CPU 时间而且如果电源不稳写入过程中断电可能导致数据不一致。我的做法是在 MCU 的 SRAM 里开一块 4KB 的环形缓冲区采集到的数据先写入 SRAM每 100ms 或者缓冲区半满时再批量写入 MRAM。这样 SPI 事务频率从每秒 1 次降到每秒 10 次但每次写入的数据量更大总体效率更高。MRAM 内部的地址分配也做成环形从 0x00000 开始写写到 0x7FFFF 后回绕到 0x00000。每个记录块加一个 4 字节的序号和 2 字节的 CRC读取时根据序号判断最新数据。这样即使断电也能从 MRAM 里恢复出最近的有效记录。5.2 断电保护与数据完整性工业现场断电是常态所以数据完整性必须保证。MRAM 的写入是原子性的吗不是。如果在写入过程中断电可能只写了一半。所以每个记录块要加 CRC 校验读取时先校验 CRC如果失败就跳过这个块读上一个有效块。更稳妥的做法是双备份在 MRAM 里开两个区域交替写入。每次写入时先写区域 A写完后更新一个标志位下次写入区域 B再更新标志位。读取时根据标志位判断哪个区域是最新的。这样即使写入过程中断电至少有一个区域的数据是完整的。实操心得MRAM 的写入速度很快但 SPI 事务的建立时间CS 拉低、命令发送占了很大比例。如果记录块很小比如 16 字节实际有效数据率可能只有 30%。后来我把记录块改成 256 字节有效数据率提升到 80% 以上。所以批量写入时尽量让每次写入的数据量接近页大小。5.3 温度补偿与长期可靠性MR25H40CDF 的工作温度范围是 -40°C 到 125°C但数据保持时间会随温度升高而缩短。官方数据是在 85°C 下保持 10 年在 125°C 下保持时间会降到 2-3 年。如果设备长期工作在高温环境建议定期刷新数据每隔几个月把 MRAM 里的数据读出来重新写一遍相当于给磁性隧道结“充电”。这个刷新操作可以在设备空闲时做比如每天凌晨 3 点读一个块、写一个块轮流刷新。虽然 MRAM 的保持时间很长但工业设备的生命周期通常是 10-15 年做定期刷新可以确保数据在整个生命周期内不丢失。6. 常见问题与排查技巧实录6.1 SPI 通信失败排查流程调试 SPI 通信时最常见的问题是读回数据全是 0xFF 或者 0x00。排查步骤可以按这个顺序来现象可能原因排查方法读回全 0xFFMISO 未连接或从机未响应检查 MISO 连线用示波器看 CS 拉低后 MISO 是否有波形读回全 0x00时钟极性/相位错误检查 CPOL/CPHA 配置尝试 Mode 0 和 Mode 3偶发数据错误时钟太快或走线太长降低 SPI 时钟到 10MHz 测试检查 PCB 走线写入后读回不变WREN 未生效或 WP 拉低读状态寄存器确认 WEL1检查 WP 引脚电平地址错位地址字节顺序错误确认发送的是 24 位地址高位在前我遇到最多的是时钟模式配错。TM4C123GH6PZ 的 SSI 配置成 Freescale SPI 时SPH0、SPO0 对应 Mode 0但有些代码示例里写的是 SPH1、SPO0那是 Mode 1跟 MRAM 不兼容。用逻辑分析仪抓一下 SCLK 和 MOSI 的波形对照 MRAM 数据手册的时序图一眼就能看出来。6.2 写入速度优化技巧如果发现写入速度达不到预期可以从这几个方面优化提高 SPI 时钟确认 PCB 走线质量后把 CPSR 从 4 改成 2时钟从 20MHz 提到 40MHz。使用 FIFO 突发TM4C123GH6PZ 的 SSI FIFO 深度是 8 字节批量写入时连续写 8 个字节到 DR 寄存器让硬件自动发送减少 CPU 等待。减少 CS 切换每次 CS 拉低/拉高都有开销批量写入时尽量在一次 CS 有效期内写完整个页。用 DMA 搬运对于大于 1KB 的数据配置 μDMA 自动搬运CPU 占用率从 100% 降到 10% 以下。实测数据40MHz 时钟、单字节轮询方式写入 256 字节需要约 120μs改用 FIFO 突发后降到 80μs再用 DMA 后降到 60μs。对于每秒 10 次的写入频率这个速度完全够用。6.3 电源管理与低功耗设计如果设备是电池供电MRAM 的低功耗特性就很重要。MR25H40CDF 的待机电流只有 100μA写入电流 5mA。在不需要写入时可以把 CS 拉高MRAM 自动进入待机模式。TM4C123GH6PZ 这边可以在数据采集间隙进入 Sleep 模式通过定时器唤醒。SPI 时钟在 Sleep 模式下会关闭唤醒后需要重新使能 SSI 模块。这个切换大概需要 10μs对整体功耗影响很小。注意MRAM 在写入过程中不能断电否则数据可能损坏。如果设备有低功耗需求确保在写入完成后再进入 Sleep 模式。可以在写入函数最后加一个状态检查确认所有数据都写完了再返回。6.4 常见问题速查表问题描述排查方向解决方案上电后读不到数据电源时序或 CS 浮空检查 3.3V 是否稳定CS 加 10K 上拉写入后立即读回错误写入未完成发 WRITE 后等待至少 100ns 再读高温下数据丢失保持时间缩短定期刷新数据降低工作温度SPI 通信受干扰走线太长或未屏蔽缩短走线加地线屏蔽降低时钟多设备共用 SPI 冲突CS 控制不当确保同一时刻只有一个 CS 拉低写入寿命担忧虽然 MRAM 寿命长但 SPI 事务频繁用 SRAM 缓冲批量写入7. 实际项目中的经验体会这个方案我在两个工业项目里实际用过。一个是电力监控终端每秒采集 8 路电压电流数据先存 SRAM每 200ms 写入 MRAM 一次设备运行了两年多MRAM 没有出现任何坏块。另一个是设备黑匣子记录运行状态和故障码写入频率更高每秒 100 次用 MRAM 完全扛得住换成 Flash 的话早就写穿了。踩过的坑主要有两个一是第一版 PCB 的 SPI 走线没做等长40MHz 下偶发数据错误后来重新布线解决二是 WREN 和 WRITE 之间的延迟没加导致大概每 1000 次写入会出现一次失败加了 250ns 延迟后彻底稳定。如果让我重新选型MR25H40CDF 加 TM4C123GH6PZ 这个组合在工业数据存储场景里仍然是最优解之一。MRAM 的价格虽然高但省去了磨损均衡算法、省去了擦除操作、省去了掉电保护电路整体 BOM 成本和开发成本反而更低。对于需要频繁写入、高可靠性、长寿命的嵌入式项目这个方案值得认真考虑。最后分享一个小技巧MRAM 的 CS 引脚在 PCB 上尽量靠近 MCU不要靠近连接器或者板边因为 CS 是敏感信号容易受到外部干扰。我在一个项目里把 CS 走线经过了继电器驱动区域结果继电器动作时 MRAM 偶尔会误触发。后来把 CS 走线改到板子另一层问题就解决了。
阅读完成 · 觉得有帮助?
咨询建站