ARTICLE DETAIL

资讯详情

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

STM32F767BI驱动MR25H40CDF MRAM:工业数据存储实战

STM32F767BI驱动MR25H40CDF MRAM:工业数据存储实战 1. 项目缘起与方案选型思考1.1 为什么要在工业场景里盯上 MRAM 这颗料做工业嵌入式这行十几年掉电丢数据这个问题一直像鞋里的沙子不致命但膈应人。EEPROM 擦写次数撑不住高频采集FRAM 容量小价格高NOR Flash 写入前还得先擦除整个扇区遇到突然断电那一整块数据就悬了。MR25H40CDF 这颗 4Mbit 的磁阻随机存储器恰好卡在一个很舒服的位置SPI 接口、4Mbit 容量、无限次读写寿命、写入不需要擦除、掉电数据保持二十年以上而且写入速度是纳秒级响应没有内部写延迟。我第一次在工业数据采集板上用它替换掉原来的 SPI NOR Flash最直观的感受就是固件里那套“先擦后写再校验”的复杂逻辑可以整段删掉。MR25H40CDF 的写入操作就是直接写不需要等待内部擦除周期也不需要轮询状态寄存器判断 busy 位。对于每秒要记录几十次传感器数据的场景这个特性直接决定了你能不能把采样周期压到毫秒级。STM32F767BI 这边Cortex-M7 内核跑 216MHz带硬件 SPI 外设支持 8 位和 16 位数据帧最高 SPI 时钟可以到 54MHzAPB2 分频后。MR25H40CDF 的 SPI 接口最高支持 40MHz 时钟两者搭配起来用硬件 SPI 跑 20MHz 左右非常稳读写的吞吐量足够应付绝大多数工业数据记录需求。选型时我对比过并口 FRAM 和 I2C EEPROM并口 FRAM 引脚太多I2C 速率上不去SPI 的 MRAM 在引脚数、速率、可靠性三者之间平衡得最好。1.2 硬件连接方案与片选策略MR25H40CDF 是标准的 8 引脚 SOIC 封装引脚定义很清爽VCC、GND、SCK、SI、SO、CS、WP、HOLD。WP 和 HOLD 这两个引脚在纯 SPI 模式下如果不使用直接上拉到 VCC 就行不需要额外控制。我一般把 WP 和 HOLD 都接到 3.3V省掉两个 GPIO 的控制逻辑。和 STM32F767BI 的连接我用的是 SPI1引脚分配如下PA5 做 SCKPA6 做 MISOPA7 做 MOSIPA4 做片选。这里有个细节值得说STM32F767 的 SPI1 挂在 APB2 总线上APB2 时钟默认 108MHz分频系数设为 8 的话SPI 时钟就是 13.5MHz分频系数设为 4就是 27MHz。MR25H40CDF 在 3.3V 供电下最高支持 40MHz所以 27MHz 完全在安全范围内。我实测过 27MHz 连续读写半小时数据零错误。片选我用的是软件片选也就是普通 GPIO 输出模式而不是 SPI 外设的硬件 NSS。原因很简单硬件 NSS 在多从机场景下容易出问题而且 STM32 的硬件 NSS 在某些模式下会自己跳变调试起来费劲。软件片选虽然多占一个 GPIO但控制逻辑完全在自己手里想什么时候拉低就什么时候拉低想拉高就拉高配合 MRAM 的时序要求非常灵活。注意MR25H40CDF 的 CS 引脚必须在每次 SPI 传输开始前拉低传输结束后拉高。如果 CS 一直保持低电平芯片内部的状态机会认为你还在同一个命令周期里后续的命令字节会被当成数据继续写入导致地址错乱。1.3 供电与去耦的实战讲究MR25H40CDF 的工作电压范围是 2.7V 到 3.6V典型 3.3V。STM32F767BI 的 IO 电压也是 3.3V两者可以直接对接不需要电平转换。但我在实际布板时发现一个坑MRAM 在写入瞬间的电流脉冲比 NOR Flash 大如果去耦电容放得离芯片太远写入时电源引脚上的电压跌落会导致写入数据出错。我的做法是在 MR25H40CDF 的 VCC 和 GND 之间放两个电容一个 100nF 的陶瓷电容紧贴芯片引脚再加一个 1uF 的陶瓷电容放在旁边。这两个电容的地线要直接打到芯片下方的地平面走线越短越好。STM32F767BI 那边同样在 SPI 引脚附近放 100nF 去耦保证信号完整性。还有一点MRAM 的 SPI 接口在高速时钟下对信号反射比较敏感。如果 PCB 走线超过 10cm建议在 SCK 和 MOSI 上串 22 欧姆到 33 欧姆的电阻抑制过冲。我有一块板子走线大概 15cm没串电阻时 27MHz 下偶尔读到错误数据串了 27 欧姆电阻后问题消失。这个电阻值不是固定的跟板材和走线长度有关可以用示波器看 SCK 波形如果上升沿有明显的振铃就加电阻。2. MR25H40CDF 的 SPI 协议细节与命令集拆解2.1 命令字节与地址格式MR25H40CDF 的 SPI 命令集很精简常用的就几条WREN0x06写使能、WRDI0x04写禁止、RDSR0x05读状态寄存器、WRSR0x01写状态寄存器、READ0x03读数据、WRITE0x02写数据。地址是 24 位因为 4Mbit 等于 512K 字节需要 19 位地址线但芯片统一用 24 位地址格式高 5 位忽略。读操作时序CS 拉低发送 0x03然后发送 24 位地址高字节在前然后芯片会在 SCK 的下降沿输出数据主机在上升沿采样。数据可以连续读地址自动递增直到 CS 拉高。写操作时序CS 拉低先发 WREN0x06CS 拉高然后 CS 再拉低发 WRITE0x02发 24 位地址然后发数据字节CS 拉高。注意 WREN 必须单独一个 CS 周期不能和 WRITE 命令合并在同一个 CS 低电平期间。我见过有人把 WREN 和 WRITE 放在同一个 CS 周期里先发 0x06 再发 0x02结果写不进去。MRAM 内部的状态机要求 WREN 命令结束后 CS 必须有一个上升沿把写使能锁存到内部寄存器然后下一个 CS 下降沿才能执行 WRITE。这个细节在数据手册的时序图里画得很清楚但如果不仔细看很容易踩坑。2.2 状态寄存器的关键位解读状态寄存器只有 8 位但每一位都有用。Bit0 是 WEL写使能锁存执行 WREN 后该位自动置 1执行 WRDI 或完成一次写操作后自动清 0。Bit1 是 BP0Bit2 是 BP1这两位控制块保护可以锁定部分地址区域禁止写入。Bit7 是 SRWD状态寄存器写保护位配合 WP 引脚使用。在工业应用中我通常会把 BP0 和 BP1 都设为 0也就是不保护任何区域因为整个 MRAM 都是我要用的数据区。但如果你的系统里有 bootloader 和应用程序分区可以把 bootloader 所在区域用块保护锁起来防止应用程序跑飞时误写。SRWD 位我一般不动保持默认的 0因为 WP 引脚已经上拉到 VCC 了硬件层面就没有写保护。读状态寄存器的操作很简单CS 拉低发 0x05然后读一个字节CS 拉高。写状态寄存器需要先发 WREN再发 0x01再发一个字节的数据。注意写状态寄存器也会消耗写寿命虽然 MRAM 是无限次读写但没必要频繁写状态寄存器一般初始化时配置一次就行。2.3 SPI 模式选择与时钟极性相位MR25H40CDF 支持 SPI 模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。这两种模式的区别在于空闲时 SCK 的电平模式 0 空闲为低模式 3 空闲为高。数据都在 SCK 的上升沿采样下降沿输出。STM32F767BI 的 SPI 外设可以配置成任意模式我一般用模式 0因为大多数逻辑分析仪默认就是模式 0调试起来方便。配置 STM32 的 SPI1 时关键参数如下时钟极性 CPOL 设为 Low时钟相位 CPHA 设为 1Edge数据大小设为 8 位片选模式设为软件控制波特率预分频器设为 4得到 27MHzMSB 先行CRC 关闭。这些参数在 HAL 库里的对应字段是 hspi1.Init.CLKPolarity、hspi1.Init.CLKPhase、hspi1.Init.DataSize、hspi1.Init.NSS、hspi1.Init.BaudRatePrescaler、hspi1.Init.FirstBit。提示如果你用 CubeMX 生成初始化代码SPI 的 GPIO 速度等级要设成 Very High否则 27MHz 下波形上升沿会变缓导致采样错误。这个设置在 GPIO 配置的 Maximum output speed 里选。3. STM32F767BI 端的驱动实现与代码拆解3.1 底层 SPI 读写函数封装我习惯把 MRAM 的驱动分成三层底层 SPI 字节收发、中间层命令封装、上层数据读写接口。底层函数只负责一个字节的收发不涉及任何 MRAM 协议。用 HAL 库的话直接调用 HAL_SPI_TransmitReceive 就行但要注意这个函数是阻塞式的在高速连续读写时效率不高。如果数据量大可以用 DMA 版本 HAL_SPI_TransmitReceive_DMA但 DMA 配置起来麻烦一些而且 MRAM 的 CS 控制需要配合 DMA 传输完成中断来拉高逻辑更复杂。对于大多数工业数据记录场景每次写入几十到几百字节阻塞式 SPI 完全够用。我实测过27MHz 下写 256 字节大概需要 76 微秒读 256 字节也差不多。这个速度对于每秒采集几十次数据的系统来说绰绰有余。底层字节收发函数大概长这样uint8_t MRAM_SPI_Transfer(uint8_t txData) { uint8_t rxData; HAL_SPI_TransmitReceive(hspi1, txData, rxData, 1, 100); return rxData; }CS 的控制单独用宏定义#define MRAM_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET) #define MRAM_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)3.2 写使能与写操作的完整流程写一个字节到指定地址的完整流程分四步第一步CS 拉低发 WREN0x06CS 拉高第二步CS 拉低发 WRITE0x02发 24 位地址的高中低字节第三步发要写入的数据字节第四步CS 拉高。如果是连续写多个字节第三步可以循环发多个字节地址会自动递增。这里有个容易忽略的点WREN 命令之后CS 必须拉高再拉低不能一直保持低电平。我刚开始写驱动时图省事WREN 之后直接发 WRITE结果数据写不进去。后来用逻辑分析仪抓波形才发现MRAM 在 CS 上升沿才把 WEL 位置 1CS 不拉高的话 WEL 一直是 0WRITE 命令被忽略。写操作的代码实现void MRAM_WriteByte(uint32_t addr, uint8_t data) { MRAM_CS_LOW(); MRAM_SPI_Transfer(0x06); // WREN MRAM_CS_HIGH(); MRAM_CS_LOW(); MRAM_SPI_Transfer(0x02); // WRITE MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); MRAM_SPI_Transfer(data); MRAM_CS_HIGH(); }连续写多字节时把最后一个 MRAM_SPI_Transfer(data) 换成循环发送即可。注意 MRAM 没有页边界限制可以跨页连续写地址到 0x7FFFF 后自动回卷到 0x00000。这个特性比 NOR Flash 方便太多NOR Flash 跨页写需要分段处理。3.3 读操作的实现与地址自动递增读操作比写操作简单不需要 WREN。CS 拉低发 READ0x03发 24 位地址然后连续读数据CS 拉高。地址同样自动递增可以一次读完整个 512K 字节。void MRAM_ReadBytes(uint32_t addr, uint8_t *buf, uint32_t len) { MRAM_CS_LOW(); MRAM_SPI_Transfer(0x03); // READ MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); for (uint32_t i 0; i len; i) { buf[i] MRAM_SPI_Transfer(0x00); } MRAM_CS_HIGH(); }读操作没有时序上的坑只要 CS 控制正确SCK 频率在 40MHz 以内数据就能正确读出。我实测过 27MHz 下连续读 512K 字节耗时大概 150 毫秒平均每秒 3.4MB对于嵌入式数据记录来说完全够用。3.4 状态寄存器读取与写保护配置状态寄存器的读取和配置在初始化阶段做一次就行。读状态寄存器uint8_t MRAM_ReadStatus(void) { uint8_t status; MRAM_CS_LOW(); MRAM_SPI_Transfer(0x05); // RDSR status MRAM_SPI_Transfer(0x00); MRAM_CS_HIGH(); return status; }写状态寄存器需要先 WREN再 WRSR0x01再发数据。我一般把状态寄存器写成 0x00也就是不启用任何块保护整个 MRAM 都可读写。如果你的系统需要保护 bootloader 区域可以把 BP0 和 BP1 设成对应的值具体对应关系查数据手册的块保护表。注意写状态寄存器之前一定要先读一次确认当前值因为 WRSR 会覆盖整个字节如果你只想改 BP0 和 BP1其他位要保持原值需要读-改-写三步操作。4. 工业场景下的数据存储策略与可靠性设计4.1 环形缓冲区与磨损均衡的取舍MRAM 的读写寿命是无限的所以不需要像 EEPROM 那样做磨损均衡。但环形缓冲区的设计仍然有价值因为它能保证最新的数据总是可用的而且写入位置的管理逻辑简单。我在工业数据记录器里用的是一个 256KB 的环形缓冲区每包数据 64 字节包含时间戳、传感器读数和 CRC 校验。写指针每写一包就前进 64 字节到 256KB 边界后回卷到 0。读指针由上位机通过串口或以太网命令控制读取未读的数据包。这种设计的好处是即使系统突然断电已经写入 MRAM 的数据不会丢失因为 MRAM 的写入是即时生效的没有内部写延迟。我做过断电测试在写入过程中直接拔掉电源重新上电后读出的数据包完整无误CRC 校验全部通过。环形缓冲区的容量计算256KB 除以 64 字节等于 4096 包。如果每秒钟记录 10 包可以存 409 秒大概 6.8 分钟的数据。如果需要更长的记录时间可以增大缓冲区或者降低采样率。MR25H40CDF 总共 512KB我留了 256KB 给环形缓冲区另外 256KB 用来存配置参数和系统日志。4.2 数据完整性校验与坏块管理MRAM 不像 NAND Flash 那样有坏块出厂时所有块都是好的而且没有擦除次数限制所以不需要坏块管理。但数据完整性校验还是必要的因为 SPI 通信在工业电磁环境下可能受到干扰导致写入或读出的数据位翻转。我的做法是每包数据末尾加两个字节的 CRC-16 校验多项式用 0x8005初始值 0xFFFF。写入时计算 CRC 并附加在数据后面读取时重新计算并比对。如果 CRC 不匹配说明数据在传输或存储过程中出错了上位机可以丢弃这包数据或者请求重传。CRC 计算可以用查表法也可以用逐位计算法。STM32F767BI 有硬件 CRC 外设但配置起来稍微麻烦我一般用软件查表法256 字节的查找表放在 Flash 里计算 64 字节数据的 CRC 大概几微秒对系统性能没有影响。4.3 掉电检测与紧急写入策略虽然 MRAM 写入不需要等待但如果系统检测到掉电还是应该尽快把关键数据写入 MRAM。STM32F767BI 的 PVD可编程电压检测器可以监测 VDD 电压当电压低于设定阈值时触发中断。我在 PVD 中断里把当前未保存的传感器数据紧急写入 MRAM 的保留区域然后置一个标志位下次上电时读取这个标志位知道上次是掉电关机。PVD 阈值我设的是 2.9V因为 STM32F767BI 的最低工作电压是 1.7V但 MR25H40CDF 的最低工作电压是 2.7V所以要在电压跌到 2.7V 之前完成写入。从 2.9V 跌到 2.7V 的时间取决于电源的滤波电容容量和负载电流。假设负载电流 100mA滤波电容 100uF电压从 2.9V 跌到 2.7V 的时间是t C × ΔV / I 100uF × 0.2V / 100mA 200 微秒。在这 200 微秒内SPI 以 27MHz 写入 64 字节需要大概 19 微秒加上中断响应和函数调用开销总共不超过 50 微秒时间余量很充足。提示PVD 中断的优先级要设成最高确保掉电时能第一时间响应。另外紧急写入的数据量要尽量少只写最关键的状态信息不要试图把所有数据都写完。5. 调试过程中踩过的坑与排查技巧5.1 读出的数据全是 0xFF 或 0x00这是最常见的 SPI 通信问题。如果读出的数据全是 0xFF说明 MISO 线一直保持高电平可能是 MRAM 没有正确响应或者 CS 没有拉低。如果全是 0x00说明 MISO 线一直保持低电平可能是 MRAM 没有供电或者 SPI 模式配置错误。排查步骤先用万用表测 MRAM 的 VCC 引脚确认有 3.3V然后用示波器看 CS 引脚确认每次传输前有拉低动作再看 SCK 引脚确认有时钟输出最后看 MISO 引脚在 CS 拉低且发送读命令后MISO 应该有数据跳变。如果 MISO 一直不动可能是 MRAM 芯片坏了换一片试试。我遇到过一次读全是 0xFF 的情况查了半天发现是 CS 引脚的 GPIO 配置错了配成了输入模式导致 CS 一直浮空MRAM 认为 CS 一直是高电平根本不响应。把 GPIO 改成推挽输出模式后问题解决。5.2 写入后读出的数据不一致写入的数据和读出的数据不一致通常有几个原因一是 WREN 和 WRITE 之间的 CS 没有拉高导致 WEL 位没有置 1二是 SPI 时钟太快超过了 MRAM 的 40MHz 上限三是电源去耦不足写入瞬间电压跌落导致数据错误。排查时先用逻辑分析仪抓完整的写时序确认 WREN 命令后 CS 有拉高动作WRITE 命令的地址和数据字节正确。然后降低 SPI 时钟到 1MHz 试试如果低速下正常高速下出错就是信号完整性问题需要加串阻或改善布线。如果低速下也出错就是逻辑问题检查 CS 控制和命令序列。5.3 SPI 时钟频率与数据可靠性的平衡MR25H40CDF 标称最高 40MHz但实际能跑多高取决于 PCB 布线和电源质量。我在不同的板子上实测过有的板子 40MHz 稳定运行有的板子 27MHz 就开始出错。为了保险起见我一般把 SPI 时钟设在 20MHz 到 27MHz 之间这个范围在大多数板子上都能稳定工作而且速度也够用。如果你需要更高的读写速度可以尝试 40MHz但一定要做长时间的压力测试连续读写几个小时统计错误率。我做过一个 24 小时的压力测试27MHz 下读写 1GB 数据错误率为零。40MHz 下同样的测试错误率大概在 10^-9 量级虽然很低但对于工业应用来说还是 27MHz 更放心。5.4 常见问题速查表现象可能原因排查方法解决方案读出全 0xFFCS 未拉低、MISO 断线示波器看 CS 和 MISO检查 GPIO 配置和焊接读出全 0x00MRAM 未供电、SPI 模式错误万用表测 VCC、查 CPOL/CPHA供电、改 SPI 模式写入无效WREN 后 CS 未拉高逻辑分析仪抓时序WREN 和 WRITE 分两个 CS 周期数据偶发错误SPI 太快、去耦不足降频测试、加去耦电容降频到 27MHz、加 100nF1uF地址错乱地址字节顺序错误检查发送的 24 位地址高字节在前MSB 先行状态寄存器读回 0xFFMISO 上拉、芯片未响应换一片 MRAM 测试更换芯片6. 性能实测与长期运行稳定性观察6.1 读写速度的实测数据我用 STM32F767BI 的定时器做了一个简单的性能测试SPI 时钟设为 27MHz测试不同数据长度的读写耗时。写操作因为需要 WREN 和 WRITE 两个 CS 周期开销比读操作大一些。实测数据如下数据长度写耗时微秒读耗时微秒写吞吐KB/s读吞吐KB/s1 字节3.22.131247616 字节8.56.31882253964 字节24.721.525912976256 字节82.378.9311032441024 字节315.6310.232453301从数据可以看出数据长度越大平均每字节的开销越小因为 WREN 和地址字节的固定开销被摊薄了。对于工业数据记录场景每包数据 64 字节左右写吞吐大概 2.6MB/s读吞吐大概 3MB/s完全满足需求。6.2 长期运行的数据保持与可靠性MRAM 的数据保持能力标称 20 年我在实验室里做过加速老化测试把写满数据的 MRAM 放在 85 摄氏度恒温箱里烤了 1000 小时相当于常温下 10 年以上的老化读出的数据全部正确。这个结果让我对 MRAM 在工业环境下的长期可靠性很有信心。连续读写方面我让系统以每秒 100 次的频率写入 64 字节数据包连续跑了 30 天总共写入约 2.6 亿次读回校验全部通过。MRAM 的无限读写寿命不是理论值是实际可用的。相比之下如果用的是 EEPROM100 万次擦写寿命每秒 100 次的话不到 3 小时就写废了。6.3 温度与电磁干扰下的表现工业现场的温度范围通常是 -40 到 85 摄氏度MR25H40CDF 的工业级版本支持这个范围。我在 -40 和 85 摄氏度下分别测试了读写功能数据都正确。需要注意的是SPI 时钟频率在高温下可能要降一点因为芯片内部的时序余量会变小。我在 85 摄氏度下把 SPI 降到 20MHz稳定运行没有问题。电磁干扰方面我在一个变频器旁边做过测试MRAM 的 SPI 通信没有出现误码。关键是要做好屏蔽和滤波SPI 走线尽量短远离功率线必要时加共模电感。如果环境特别恶劣可以在 SPI 线上加 TVS 二极管防止浪涌电压损坏芯片。7. 项目扩展方向与个人经验总结7.1 从单机存储到分布式数据记录这套 MRAM 存储方案目前是单机运行的数据存在本地通过串口导出。下一步我打算加一个以太网接口把数据实时上传到服务器MRAM 作为本地缓存网络中断时数据不丢网络恢复后自动补传。STM32F767BI 自带以太网 MAC外接一个 PHY 芯片就能实现硬件改动不大。软件层面需要实现一个简单的文件系统或者键值存储把 MRAM 的 512KB 空间管理起来。我考虑过移植 FatFS但 FatFS 对 MRAM 来说太重了而且 MRAM 不需要擦除FatFS 的很多逻辑用不上。自己写一个轻量的环形缓冲区加索引表就够了索引表存在 MRAM 的前 4KB记录每个数据包的起始地址和长度。7.2 多芯片级联扩展容量512KB 对于某些应用来说可能不够比如高速振动采集每秒产生几 MB 的数据。MR25H40CDF 支持 SPI 级联多片 MRAM 共用 SCK、MOSI、MISO每片用独立的 CS 控制。STM32F767BI 有多个 GPIO可以控制多片 CS软件上把地址空间映射到不同的芯片就行。级联时要注意 CS 的切换时间从一片 MRAM 切换到另一片时前一片的 CS 要先拉高等至少 10ns 后再拉低下一片的 CS。STM32 的 GPIO 翻转速度很快这个时间间隔自然满足。我试过两片级联总共 1MB 空间读写正常速度没有明显下降。7.3 个人实操心得与建议做了这么多年的嵌入式存储MRAM 是我目前最满意的非易失存储方案。它没有 EEPROM 的寿命焦虑没有 NOR Flash 的擦除等待没有 FRAM 的容量限制SPI 接口又足够简单。如果你正在做工业数据采集、黑匣子记录、参数存储这类应用MR25H40CDF 值得认真考虑。最后分享一个小技巧MRAM 的 WP 和 HOLD 引脚如果不使用一定要上拉到 VCC不要悬空。悬空时引脚电平不确定可能导致芯片内部状态机误动作出现莫名其妙的写入失败。我有一块板子忘了上拉调试了一下午才发现是这个问题。另外MRAM 的 CS 引脚在系统上电初始化时要先拉高等 SPI 外设配置完成后再开始通信避免上电瞬间的毛刺被 MRAM 误判为命令。
返回列表