拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

STM32F745ZG 驱动 MR25H40CDF MRAM:SPI 配置与工业数据存储实战

STM32F745ZG 驱动 MR25H40CDF MRAM:SPI 配置与工业数据存储实战

1. 为什么 MRAM 在工业嵌入式场景里越来越受关注

搞过工业设备数据记录的朋友应该都有体会,选存储介质这件事,看着简单,实际上坑特别多。EEPROM 擦写寿命十万次,频繁记录的场合撑不了多久;NOR Flash 写入前必须擦除整个扇区,掉电时机不对就丢数据;FRAM 倒是好用,但容量小、价格高,大一点的型号不好买。这几年 MRAM 慢慢进入大家的视野,MR25H40CDF 就是其中一颗很有代表性的芯片。

MR25H40CDF 是 Everspin 出的 4Mbit 串行 MRAM,SPI 接口,512K 字节容量,3.3V 供电,工业级温度范围覆盖 -40 到 105 摄氏度。它最大的特点就是:写入不需要擦除、没有写入延迟、擦写寿命理论上无限、掉电数据不丢。这几点加在一起,对于工业数据记录、参数存储、日志缓存这类场景来说,几乎是理想方案。

我这次用的主控是 STM32F745ZG,Cortex-M7 内核,216MHz 主频,片上带 Quad-SPI 和多个普通 SPI 外设。选它是因为这个项目本身还要跑一些实时控制逻辑,F7 的算力和外设资源都够用,而且 STM32 的 HAL 库生态成熟,SPI 驱动写起来快。整套方案的目标很明确:用 MR25H40CDF 做非易失数据存储,STM32F745ZG 通过 SPI 总线读写,实现工业现场的参数保存和运行日志记录。

这篇文章我会把整个链路拆开讲:从 MRAM 的选型逻辑、SPI 硬件设计、CubeMX 配置、HAL 库驱动编写,到实际调试中遇到的问题和排查方法。如果你手头正好有类似的存储需求,或者单纯想了解一下 MRAM 怎么用,这篇内容应该能帮你少走一些弯路。

2. 方案整体设计与核心选型思路

2.1 为什么选 MRAM 而不是 EEPROM 或 Flash

先把这个事情说清楚,因为选型选错了后面全是白费功夫。工业场景下的非易失存储,常见选项就那么几个:EEPROM、NOR Flash、FRAM、MRAM。我列个表对比一下,这样更直观。

特性EEPROMNOR FlashFRAMMRAM (MR25H40CDF)
擦写寿命约100万次约10万次10^14次无限次
写入前擦除不需要需要不需要不需要
写入速度慢(ms级)慢(ms级)快(ns级)快(ns级)
掉电保持10年+10年+10年+20年+
容量范围小中小中
接口I2C/SPISPII2C/SPISPI
单位成本低低高中高

从表里能看出来,MRAM 在寿命和写入速度上优势明显。有人可能会问,FRAM 不是也很快吗?对,但 FRAM 的容量通常偏小,而且价格更贵,供货也不如 MRAM 稳定。MR25H40CDF 的 512KB 容量对于大多数参数存储和日志记录场景已经够用了。

还有一个很关键的点:MRAM 的写入行为和普通 RAM 几乎一样,你直接往地址里写数据就行,不需要关心扇区边界、不需要先擦后写。这意味着驱动层可以做得非常简单,不用维护复杂的擦除管理逻辑。对于工业设备这种要求长期稳定运行、维护成本高的场景,简单就意味着可靠。

2.2 STM32F745ZG 的 SPI 外设分配

STM32F745ZG 有多个 SPI 接口,SPI1 到 SPI6,其中 SPI1/4/5/6 支持最高 50MHz 时钟,SPI2/3 最高 25MHz。MR25H40CDF 的 SPI 最高支持 40MHz,所以理论上用 SPI1 可以跑到比较高的速率。

但实际选哪个 SPI 口,不能只看速度。我当时的考虑是:

  • SPI1 通常被板上的其他高速外设占用(比如某些传感器或显示屏),需要确认引脚复用情况
  • SPI2/3 速度够用,而且引脚布局更方便走线
  • 如果后续要接多个 SPI 从设备,需要提前规划片选引脚

最后我选了 SPI2,时钟配置到 20MHz 左右。为什么不上更高?因为工业现场的 PCB 走线往往比较长,信号完整性是个现实问题,20MHz 在稳定性和速度之间是个比较好的平衡点。后面调试部分我会详细说这个取舍。

2.3 硬件连接与片选设计

MR25H40CDF 的引脚不多,核心就是 SPI 四线加片选:

  • SCLK:SPI 时钟
  • SI:数据输入(MOSI)
  • SO:数据输出(MISO)
  • CS:片选,低有效
  • VDD:3.3V
  • VSS:地

