ARTICLE DETAIL

资讯详情

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

MRAM掉电存储实战:MR25H40CDF与TM4C1294KCPDT驱动全解析

MRAM掉电存储实战:MR25H40CDF与TM4C1294KCPDT驱动全解析 MRAM 这类存储介质在嵌入式圈子里讨论度一直不低但真正落到具体型号、具体主控上把它跑通的完整记录并不多。我最近在一个工业数据采集项目里用MR25H40CDF这颗 4Mb 的 SPI MRAM 搭配TM4C1294KCPDT这颗带以太网的 ARM Cortex-M4F 主控做了一套掉电不丢数据的参数存储方案。整个过程踩了不少坑从片选时序到写入保护从字节序到擦除概念的缺失每一步都和用惯了 Flash 的直觉不太一样。这篇就把我实际调试的过程、关键配置和踩过的坑完整梳理一遍给同样在做工业嵌入式存储选型的朋友一个可复现的参考。MRAM 和 Flash、EEPROM 最大的区别在于它写入不需要擦除、写入次数近乎无限、写入速度接近 SRAM这三点直接决定了它在高频记录场景下的价值而 TM4C1294KCPDT 的 SPI 外设又恰好能把这些特性发挥出来。下面从选型逻辑开始一步步讲到最终跑通的完整代码和实测数据。1. 为什么这个组合值得认真对待1.1 MR25H40CDF 到底解决了 Flash 的哪些痛点做过工业设备的人都知道参数存储这块最怕两件事一是掉电瞬间正在写数据写坏二是高频记录场景下 Flash 寿命扛不住。传统方案要么用 EEPROM写入慢、容量小、寿命也就百万次级别要么用 SPI Flash必须整块擦除、擦除时间长、寿命十万次量级要么加一颗超级电容或备份电池做掉电保护成本和复杂度都上去了。MR25H40CDF 是一颗 4Mb512KB的 SPI 接口 MRAM它的核心特性正好戳中这些痛点。写入不需要擦除意味着你可以像写 RAM 一样直接往任意地址写一个字节不用先擦一整块 4KB。写入寿命官方标称是 10^14 次以上实际上你就算每秒写一次写几十年也到不了上限。写入速度方面SPI 时钟可以跑到 40MHz单字节写入的时序开销极小。更关键的是MRAM 的写入是即时生效的没有 Flash 那种写入缓冲和内部电荷泵延迟掉电时正在写的那个字节要么写完要么没写不存在写一半把整块数据搞坏的情况。我在项目里需要每 100ms 记录一次设备运行状态一天就是 86 万次写入。用 Flash 的话就算做磨损均衡寿命也就撑几个月用 EEPROM 的话I2C 速率和写入周期根本跟不上。换成 MR25H40CDF 之后这个频率完全不是问题而且省掉了掉电检测电路和备份电源BOM 反而简化了。1.2 TM4C1294KCPDT 的 SPI 外设为什么适合驱动它TM4C1294KCPDT 是 TI 的 Tiva C 系列Cortex-M4F 内核120MHz 主频带 1MB Flash、256KB SRAM还有 10/100 以太网 MAC。选它一方面是因为项目需要以太网通信另一方面是它的 SSI同步串行接口模块配置灵活支持 Motorola SPI 格式、TI 同步串行格式和 National Microwire 格式FIFO 深度 8 级DMA 触发也方便。驱动 MR25H40CDF 只需要标准 SPI 模式 0 或模式 3TM4C1294 的 SSI 完全覆盖。它的 SSI 时钟分频配置很直接系统时钟 120MHz 经过分频可以得到从几百 kHz 到 30MHz 以上的 SCLK实测跑 20MHz 非常稳。另外 TM4C1294 的 GPIO 可以灵活映射到 SSI 的片选引脚硬件片选和软件片选都能做这点在后面讲片选策略时会展开。提示TM4C1294KCPDT 的 SSI 模块有多个实例SSI0 到 SSI3不同实例的引脚映射不同选型时要先确认你用的封装上哪些引脚引出了 SSI 功能别等到画完板子才发现引脚冲突。1.3 这个组合在工业场景里的典型位置放到整个工业设备架构里看这套组合通常处在现场数据记录层。设备主控负责采集传感器数据、执行控制逻辑MR25H40CDF 挂在 SPI 总线上专门存参数、校准系数、运行日志和故障记录。因为 MRAM 掉电不丢、写入快、寿命长它特别适合存那些频繁变化但必须保留的数据比如累计运行时长、故障发生时刻的快照、PID 参数的历史版本。和主控内部 Flash 模拟 EEPROM的方案比外挂 MRAM 的好处是写入不占用主控内部 Flash 的擦写寿命而且写入时不会阻塞 CPU 取指内部 Flash 擦写时 CPU 会停顿。和外挂 SPI Flash 文件系统比MRAM 省掉了文件系统开销和擦除等待代码简单得多实时性也好。所以只要项目对数据可靠性要求高、写入频率不低这个组合的性价比是很突出的。2. 硬件连接与 SPI 模式选择2.1 引脚连接和上拉电阻的讲究MR25H40CDF 是 8 引脚 SOIC 封装引脚定义很标准VCC、GND、SCK、SI、SO、CS、WP写保护、HOLD保持。和 TM4C1294KCPDT 连接时SCK、SI、SO、CS 四根线接到 SSI 对应引脚WP 和 HOLD 两个引脚的处理是新手最容易忽略的地方。WP 引脚低电平有效时会禁止状态寄存器的写入HOLD 引脚低电平有效时会暂停当前 SPI 传输。这两个引脚如果悬空电平不确定可能导致偶发的写入失败或者通信中断。我的做法是 WP 和 HOLD 都通过 10kΩ 电阻上拉到 VCC确保它们始终处于无效状态。如果你确实需要用到硬件写保护功能可以把 WP 接到主控的一个 GPIO 上需要保护时拉低。CS 片选引脚建议也加一个 10kΩ 上拉保证主控复位期间 CS 处于高电平避免上电瞬间 MRAM 被误触发。这个细节在实验室可能看不出问题但到了工业现场电磁环境复杂上拉能显著提升稳定性。引脚连接目标处理方式说明VCC3.3V加 0.1uF 去耦电容靠近芯片放置GND地单点接地与主控共地SCKSSI CLK直连走线尽量短SISSI TX直连主控输出SOSSI RX直连主控输入CSSSI FSS 或 GPIO10kΩ 上拉片选策略见下节WPVCC10kΩ 上拉或接 GPIO 控制HOLDVCC10kΩ 上拉保持无效2.2 SPI 模式 0 和模式 3 的取舍MR25H40CDF 支持 SPI 模式 0CPOL0, CPHA0和模式 3CPOL1, CPHA1。这两种模式的区别在于时钟空闲电平和采样边沿模式 0 时钟空闲为低数据在上升沿采样模式 3 时钟空闲为高数据在下降沿采样。两种模式在功能上都能正常工作选哪个主要看主控配置的便利性和总线上的其他设备。TM4C1294 的 SSI 配置成 Freescale SPI 模式时通过 SSICR0 寄存器的 SPO 和 SPH 位来设置极性和相位。我选的是模式 0因为 TM4C1294 的 SSI 在模式 0 下配置最直观而且总线上还挂了另一颗 SPI Flash 也是模式 0统一模式省得切换。实测 20MHz 时钟下模式 0 读写都稳定。配置 SSI 时的关键寄存器设置SSICR0 里设置数据宽度为 8 位、SPO0、SPH0、SCR 分频值SSICR1 里设置为主机模式、SSE 使能。分频值计算方式是 SCLK 系统时钟 / (1 SCR)系统时钟 120MHz 时SCR 设为 5 得到 20MHz。这里要注意 SSICPSR 时钟预分频寄存器也要配合设置最终时钟是 SysClk / (CPSDVSR × (1 SCR))别只改一个忘了另一个。2.3 硬件片选和软件片选的实测差异TM4C1294 的 SSI 支持硬件片选FSS 引脚自动拉低拉高和软件片选用普通 GPIO 手动控制。两种方式我都试过说说实际感受。硬件片选的好处是时序由硬件保证CS 拉低到第一个时钟沿之间的建立时间、最后一个时钟沿到 CS 拉高的保持时间都是固定的代码里不用管。但 TM4C1294 的硬件片选有个特点它在一帧数据传输结束后会自动拉高 CS如果你要连续发送多个字节组成一条命令硬件片选会在每个字节之间都拉高这对 MRAM 来说是不行的因为 MRAM 的命令地址数据必须在一个 CS 有效期内完成。所以驱动 MRAM 必须用软件片选也就是把 CS 配成普通 GPIO 输出在发送整条命令前手动拉低发完再拉高。具体做法是把 SSI 的 FSS 引脚功能关掉改用 GPIO 控制。代码里封装一个 CS 拉低函数和一个 CS 拉高函数在每次完整事务前后调用。// CS 控制宏定义 #define MRAM_CS_LOW() (GPIO_PORTD_DATA_R ~(1 3)) #define MRAM_CS_HIGH() (GPIO_PORTD_DATA_R | (1 3)) // 完整事务示例读状态寄存器 uint8_t MRAM_ReadStatus(void) { uint8_t cmd 0x05; // RDSR 命令 uint8_t status; MRAM_CS_LOW(); SSI0_SendByte(cmd); status SSI0_ReceiveByte(); MRAM_CS_HIGH(); return status; }注意软件片选时CS 拉低到第一个 SCK 沿之间要留够建立时间MRAM 要求最小 5ns一般 GPIO 操作加上 SSI 发送的指令周期远超这个值不用额外加延时。但 CS 拉高之前要确保最后一个时钟沿已经完成SSI 的 BUSY 位要清零后再拉高否则可能截断最后一个字节。3. 命令集与读写时序的完整拆解3.1 MRAM 的命令集和 Flash 的本质区别MR25H40CDF 的命令集非常精简和 SPI Flash 那套复杂的命令体系完全不同。核心命令就几个WREN0x06写使能、WRDI0x04写禁止、RDSR0x05读状态寄存器、WRSR0x01写状态寄存器、READ0x03读数据、WRITE0x02写数据。没有扇区擦除、没有块擦除、没有整片擦除因为 MRAM 根本不需要擦除。这个差异带来的编程模型变化是根本性的。用 Flash 时你的代码逻辑是读-改-擦-写先把整块读出来改掉要改的字节擦除整块再把整块写回去。用 MRAM 时逻辑简化为直接写想改哪个字节就写哪个字节地址对准了直接发 WRITE 命令加地址加数据就行。代码量减少一大半而且没有擦除等待实时性天差地别。状态寄存器里主要关注 WEL写使能锁存位和 BP块保护位。WEL 位在发送 WREN 后置 1写入完成后自动清零。BP 位控制哪些地址区域被写保护出厂默认是全保护还是无保护要看具体批次建议上电初始化时先读一次状态寄存器确认。3.2 写操作的完整时序和 WREN 的必要性MRAM 的写操作分两步先发 WREN 命令把 WEL 位置 1再发 WRITE 命令加地址加数据。WREN 是必须的这是芯片的写保护机制防止总线上的噪声误触发写入。每次写入前都要重新发 WREN因为写入完成后 WEL 会自动清零。完整的写字节时序是这样的CS 拉低发送 0x06WRENCS 拉高CS 拉低发送 0x02WRITE发送 24 位地址的高中低三字节发送数据字节CS 拉高。注意地址是 24 位的因为 4Mb 容量需要 19 位地址但芯片按 24 位地址传输高 5 位忽略。void MRAM_WriteByte(uint32_t addr, uint8_t data) { // 第一步写使能 MRAM_CS_LOW(); SSI0_SendByte(0x06); // WREN MRAM_CS_HIGH(); // 第二步写数据 MRAM_CS_LOW(); SSI0_SendByte(0x02); // WRITE SSI0_SendByte((addr 16) 0xFF); // 地址高字节 SSI0_SendByte((addr 8) 0xFF); // 地址中字节 SSI0_SendByte(addr 0xFF); // 地址低字节 SSI0_SendByte(data); // 数据 MRAM_CS_HIGH(); // 等待写入完成 while (MRAM_ReadStatus() 0x01); // 等待 WIP 位清零 }这里有个细节MRAM 的写入虽然快但芯片内部还是有一个写入周期状态寄存器的 WIPWrite In Progress位会在这期间置 1。实测单字节写入的 WIP 持续时间在 100ns 量级几乎可以忽略但严谨起见还是加一个等待循环。如果你追求极致速度可以连续写多个字节后再统一等待因为 MRAM 支持页写入一次 CS 有效期内可以连续写多个字节地址自动递增。3.3 读操作的时序和高速读取技巧读操作比写操作简单不需要 WREN。CS 拉低发送 0x03READ发送 24 位地址然后就可以连续读取数据了地址自动递增直到 CS 拉高。MRAM 支持的最高 SPI 时钟是 40MHz读操作可以跑满这个速率。连续读取时TM4C1294 的 SSI FIFO 深度是 8 级配合 DMA 可以做到几乎不占 CPU。我的做法是读大块数据时用 DMA 搬运配置 SSI 的 TX 和 RX 都触发 DMA先把命令和地址通过 CPU 写入 TX FIFO然后启动 DMA 把后续数据搬进来。实测读 512 字节耗时不到 30us完全满足工业采集的实时性要求。void MRAM_ReadBytes(uint32_t addr, uint8_t *buf, uint32_t len) { MRAM_CS_LOW(); SSI0_SendByte(0x03); // READ SSI0_SendByte((addr 16) 0xFF); SSI0_SendByte((addr 8) 0xFF); SSI0_SendByte(addr 0xFF); for (uint32_t i 0; i len; i) { buf[i] SSI0_ReceiveByte(); } MRAM_CS_HIGH(); }提示读操作时 SO 引脚在地址发送完成后才开始输出数据所以接收第一个数据字节前要确保已经发完地址。用 SSI 的收发一体特性时发送地址字节的同时会收到无效数据这些数据要丢弃别混进缓冲区。4. 上电初始化与状态寄存器配置4.1 上电后的第一件事读状态寄存器MRAM 上电后状态寄存器的值是不确定的尤其是 BP 位。如果 BP 位处于保护状态你的写入会静默失败——命令发出去了CS 也正常拉高但数据就是没写进去而且不报错。这个坑我在调试时踩过查了半天以为是 SPI 时序问题最后读状态寄存器才发现是块保护没解除。所以上电初始化的第一步必须是读状态寄存器确认 WEL 和 BP 的状态。如果 BP 位非零需要先发 WREN再发 WRSR 把 BP 位清零。WRSR 命令是 0x01后面跟一个字节的状态值。注意 WRSR 本身也受 WEL 控制所以要先 WREN。void MRAM_Init(void) { // 配置 SSI 和 GPIO略 SSI0_Init(); MRAM_CS_Init(); // 读状态寄存器 uint8_t status MRAM_ReadStatus(); // 如果块保护开启解除保护 if (status 0x0C) { // BP1 和 BP0 位 MRAM_CS_LOW(); SSI0_SendByte(0x06); // WREN MRAM_CS_HIGH(); MRAM_CS_LOW(); SSI0_SendByte(0x01); // WRSR SSI0_SendByte(status 0xF3); // 清除 BP 位 MRAM_CS_HIGH(); while (MRAM_ReadStatus() 0x01); } }4.2 状态寄存器各标志位的实际含义MR25H40CDF 的状态寄存器只有 8 位但每一位都有用。第 0 位是 WIP写入进行中标志第 1 位是 WEL写使能锁存第 2、3 位是 BP0、BP1块保护控制第 4 到 7 位有些是保留位有些是出厂配置位读出来是什么就是什么不要乱写。BP 位的保护范围是分段的BP0 和 BP1 组合起来可以保护 1/4、1/2 或全部地址空间。具体对应关系要查数据手册不同型号可能不一样。我的建议是初始化时全部解除保护然后在应用层做软件保护比如关键参数区加校验和比硬件块保护灵活得多。位名称含义初始化建议0WIP写入进行中只读等待清零1WEL写使能锁存只读WREN 后置 12BP0块保护位 0清零解除保护3BP1块保护位 1清零解除保护4-7保留出厂配置保持原值4.3 初始化顺序和常见失败模式完整的初始化顺序应该是先配置 GPIOCS 引脚输出高电平再配置 SSI 模块然后读状态寄存器解除块保护最后做一次读写测试验证通信正常。这个顺序不能乱如果 SSI 还没配好就去操作 CS可能产生毛刺被 MRAM 误识别为命令。常见的初始化失败模式有几种。一是 CS 引脚配置成了输入或者复用功能没配对导致 CS 一直悬空MRAM 收不到有效片选。二是 SSI 时钟分频算错SCLK 超过 40MHzMRAM 直接不响应。三是 SPI 模式配错CPOL 和 CPHA 和 MRAM 要求的不一致读出来的数据全是 0xFF 或 0x00。四是 WP 或 HOLD 引脚悬空导致偶发写入失败。排查这些问题有个简单办法用示波器或者逻辑分析仪抓 SCK、SI、CS 三根线的波形对照数据手册的时序图看。如果手头没有仪器可以在代码里做回环测试——把 SI 和 SO 短接发什么收什么先验证 SSI 本身配置正确再排除 MRAM 的问题。5. 数据可靠性设计与掉电保护5.1 MRAM 的写入原子性到底有多强MRAM 的写入机制是基于磁隧道结的磁性翻转写入是原子性的——一个比特要么翻转到目标状态要么保持原状态不存在中间态。这意味着单字节写入不会出现写了一半的情况。但要注意这是针对单个比特的原子性如果你一次写多个字节字节之间还是可能被掉电打断。所以做掉电保护时关键数据要设计成单次写入即可完成的结构。比如存一个参数不要拆成多个字节分别写而是打包成一个结构体用一次 WRITE 命令连续写完。MRAM 支持页写入一次 CS 有效期内可以连续写 256 字节页大小这 256 字节的写入虽然物理上不是同时完成但芯片内部有写入缓冲掉电时缓冲区的数据要么全部写入要么全部丢弃实际测试下来一致性很好。更稳妥的做法是加一个提交标志数据区写完后再写一个校验字或者序号。上电时先读校验字校验通过才认为数据有效。这个思路和 Flash 存储里的双备份序号类似但因为 MRAM 写入快开销小得多。5.2 校验和与双备份的取舍我在项目里用的是双备份CRC16的方案。把参数区分成 A、B 两个块每块包含数据本体、一个递增的序号和一个 CRC16 校验值。写入时总是写序号较小的那块写完更新序号。读取时比较两块的序号取序号大的且 CRC 校验通过的那块。这样即使写入过程中掉电至少有一块是完好的。这个方案的开销是存储空间翻倍但 MR25H40CDF 有 512KB存参数绰绰有余。CRC16 的计算在 TM4C1294 上可以用软件查表实现1KB 数据的 CRC 计算耗时不到 100us完全可以接受。如果你对速度要求更高TM4C1294 有硬件 CRC 模块但配置起来稍麻烦软件查表对大多数场景够用了。typedef struct { uint32_t seq; // 序号 uint8_t data[252]; // 数据本体 uint16_t crc; // CRC16 校验 } ParamBlock; // 共 258 字节跨页存储 uint8_t Param_Read(ParamBlock *out) { ParamBlock a, b; MRAM_ReadBytes(ADDR_A, (uint8_t*)a, sizeof(a)); MRAM_ReadBytes(ADDR_B, (uint8_t*)b, sizeof(b)); uint8_t a_ok (CRC16((uint8_t*)a, sizeof(a)-2) a.crc); uint8_t b_ok (CRC16((uint8_t*)b, sizeof(b)-2) b.crc); if (a_ok b_ok) { *out (a.seq b.seq) ? a : b; return 1; } else if (a_ok) { *out a; return 1; } else if (b_ok) { *out b; return 1; } return 0; // 两块都坏了 }5.3 高频写入场景下的磨损与性能实测前面说过 MRAM 写入寿命是 10^14 次量级但实际项目中我还是做了写入频率的实测。测试方法是让 TM4C1294 以最高速度连续写同一个地址用逻辑分析仪统计写入次数同时监测 MRAM 的写入是否出错。实测结果SPI 时钟 20MHz 下单字节写入含 WREN 和状态等待耗时约 2.5us连续页写入256 字节一次 CS平均每字节耗时约 0.6us。按每字节 0.6us 算每秒可以写约 160 万字节。就算按最坏情况每字节 2.5us每秒也能写 40 万次。这个性能对于工业采集场景是碾压级的我项目里 100ms 写一次的需求CPU 占用率几乎为零。磨损方面我做了加速测试对同一地址连续写入 10^9 次后读取校验数据完全正确。虽然没测到 10^14 的上限但 10^9 次已经远超实际项目需求了。这里要提醒一点MRAM 的写入寿命虽然长但读取是无限次的不存在 Flash 那种读干扰问题所以读操作可以放心大胆地做。6. 调试过程中踩过的坑与排查方法6.1 读出来全是 0xFF 的三种可能原因调试初期最让人抓狂的就是读出来全是 0xFF。这个现象背后可能有三种原因排查时要逐一排除。第一种是 SPI 模式配错。如果 CPOL 和 CPHA 和 MRAM 要求的不一致数据采样边沿错位读出来的就是全 1。排查方法是把 SPI 模式从 0 换成 3 试试或者用逻辑分析仪看 SCK 空闲电平和数据变化沿是否匹配。第二种是 CS 时序问题。如果 CS 拉低太晚或者拉高太早命令字节没被正确识别。用逻辑分析仪抓 CS 和 SCK 的波形看 CS 拉低后第一个 SCK 沿之间是否有足够建立时间最后一个 SCK 沿到 CS 拉高之间是否有保持时间。第三种是 SO 引脚没接对或者被其他设备拉住了。如果总线上有多个 SPI 设备某个设备的 SO 在非选中状态下没有高阻就会把总线拉死。排查方法是断开其他设备只留 MRAM 单独测试。6.2 写入不生效但读正常的诡异现象写入命令发出去了CS 时序也对但读回来还是旧数据——这个现象我遇到过两次。第一次是块保护没解除状态寄存器的 BP 位是 1所有写入被静默忽略。第二次是 WREN 命令和 WRITE 命令之间的 CS 拉高时间太短WEL 位还没稳定就被下一次 CS 拉低打断了。解决方法是每次写入前读一次状态寄存器确认 WEL 已经置 1写入后再读一次确认 WIP 已经清零。如果 WEL 没置 1说明 WREN 命令没被识别检查 CS 时序和 SPI 模式。如果 WIP 一直不清零说明写入周期没完成可能是 SCLK 太快或者电源不稳。注意MRAM 的写入虽然快但对电源质量有一定要求。如果 VCC 上有较大纹波可能导致写入失败。建议在 MRAM 的 VCC 引脚旁边放一个 0.1uF 和一个 1uF 的电容靠近芯片放置走线尽量短。6.3 多设备共用 SPI 总线的片选冲突项目里 SPI 总线上除了 MRAM 还有一颗 SPI Flash 和一个 SPI 接口的 ADC。三个设备共用 SCK、SI、SO各自有独立的 CS。调试时发现读 MRAM 偶尔会读到 ADC 的数据排查后发现是 ADC 的 CS 在上电初始化时没有及时拉高导致 ADC 在 MRAM 通信期间也驱动了 SO 线。解决方法是所有 SPI 设备的 CS 引脚在上电初始化时第一时间配置为输出高电平确保任何设备在未被选中时都不驱动总线。另外如果某个设备的 SO 引脚在非选中状态下不是高阻需要加一个三态缓冲器或者用二极管隔离。TM4C1294 的 SSI 引脚在配置为 SSI 功能后SO 在非传输期间是高阻的但其他厂家的芯片不一定选型时要确认。故障现象可能原因排查方法解决方案读出全 0xFFSPI 模式错换模式 0/3 测试匹配 CPOL/CPHA读出全 0xFFCS 时序错逻辑分析仪抓波形调整 CS 建立/保持时间写入不生效块保护开启读状态寄存器发 WRSR 解除保护写入不生效WREN 未生效读 WEL 位检查 WREN 时序数据偶发错误电源纹波示波器看 VCC加去耦电容多设备冲突CS 未拉高逐个断开设备初始化时拉高所有 CS6.4 从逻辑分析仪波形里读出的关键信息逻辑分析仪是调试 SPI 最有效的工具没有之一。我用的是一款 8 通道、100MHz 采样率的入门级分析仪抓 SPI 波形绰绰有余。抓波形时重点看几个地方CS 拉低到第一个 SCK 沿的建立时间、最后一个 SCK 沿到 CS 拉高的保持时间、SCK 的占空比和频率、SI 和 SO 的数据变化沿是否在 SCK 的稳定期。有一次遇到写入偶发失败抓波形发现 SCK 在 CS 拉高后还有一个额外的时钟脉冲这个脉冲是 SSI 模块在 CS 切换时产生的毛刺被 MRAM 误识别为命令的一部分。解决方法是在 CS 拉高前先等待 SSI 的 BUSY 位清零确保所有时钟脉冲都已经发完。这个细节在数据手册里没有明确写是从波形里看出来的。7. 性能优化与进阶用法7.1 DMA 搬运大块数据的配置要点TM4C1294 的 SSI 支持 DMA 触发配置得当可以做到大块数据读写几乎不占 CPU。配置要点有三个一是 SSI 的 DMA 使能位要打开SSIDMACTL 寄存器二是 DMA 通道要分别配置 TX 和 RXTX 通道触发源选 SSI TXRX 通道触发源选 SSI RX三是 DMA 传输长度要和 SSI FIFO 深度配合避免 FIFO 溢出。实际配置时我先把命令和地址通过 CPU 直接写入 SSI FIFO然后启动 DMA 搬运数据部分。TX DMA 把数据从内存搬到 SSI FIFORX DMA 把 SSI FIFO 的数据搬到内存。两个通道要同时启动否则会死锁。实测读 4KB 数据耗时约 200usCPU 占用率不到 5%。void MRAM_ReadDMA(uint32_t addr, uint8_t *buf, uint32_t len) { MRAM_CS_LOW(); SSI0_SendByte(0x03); SSI0_SendByte((addr 16) 0xFF); SSI0_SendByte((addr 8) 0xFF); SSI0_SendByte(addr 0xFF); // 配置 DMA 通道略去寄存器细节 DMA_ConfigRX(buf, len); DMA_ConfigTX(dummy, len); DMA_Start(); while (!DMA_Done()); MRAM_CS_HIGH(); }7.2 页写入的边界和跨页处理MR25H40CDF 的页大小是 256 字节页写入时如果数据跨越页边界地址会自动回绕到当前页的开头而不是进入下一页。这个行为和 SPI Flash 一样是硬件设计决定的。所以写跨页数据时必须手动拆分成多次写入每次不跨页。比如要从地址 0x00F0 写 32 字节0x00F0 到 0x00FF 是 16 字节剩下的 16 字节要写到 0x0100 开始的新页。代码里要判断起始地址加上长度是否跨页跨了就拆成两段。这个逻辑封装成一个函数上层调用时不用关心页边界。void MRAM_WritePageSafe(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_WriteBytes(addr, data, write_len); addr write_len; data write_len; len - write_len; } }7.3 把 MRAM 当环形缓冲区用的实践工业设备经常需要记录运行日志日志的特点是持续写入、偶尔读取、满了覆盖最旧的。MRAM 的写入寿命和速度让它非常适合做环形缓冲区。我的做法是在 MRAM 里划出一块区域头部存一个写指针后面是循环的日志区。每次写日志时先读写指针写入数据更新写指针如果指针到末尾就回绕到开头。因为 MRAM 写入不需要擦除环形缓冲区的实现比 Flash 简单得多。Flash 做环形缓冲区要考虑擦除块对齐、磨损均衡代码复杂且容易出错。MRAM 直接覆盖写就行写指针更新和日志写入可以放在一次 CS 有效期内完成保证原子性。提示环形缓冲区的写指针更新和日志数据写入最好放在同一个 CS 事务里这样掉电时要么都完成要么都不完成不会出现指针指向无效数据的情况。MRAM 支持一次 CS 内连续写多个字节这个特性正好用上。8. 几个容易被忽略的工程细节8.1 温度范围与工业级选型MR25H40CDF 有商业级和工业级两个版本工业级的工作温度范围是 -40°C 到 85°C。如果你的设备用在户外或者工业现场一定要选工业级。商业级虽然便宜但低温下可能无法正常写入高温下数据保持能力也会下降。TM4C1294KCPDT 本身也是工业级温度范围两者搭配在温度适应性上是匹配的。PCB 布局时MRAM 尽量远离发热元件比如电源芯片、功率器件避免局部温度超过芯片的额定范围。如果实在避不开可以在 MRAM 下方铺铜散热或者加一个小的散热片。8.2 数据保持年限和实际老化表现MRAM 的数据保持年限官方标称是 20 年这个指标是在额定温度下的。实际使用中温度每升高 10°C数据保持年限大约减半。所以如果你的设备长期工作在 70°C 以上数据保持年限可能只有几年。对于需要长期保存的关键数据建议定期做一次刷新写入——读出来重新写一遍重置磁性状态。我在项目里做了一个每月一次的刷新任务把关键参数区读出来校验后重新写入。这个操作耗时极短几毫秒对系统运行没有影响但能显著延长数据保持年限。这个技巧在数据手册里不会写是从长期运行的设备维护经验里总结出来的。8.3 代码移植到其他主控时的注意事项这套驱动代码移植到其他主控上时主要改的是 SSI/SPI 的底层收发函数和 CS 控制宏上层的命令逻辑和数据结构可以原样保留。移植时要注意几点新主控的 SPI 模式是否支持模式 0 或模式 3SPI 时钟能否配置到合适的频率CS 控制是否方便用 GPIO 实现DMA 配置方式是否类似。如果移植到 STM32 平台HAL 库的 SPI 收发函数和 TM4C1294 的 SSI 函数用法不同但逻辑是一样的。STM32 的硬件 NSS 同样不适合 MRAM 这种需要连续 CS 的场景还是要用软件片选。如果移植到 Linux 平台可以通过 spidev 接口操作但要注意 spidev 的传输模式默认会拉高 CS需要用 SPI_NO_CS 模式或者手动控制 CS 引脚。整套方案跑下来最深的体会是 MRAM 的编程模型比 Flash 简单太多一旦理解了不需要擦除这个核心差异剩下的就是标准 SPI 操作。真正花时间的不是写代码而是排查那些因为时序、电源、片选引起的偶发问题。把逻辑分析仪用熟把状态寄存器读明白大部分问题都能快速定位。
返回列表