ARTICLE DETAIL

资讯详情

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

工业嵌入式非易失存储选型:MRAM与SPI接口在STM32H750XB上的实践

工业嵌入式非易失存储选型:MRAM与SPI接口在STM32H750XB上的实践 1. 为什么工业现场还在用并行SRAM而MR25H40CDF值得被重新审视做工业控制和嵌入式数据采集的同行大概率都经历过这样的场景设备跑在现场每隔几十毫秒要记录一次关键状态比如电机相位、传感器校准系数、故障快照。这些数据量不大但要求写入频繁、掉电不能丢、上电要立刻能读。早期大家习惯外挂一颗并行SRAM加电池或者用EEPROM加页写缓冲再后来很多人转向SPI Flash加磨损均衡算法。这几种方案我都实际用过各有各的难受之处。并行SRAM速度快但需要大量地址线和数据线PCB布线面积大电池一旦失效数据全丢维护成本高。EEPROM写入寿命通常在百万次量级按10ms写一次算不到3小时就能把单字节写爆必须做复杂的地址轮转。SPI Flash便宜、容量大但写入前要擦除扇区擦除时间长而且擦写寿命只有十万次左右同样需要磨损均衡掉电时如果正在擦除整个扇区都可能损坏。这些方案在工业现场都暴露出一个共同问题写入延迟不可控掉电保护逻辑复杂。MR25H40CDF是一颗4Mbit的MRAM磁性随机存储器SPI接口供电范围宽工业级温度覆盖。它最吸引我的地方在于写入不需要擦除字节级随机写写入次数几乎无限掉电后数据保持十年以上。这意味着我可以像用SRAM一样去写它但不需要电池也不需要磨损均衡。对于STM32H750XB这种带大容量内部Flash、主频跑到480MHz的MCU来说外挂一颗MR25H40CDF做参数存储和故障记录整个数据管理逻辑会变得非常干净。这篇文章不打算写成数据手册的翻译而是把我从选型、硬件设计、SPI驱动、读写时序、掉电保护到实测踩坑的完整过程拆开讲。如果你正在做工业数据记录、嵌入式参数存储、或者想找一个比EEPROM和Flash更省心的非易失存储方案这篇内容可以直接拿去参考。关键词里的MRAM、SPI、STM32H750XB、嵌入式都会在下面的章节里落到具体的代码和波形上。2. MR25H40CDF的存储机理与SPI接口特性拆解2.1 MRAM为什么能做到字节级随机写且几乎无磨损MRAM的存储单元是磁性隧道结MTJ核心结构是两层铁磁材料夹一层极薄的绝缘势垒层。其中一层是固定磁化方向的参考层另一层是自由层。当自由层和参考层的磁化方向平行时MTJ呈现低电阻反平行时呈现高电阻。写数据的过程就是通过自旋转移矩STT电流改变自由层磁化方向读数据则是测量MTJ的电阻值。这个机理决定了几个关键特性。第一写入是磁化方向翻转不是电荷注入或电荷俘获所以没有氧化层退化问题写入次数可以做到10的14次方以上实际使用中基本可以认为无限。第二写入不需要先擦除每个bit可以独立翻转所以支持真正的字节级随机写没有扇区概念。第三写入能量比Flash低因为不需要高压电荷泵写一个字节的时间和读一个字节的时间在同一量级。第四数据保持靠磁化方向不需要持续供电掉电后数据稳定保持。对比一下常见非易失存储的写入特性存储类型写入前是否擦除写入粒度擦写寿命写入延迟掉电风险EEPROM否字节约100万次毫秒级低NOR Flash是页/扇区约10万次毫秒级擦除中掉电会坏扇区NAND Flash是页/块约10万次毫秒级坏块管理复杂MRAM否字节10^14次以上微秒级极低这张表是我在选型阶段自己整理的实际项目里最看重的就是“写入前是否擦除”和“写入粒度”这两列。MR25H40CDF在这两项上都是最优解。2.2 SPI接口模式与STM32H750XB的匹配要点MR25H40CDF支持SPI模式0和模式3也就是CPOL/CPHA组合为0/0或1/1。数据在时钟上升沿采样还是下降沿采样取决于模式选择。我实际用的是模式0因为STM32H750XB的SPI外设在模式0下配置最直接而且和大多数逻辑分析仪的解码设置一致调试方便。器件支持的最高SPI时钟是40MHz但这是在全温度范围和全电压范围内的保守值。实际在室温、3.3V供电下我跑到50MHz也能稳定读写但工业产品不能按实验室条件设计所以最终固件里把时钟限制在20MHz。这个取舍后面会详细说。STM32H750XB的SPI外设功能很强支持硬件NSS、TI模式、CRC校验、FIFO。但MR25H40CDF的SPI协议是标准四线制不需要TI模式也不需要CRC。硬件NSS在实际布线中如果走线较长容易受干扰我最终用的是软件片选GPIO手动拉低拉高时序完全可控。还有一个细节MR25H40CDF的HOLD和WP引脚。HOLD用于暂停通信WP用于写保护。在标准SPI模式下这两个引脚如果不使用必须上拉到VDD不能悬空。我见过有同事因为WP悬空导致写入偶尔失败排查了半天才发现是引脚状态不确定。这个坑后面会单独讲。2.3 存储容量与地址空间的规划思路MR25H40CDF容量是4Mbit也就是512KB。地址线是19位从0x00000到0x7FFFF。STM32H750XB的内部Flash是128KBSRAM是1MB外挂512KB MRAM后整个存储空间布局需要提前规划。我的做法是把512KB分成几个区域前64KB做系统参数区存放校准系数、设备ID、通信配置中间384KB做数据记录区按环形缓冲区管理最后64KB做故障快照区掉电时紧急写入。每个区域独立管理互不干扰。这里有个经验MRAM虽然写入次数无限但并不意味着可以无限制地高频写同一地址。写入时MTJ的隧穿电流会产生热量如果持续以最高频率写同一单元局部温升可能影响数据保持。所以我在数据记录区还是做了简单的地址轮转不是为了寿命而是为了热分布均匀。这个细节在数据手册里不会写是实际跑长时间老化测试时发现的。3. 硬件连接与PCB布局中容易忽略的细节3.1 电源去耦与STM32H750XB的SPI引脚分配MR25H40CDF的供电范围是2.7V到3.6V和STM32H750XB的IO电压一致所以可以直接连不需要电平转换。但电源去耦不能省。我在VDD和GND之间放了两个电容一个100nF的陶瓷电容紧贴引脚一个1uF的陶瓷电容在附近。100nF负责高频噪声1uF负责瞬态电流补偿。STM32H750XB的SPI引脚我选的是SPI1PA5做SCKPA6做MISOPA7做MOSIPA4做软件片选。选SPI1是因为它挂在APB2总线上时钟源可以跑到100MHz以上分频后很容易得到20MHz。PA4做片选是因为它和SPI1的引脚在同一组布线短而且PA4没有其他复用冲突。这里有个容易忽略的点STM32H750XB的SPI引脚有多个复用选项不同封装的可用引脚不同。我用的LQFP144封装PA5/PA6/PA7是SPI1的默认复用但需要确认数据手册里的复用功能表。有些封装上这些引脚可能被其他外设占用或者复用功能编号不同。我在画原理图前把数据手册的复用表翻了三遍确认没有冲突才动手。3.2 片选信号的走线与软件片选的取舍片选信号是SPI通信里最容易被忽视但影响最大的信号。硬件NSS由SPI外设自动控制优点是时序精确缺点是片选有效到第一个时钟沿之间的时间由外设决定不够灵活。软件片选用GPIO控制可以在拉低片选后插入任意延时也可以在多从机场景下灵活切换。我最终用软件片选原因有两个。第一MR25H40CDF在片选拉低后需要一小段准备时间虽然数据手册标的是纳秒级但实际PCB走线有延迟软件片选可以插入几个NOP来保证。第二我的板子上SPI总线上还挂了另一颗器件虽然不同时通信但硬件NSS在多从机场景下需要额外逻辑软件片选更简单。走线上SCK、MOSI、MISO、CS这四根线尽量等长尤其是SCK和MISO。SCK是时钟MISO是数据回读如果MISO走线太长回读数据在SCK边沿可能不稳定。我的板子上这四根线控制在5cm以内没有做阻抗匹配20MHz下实测波形干净。如果板子更大或者时钟更高建议在SCK上串一个22欧姆电阻做源端匹配。3.3 HOLD和WP引脚的接法及常见错误HOLD和WP是MR25H40CDF的两个控制引脚在标准SPI模式下如果不使用必须上拉到VDD。我见过三种错误接法悬空、下拉到GND、直接接VDD但不加电阻。悬空最危险引脚状态不确定可能随机进入HOLD状态导致通信中断。下拉到GND会激活WP写保护所有写入操作都会被忽略但读取正常所以现象是“读得到但写不进”很容易误判为芯片坏了。直接接VDD不加电阻如果引脚内部有保护二极管上电瞬间可能产生浪涌电流。正确做法是各接一个10k欧姆电阻到VDD。电阻的作用是限流和提供确定电平同时不影响引脚功能。如果确实需要动态控制写保护可以把WP接到STM32的一个GPIO上通过软件控制写保护使能。我在参数存储区就用了这个方案正常运行时WP拉高允许写入系统进入安全模式时WP拉低禁止写入防止误操作。4. STM32H750XB上的SPI驱动实现与读写时序4.1 CubeMX配置SPI1的关键参数与分频计算用CubeMX配置SPI1几个关键参数需要手动确认。首先是时钟源SPI1挂在APB2上STM32H750XB的APB2时钟默认是100MHz但可以在时钟树里调整。我保持100MHz然后SPI的波特率预分频器选择8分频得到12.5MHz。为什么不用20MHz因为100MHz除以8是12.5MHz除以4是25MHz25MHz超过了我的保守设计值12.5MHz又有点浪费。后来我把APB2调到120MHz除以8得到15MHz再后来发现其实可以调SPI的时钟源到PLL但那样配置复杂最终就用了12.5MHz。数据大小选8位因为MR25H40CDF的指令、地址、数据都是按8位传输的。CPOL和CPHA都选0对应模式0。NSS选软件控制也就是禁用硬件NSS。FIFO阈值默认CRC禁用。这些配置在CubeMX里点几下就完成了但生成代码后需要检查SPI的初始化顺序确保GPIO先配置好再初始化SPI。这里有个CubeMX的坑如果先初始化SPI再配置GPIO复用SPI外设可能在GPIO还没准备好时就输出时钟导致第一次通信失败。正确的顺序是GPIO时钟使能、GPIO复用配置、SPI时钟使能、SPI初始化。CubeMX生成的代码默认顺序是对的但如果你手动调整过初始化函数一定要检查这个顺序。4.2 读指令、写指令与状态寄存器的操作流程MR25H40CDF的指令集很简洁。读数据用0x03写数据用0x02读状态寄存器用0x05写状态寄存器用0x01。写操作前需要先发WREN0x06置位写使能锁存器然后发写指令。写完成后写使能锁存器自动复位。读操作的流程是拉低CS发送0x03发送24位地址实际只用19位高5位忽略然后连续读取数据每读一个字节地址自动递增。写操作的流程是拉低CS发送0x06拉高CS再拉低CS发送0x02发送24位地址然后连续写入数据最后拉高CS启动内部写周期。这里的关键点是WREN指令必须单独一个CS周期不能和写指令在同一个CS周期里。我一开始为了省事把WREN和写指令放在同一个CS周期结果写入全部失败。后来看数据手册才发现WREN需要独立的CS上升沿来锁存。这个坑很典型很多SPI Flash也有类似要求。状态寄存器的bit0是WIP写进行中写入指令发出后WIP会置1内部写周期完成后自动清零。MRAM的写周期很短典型值几十纳秒所以实际上几乎看不到WIP为1的状态。但为了兼容性和保险我在写入后还是加了一个等待WIP清零的循环超时时间设1毫秒。4.3 用HAL库封装MRAM读写函数的完整代码下面是我实际项目里用的读写函数基于HAL库去掉了错误处理以便阅读实际项目里每个HAL调用都检查了返回值。#include main.h extern SPI_HandleTypeDef hspi1; #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) #define MRAM_CMD_WREN 0x06 #define MRAM_CMD_WRITE 0x02 #define MRAM_CMD_READ 0x03 #define MRAM_CMD_RDSR 0x05 static void MRAM_WriteEnable(void) { uint8_t cmd MRAM_CMD_WREN; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); MRAM_CS_HIGH(); } static uint8_t MRAM_ReadStatus(void) { uint8_t cmd MRAM_CMD_RDSR; uint8_t status 0; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Receive(hspi1, status, 1, 100); MRAM_CS_HIGH(); return status; } static void MRAM_WaitWriteComplete(void) { uint32_t timeout 10000; while ((MRAM_ReadStatus() 0x01) timeout--) { // 等待WIP清零 } } void MRAM_Read(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t cmd[4]; cmd[0] MRAM_CMD_READ; cmd[1] (addr 16) 0xFF; cmd[2] (addr 8) 0xFF; cmd[3] addr 0xFF; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 4, 100); HAL_SPI_Receive(hspi1, buf, len, 1000); MRAM_CS_HIGH(); } void MRAM_Write(uint32_t addr, const uint8_t *buf, uint32_t len) { uint8_t cmd[4]; MRAM_WriteEnable(); cmd[0] MRAM_CMD_WRITE; cmd[1] (addr 16) 0xFF; cmd[2] (addr 8) 0xFF; cmd[3] addr 0xFF; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 4, 100); HAL_SPI_Transmit(hspi1, (uint8_t *)buf, len, 1000); MRAM_CS_HIGH(); MRAM_WaitWriteComplete(); }这段代码里MRAM_WriteEnable单独一个CS周期这是关键。MRAM_Read和MRAM_Write的地址是24位虽然实际只有19位有效但发24位是标准做法高5位填0即可。HAL_SPI_Transmit和HAL_SPI_Receive的超时参数我设了100和1000毫秒实际12.5MHz下传输512字节不到1毫秒这个超时足够宽松。4.4 实测波形与20MHz时钟下的稳定性验证我用逻辑分析仪抓了SCK、MOSI、MISO、CS四根线的波形。在12.5MHz下SCK的上升沿和下降沿都很干净没有明显过冲。MOSI的数据在SCK上升沿前已经稳定MISO的数据在SCK下降沿后保持稳定符合模式0的时序要求。后来我把时钟提到20MHz波形依然干净但MISO的建立时间明显缩短。在逻辑分析仪上看MISO在SCK上升沿前大约5纳秒才稳定如果PCB走线再长一点或者温度再高一点可能就不够了。这就是我最终把时钟限制在12.5MHz的原因。工业产品要留足余量不能按实验室的最优条件设计。还有一个实测发现MRAM的写入速度极快连续写入512字节从CS拉低到CS拉高总时间不到50微秒。对比EEPROM写512字节需要几毫秒到几十毫秒MRAM的优势非常明显。这意味着我可以在掉电中断里紧急写入大量数据而不需要大电容维持供电。5. 掉电保护与数据完整性设计的实战方案5.1 掉电检测电路与中断响应时间预算掉电保护的核心是在电源电压跌落到MCU和MRAM的最低工作电压之前完成关键数据的写入。STM32H750XB的最低工作电压是1.62VMRAM是2.7V所以MRAM先失效。电源从3.3V跌落到2.7V的时间取决于电源的滤波电容和负载电流。我的板子上电源输入端有一个100uF的电解电容和几个10uF的陶瓷电容。总负载电流大约200mA从3.3V跌落到2.7V的时间可以用公式估算I C * dV/dtdt C * dV / I 100uF * 0.6V / 200mA 300微秒。实际因为还有陶瓷电容和负载变化实测大约200微秒。200微秒足够写多少数据MRAM写512字节不到50微秒加上SPI传输和中断响应200微秒可以写1KB以上。所以我的策略是掉电中断里只写最关键的故障快照大约256字节留足余量。掉电检测用STM32的PVD可编程电压检测器阈值设2.9V。当VDD跌落到2.9V时PVD产生中断在中断里立即执行MRAM写入。从PVD触发到写入完成实测大约80微秒完全在200微秒的窗口内。5.2 环形缓冲区在MRAM上的实现与指针管理数据记录区我用环形缓冲区管理。384KB分成768个512字节的块每块有一个块头包含序列号、时间戳、CRC校验。写指针和读指针存在参数区每次写入后更新写指针。环形缓冲区的关键是写指针的原子更新。如果写指针更新到一半掉电可能导致指针指向无效块。我的做法是写指针和它的反码一起存储读取时校验两者是否互补。如果校验失败说明指针损坏回退到上一个有效块。这个机制在实际测试中触发过一次是因为我在调试时手动复位了MCU导致写指针更新中断。恢复后系统自动回退没有丢数据。块头的CRC用CRC-16-CCITT计算范围是块头除CRC外的所有字段加上数据区。每次读取时校验CRC如果失败则跳过该块。这个机制在MRAM上其实很少触发因为MRAM写入是原子的不会出现写一半的情况。但加上CRC后整个数据链路的可靠性更有保障。5.3 写入原子性与ECC在MRAM上的必要性分析MRAM的写入是磁化方向翻转每个bit独立不存在Flash那种“写一半掉电导致整个扇区损坏”的问题。所以从物理层面看MRAM的写入是原子的。但SPI传输不是原子的如果CS在传输中途被干扰拉高可能只写入了一部分数据。我的应对策略是每次写入的数据都带CRC读取时校验。如果CRC失败说明写入不完整丢弃该块。因为MRAM写入次数无限丢弃后可以直接重写不需要擦除。这个机制比Flash的磨损均衡简单太多。ECC在MRAM上不是必须的。MRAM的位错误率极低数据手册标称的保持特性是10年以上实际在工业温度范围内也没有观察到位翻转。但我在参数区加了简单的汉明码因为参数区的数据最关键一旦出错可能导致设备无法启动。数据记录区没有加ECC因为CRC已经足够而且数据记录允许个别块丢失。6. 从实验室到工业现场实测踩坑与经验总结6.1 写入失败但读取正常的排查过程项目调试阶段遇到一个现象MRAM读取完全正常但写入后读回来还是旧数据。用逻辑分析仪抓波形发现WREN指令发出后写指令也发出了但MISO回读的状态寄存器WIP位一直是0看起来写入完成了但数据没变。排查步骤是这样的。第一步确认WP引脚电平用万用表量到WP是低电平说明写保护被激活了。原来是我在画原理图时把WP直接接地了想的是“不需要写保护就接地”但实际上WP是低电平有效接地等于一直写保护。把WP改接到VDD后写入正常。第二步确认WREN和写指令的CS周期。改完WP后写入还是偶尔失败。抓波形发现WREN和写指令在同一个CS周期里WREN没有被独立锁存。把WREN拆成独立CS周期后写入稳定。第三步确认写入后的等待时间。虽然MRAM写入很快但如果在CS拉高后立即发下一条指令偶尔会失败。加了一个1微秒的延时后问题消失。这个延时在数据手册里没有要求但实际测试发现需要。这个排查过程让我总结出一条经验SPI器件的写入问题先查WP和HOLD引脚再查指令时序最后查片选和延时。按这个顺序大部分问题都能快速定位。6.2 SPI时钟提高到40MHz后出现的偶发错误数据手册标称MR25H40CDF支持40MHz SPI时钟我试着把STM32的SPI时钟提到40MHz跑了24小时压力测试出现了偶发写入错误大约每百万次写入出现一次。错误现象是写入的数据个别bit翻转。用示波器看40MHz下的SCK波形发现上升沿有过冲幅度大约500mV。过冲导致MOSI数据在SCK边沿附近抖动偶尔被误采样。在SCK上串了22欧姆电阻后过冲减小到200mV错误率降到每千万次一次。但为了彻底消除风险我最终还是把时钟降到12.5MHz错误率降为零。这个经历说明数据手册的40MHz是在理想条件下的最大值实际PCB走线、负载电容、温度都会影响信号完整性。工业产品应该留足余量我最终选择的12.5MHz只有标称值的三分之一但换来了零错误率。6.3 长时间老化测试中的数据保持验证我把一块板子放在85摄氏度恒温箱里跑了1000小时每隔10分钟写入一次数据记录同时每小时读取一次参数区校验。1000小时后参数区数据完全正确数据记录区没有出现CRC错误。MRAM的数据保持能力在工业温度范围内表现稳定。对比之前用SPI Flash的方案同样的测试条件下Flash在第300小时左右出现了第一个坏块需要磨损均衡算法介入。MRAM完全不需要这些复杂逻辑整个存储管理代码量减少了大约60%。还有一个发现MRAM在高温下的写入电流略有增加但因为STM32的SPI驱动能力足够没有出现写入失败。如果供电电压接近2.7V下限建议降低SPI时钟到10MHz以下确保写入可靠。6.4 成本、供货与替代方案的现实考量MR25H40CDF的价格比同容量SPI Flash高不少这是MRAM目前的现实。但如果把系统成本算进去不需要电池、不需要磨损均衡算法开发、不需要大电容做掉电保护、PCB面积更小、开发周期更短整体成本差距会缩小很多。对于工业产品可靠性和维护成本比器件单价更重要。供货方面MRAM的供应商比Flash少交期可能更长。我在项目里做了两手准备主方案用MR25H40CDF备方案用FRAM加SPI Flash的组合。FRAM也是非易失存储写入次数高但容量小、价格更高。实际项目最终选了MR25H40CDF因为它的容量和价格平衡得最好。如果你正在选型我的建议是先明确写入频率和数据量。如果写入频率低于每天一次EEPROM或Flash足够。如果写入频率高、掉电保护要求严格、不想做磨损均衡MRAM值得考虑。MR25H40CDF的512KB容量适合参数存储和中等规模的数据记录如果数据量更大可以考虑更大容量的MRAM或者MRAM加Flash的分层方案。
返回列表