片选这块有个细节值得说。STM32 的 SPI 外设支持硬件片选(NSS)和软件片选两种方式。硬件片选由 SPI 外设自动控制,时序更精确,但灵活性差,多个从设备时管理麻烦。软件片选就是用普通 GPIO 手动拉低拉高,时序由代码控制,灵活但需要自己保证时序正确。

我选的是软件片选。原因很简单:工业板子上往往不止一个 SPI 从设备,用软件片选可以自由管理多个设备的片选信号,而且调试的时候更容易用逻辑分析仪抓波形。代价就是代码里要严格保证片选拉低和拉高的时机,这个后面驱动部分会讲。

硬件上还有两个容易忽略的点:一是 CS 引脚最好加上拉电阻,防止上电瞬间浮空导致误触发;二是 SCLK 和 MOSI 走线如果比较长,建议串接小电阻(22到33欧姆)做阻抗匹配,减少过冲和振铃。

3. MR25H40CDF 的 SPI 通信协议细节

3.1 命令集与操作码

MR25H40CDF 的 SPI 命令集比较简洁,常用的就几条:

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

注意 WRITE 操作之前必须先发 WREN 命令,这是很多 SPI 存储芯片的通用规则。MRAM 虽然写入不需要擦除,但写保护机制还是有的,WREN 就是用来解除写保护的。

状态寄存器的 bit0 是 WEL(Write Enable Latch),发完 WREN 之后这位会置1,写完数据后自动清零。调试的时候如果发现写不进去,第一件事就是读状态寄存器看 WEL 位是不是1。

3.2 读写时序分析

读操作的时序是这样的:CS 拉低,发送 0x03,然后发送 3 字节地址(MR25H40CDF 是 19 位地址,但用 3 字节传输,高位补0),接着时钟继续输出,数据就从 SO 引脚出来了。可以连续读,地址自动递增,直到 CS 拉高。

写操作的时序类似:CS 拉低,先发 WREN(0x06),CS 拉高;然后 CS 再拉低,发 WRITE(0x02),发 3 字节地址,然后发数据。数据可以连续写,地址自动递增。写完最后一个字节后 CS 拉高,芯片内部开始写入周期。

这里有个关键点:MRAM 的写入是几乎无延迟的,不像 Flash 需要等几毫秒的写入周期。但状态寄存器的 WEL 位在 CS 拉高后需要一点时间才能反映最新状态,所以连续写操作之间建议稍微加一点延时,或者每次写之前都重新发 WREN。

3.3 SPI 模式选择

MR25H40CDF 支持 SPI Mode 0 和 Mode 3。Mode 0 是 CPOL=0、CPHA=0,时钟空闲低电平,第一个边沿采样;Mode 3 是 CPOL=1、CPHA=1,时钟空闲高电平,第二个边沿采样。

我用的是 Mode 0,这也是最常用的配置。在 CubeMX 里配置 SPI 的时候,CPOL 和 CPHA 都设成 Low 就行。如果你发现读出来的数据全是 0xFF 或者乱码,先检查一下模式设置对不对,这是最常见的坑之一。

4. STM32F745ZG 的 CubeMX 配置与驱动实现

4.1 CubeMX 中的 SPI 参数配置

打开 CubeMX,选好 STM32F745ZG 之后,找到 SPI2 进行配置。关键参数如下:

  • Mode:Full-Duplex Master
  • Data Size:8 Bits
  • Clock Polarity:Low
  • Clock Phase:1 Edge(也就是第一个边沿)
  • NSS:Software(软件片选)
  • Baud Rate Prescaler:根据主频计算,目标是 20MHz 左右
  • First Bit:MSB First

波特率这块说一下计算过程。STM32F745ZG 的 APB1 总线时钟我配置的是 54MHz,SPI2 挂在 APB1 上。分频系数可选 2、4、8、16、32、64、128、256。54MHz 除以 4 等于 13.5MHz,除以 2 等于 27MHz。27MHz 虽然没超过 MR25H40CDF 的 40MHz 上限,但考虑到走线损耗,我选了 4 分频,实际时钟 13.5MHz。实测这个速率下读写都很稳定,对于数据记录场景完全够用。

另外别忘了配置一个 GPIO 输出引脚作为 CS 片选,初始电平设为高。

4.2 HAL 库驱动代码实现

CubeMX 生成代码之后,SPI 的初始化已经自动完成了。我们需要自己写的是 MRAM 的读写函数。下面是我实际用的代码,基于 HAL 库。

先定义片选操作宏:

#define MRAM_CS_LOW() HAL_GPIO_WritePin(MRAM_CS_GPIO_Port, MRAM_CS_Pin, GPIO_PIN_RESET) #define MRAM_CS_HIGH() HAL_GPIO_WritePin(MRAM_CS_GPIO_Port, MRAM_CS_Pin, GPIO_PIN_SET)

写使能函数:

void MRAM_WriteEnable(void) { uint8_t cmd = 0x06; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, &cmd, 1, 100); MRAM_CS_HIGH(); }

读状态寄存器:

uint8_t MRAM_ReadStatus(void) { uint8_t cmd = 0x05; uint8_t status = 0; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, &cmd, 1, 100); HAL_SPI_Receive(&hspi2, &status, 1, 100); MRAM_CS_HIGH(); return status; }

写数据函数:

void MRAM_Write(uint32_t addr, uint8_t *data, uint16_t len) { uint8_t cmd[4]; cmd[0] = 0x02; cmd[1] = (addr >> 16) & 0xFF; cmd[2] = (addr >> 8) & 0xFF; cmd[3] = addr & 0xFF; MRAM_WriteEnable(); MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, cmd, 4, 100); HAL_SPI_Transmit(&hspi2, data, len, 1000); MRAM_CS_HIGH(); }

读数据函数:

void MRAM_Read(uint32_t addr, uint8_t *buf, uint16_t len) { uint8_t cmd[4]; cmd[0] = 0x03; cmd[1] = (addr >> 16) & 0xFF; cmd[2] = (addr >> 8) & 0xFF; cmd[3] = addr & 0xFF; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi2, cmd, 4, 100); HAL_SPI_Receive(&hspi2, buf, len, 1000); MRAM_CS_HIGH(); }

这段代码看起来简单,但有几个地方容易出问题。第一,HAL_SPI_Transmit 和 HAL_SPI_Receive 的超时参数要设够,尤其是写操作,如果超时太短可能返回 HAL_TIMEOUT。第二,CS 拉高之后最好加一个微秒级的延时再执行下一次操作,给芯片内部状态机一点恢复时间。第三,地址是 19 位的,但用 3 字节传输,高位补0就行,MR25H40CDF 会自动忽略超出容量的地址位。

4.3 读写验证与测试方法

驱动写完之后,第一步肯定是做基本的读写测试。我的做法是:往地址 0x00000 写一串已知数据,比如 0xAA、0x55、0x01、0x02 这样,然后读回来对比。如果一致,再测试跨页写入、连续读写、边界地址等场景。

这里有个小技巧:测试数据不要全用 0x00 或 0xFF,因为这两种数据在 SPI 通信异常时最容易出现(MISO 一直低或一直高),用交替的 pattern 更容易发现问题。

我一般会写一个简单的测试函数,循环写入递增的数据,然后读回来校验,跑个几千次,看看有没有偶发错误。这个测试能覆盖大部分硬件连接和时序问题。

5. 实操过程中的问题排查与经验总结

5.1 常见问题速查表

在实际调试过程中,我遇到和收集到的问题大概有这些:

现象可能原因排查方法
读出来全是0xFFMISO未连接或CS未拉低检查硬件连接,用逻辑分析仪抓CS和MISO
读出来全是0x00MISO被拉低或SPI模式错误检查SPI模式设置,确认CPOL/CPHA
写入后读回数据不对WREN未发送或WEL位未置1读状态寄存器确认WEL位
偶发数据错误时钟太快或走线干扰降低SPI时钟,检查PCB走线
完全无响应供电异常或芯片损坏测量VDD电压,检查焊接
写入成功但掉电丢失写保护引脚状态错误检查WP引脚电平

5.2 逻辑分析仪抓波形的重要性

调试 SPI 设备,逻辑分析仪几乎是必备工具。我用的是一款入门级的 8 通道逻辑分析仪,配合开源软件就能解码 SPI 协议。抓波形的时候重点看几个东西:

CS 拉低到第一个时钟边沿的建立时间够不够,一般要求至少几纳秒,实际看波形只要不是同时变化就行。时钟极性对不对,Mode 0 下空闲应该是低电平。数据在时钟的哪个边沿变化、哪个边沿采样,这个和 CPHA 设置直接相关。最后一个字节发完之后 CS 拉高的时机,太早可能导致最后一个字节没发完,太晚可能影响下一次操作。

有一次我遇到读数据偶尔错一位的情况,抓波形发现是 CS 拉高的时机比最后一个时钟边沿早了大概 10ns,导致最后一个 bit 没被正确采样。后来在 CS 拉高之前加了一个 __NOP() 就好了。这种问题不看波形根本找不到。

5.3 工业现场的可靠性加固

实验室里跑通不代表现场能用。工业环境的电磁干扰、温度变化、电源波动都比实验室恶劣得多。我在实际项目中做了这几件事来加固:

电源端加 0.1uF 和 10uF 的退耦电容,尽量靠近 MRAM 的 VDD 引脚。SPI 信号线上串接 33 欧姆电阻,减少反射。CS 信号加上拉电阻,防止浮空。PCB 布局上,MRAM 尽量靠近主控,走线短而直,避免和功率线路平行走。

软件层面,每次写入之后都读回来校验,如果校验失败就重试,重试三次还失败就记录错误标志。这个机制在实际运行中确实抓到过几次偶发的写入异常,虽然概率很低,但工业设备要求长期无人值守运行,这种保护是必要的。

另外,MRAM 虽然写入寿命无限,但读操作理论上也有次数限制(虽然极高),所以不要在高频循环里反复读同一地址,该用缓存的地方还是用缓存。

6. 数据存储结构设计与实际应用场景

6.1 存储区域划分

512KB 的空间看着不大,但规划好了能存不少东西。我的划分方式是:

前 4KB 作为配置参数区,存储设备ID、校准参数、运行模式等。中间 252KB 作为日志区,循环写入运行日志。最后 256KB 作为数据记录区,存储采集到的历史数据。

配置参数区每次修改都直接写入,因为 MRAM 不需要擦除,改哪个字节就写哪个字节,非常方便。日志区用环形缓冲的方式管理,写满之后从头覆盖。数据记录区按固定大小的记录块存储,每条记录带时间戳和校验和。

6.2 掉电保护策略

MRAM 的一大优势就是掉电数据不丢,但前提是数据已经写进去了。如果掉电发生在写入过程中,可能会造成部分数据不完整。我的做法是:每条记录写入之前先写一个标志字节表示"正在写入",写完之后再改成"写入完成"。上电初始化的时候扫描记录区,遇到"正在写入"状态的记录就标记为无效。

这个机制配合 MRAM 的快速写入特性,实际上掉电丢失数据的窗口非常小,基本可以忽略不计。相比 Flash 需要几毫秒的写入周期,MRAM 的写入是纳秒级的,掉电时正在写入的概率极低。

6.3 与文件系统的配合使用

如果数据量比较大,需要更复杂的管理,可以考虑在 MRAM 上跑一个轻量级文件系统,比如 LittleFS 或者 SPIFFS。这两个都是为嵌入式设计的,支持掉电保护、磨损均衡(虽然 MRAM 不需要)、目录结构。

不过对于大多数工业参数存储场景,直接按地址读写反而更简单可靠。文件系统会引入额外的复杂度和开销,除非你确实需要文件管理功能,否则没必要上。我个人的经验是:数据量小于 100KB、结构固定的话,直接地址映射最省事;数据量大、结构灵活的话,再考虑文件系统。

7. 性能实测与优化建议

7.1 读写速度实测数据

我在 13.5MHz SPI 时钟下做了实际测试,结果如下:

操作数据量耗时等效速率
单字节写1字节约2us-
连续写1KB约620us约1.65MB/s
连续读1KB约610us约1.68MB/s
单字节读1字节约2us-

这个速度对于工业数据记录来说完全够用。如果你需要更高的吞吐量,可以把 SPI 时钟提到 27MHz 甚至更高,但要注意信号完整性问题。我试过 27MHz,在短走线的板子上没问题,但走线超过 10cm 之后误码率明显上升。

7.2 DMA 传输的优化

如果 CPU 需要同时处理其他任务,可以用 DMA 来搬运 SPI 数据。STM32F745ZG 的 SPI2 支持 DMA 请求,配置好 DMA 通道之后,读写操作可以后台完成,CPU 只需要处理完成中断。

不过对于小数据量的读写(比如几十字节),DMA 的配置开销可能比直接传输还大。我的建议是:数据量超过 256 字节再考虑 DMA,小数据量直接阻塞传输更简单。

7.3 实际项目中的经验参数

最后分享几个我在实际项目中总结的参数,可以直接参考:

  • SPI 时钟:13.5MHz(稳定优先)或 27MHz(速度优先,需验证信号质量)
  • CS 建立时间:拉低后至少延时 100ns 再发时钟
  • CS 保持时间:最后一个时钟后至少延时 100ns 再拉高
  • 写操作重试次数:3次
  • 读校验:每次写入后立即读回校验
  • 退耦电容:0.1uF + 10uF 并联,靠近芯片引脚

这套参数在我做的几个工业项目里都跑得很稳,现场运行一年多没有出现过存储相关的故障。当然具体项目还要根据实际情况调整,尤其是 SPI 时钟,一定要用逻辑分析仪或者示波器确认波形质量之后再定。

返回列表