ARTICLE DETAIL

资讯详情

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

MRAM与PIC24 SPI接口实战:MR25H40CDF数据存储与读写优化

MRAM与PIC24 SPI接口实战:MR25H40CDF数据存储与读写优化 1. 为什么工业现场还在用SPI接口的MRAM如果你拆过工业控制器、电力监测终端或者车载数据记录仪大概率会在板子上看到一颗8脚的小芯片旁边连着主控的SPI总线。这类场景里NOR Flash和EEPROM曾经是绝对主力但这几年MRAM磁性随机存储器的出货量在工业领域涨得很快。MR25H40CDF就是其中一颗典型代表——4Mbit容量、SPI接口、DFN封装而PIC24HJ256GP610则是Microchip自家16位MCU里比较能打的一款256KB Flash、16KB RAM带DMA和多个SPI模块。把这两颗芯片凑在一起做数据存储和读取是很多工业嵌入式项目的标准组合。原因不复杂工业现场对掉电不丢数据这件事有执念而MRAM的写入机制跟Flash完全不同——它靠磁隧道结的电阻状态存数据写入不需要擦除周期字节级写入延迟在几十纳秒量级擦写寿命理论上无限。这意味着你可以把它当RAM一样频繁写而不用担心像Flash那样写几万次就报废。但问题也来了。MR25H40CDF虽然接口是SPI看起来跟普通SPI Flash一样实际用起来有不少细节差异。PIC24HJ256GP610的SPI模块配置项又多稍不注意就会出现读出来全是0xFF、写入后读回不一致、高速时钟下数据错位这些经典问题。我见过不少项目在这两颗芯片的对接上卡了两三天最后发现是时序模式或者片选控制的问题。这篇文章面向的是正在用或者准备用这两颗芯片做数据存储的嵌入式工程师尤其是做工业仪表、数据采集、设备参数保存这类场景的。我会从硬件连接到SPI模式配置从单字节读写到页操作从掉电保护到实测踩坑把整套流程拆开讲清楚。即使你用的是其他型号的MRAM或PIC24系列底层的SPI通信逻辑和存储管理思路也是通用的。2. MR25H40CDF的存储特性与PIC24HJ256GP610的SPI资源匹配2.1 MR25H40CDF到底和普通SPI Flash差在哪先把这个芯片的核心参数摆出来方便后面配置时对照。MR25H40CDF是Everspin的4Mbit MRAM组织方式是512K×8位SPI接口支持模式0和模式3最高时钟频率40MHz工作电压3.0V到3.6V工业级温度范围-40到85摄氏度。封装是8引脚DFN引脚定义跟标准SPI Flash兼容CS、SCK、SI、SO加上VDD、VSS和两个NC。关键差异在写入行为上。普通SPI Flash写数据之前必须先发写使能命令然后发页编程命令等内部擦写完成典型几毫秒期间不能响应其他命令。MRAM没有这个过程写命令发完数据在时钟边沿就打进去了不需要等待也不需要擦除。这意味着两件事第一你的固件里不需要写等待忙的轮询逻辑第二你可以对任意地址做单字节写入不用考虑页边界对齐。但MRAM也有它的脾气。它的写操作对电源稳定性比Flash敏感因为写电流要驱动磁隧道结翻转。如果VDD在写入瞬间有较大纹波可能出现写入失败但状态寄存器不报错的情况。所以硬件上VDD的退耦电容不能省建议在芯片电源脚旁边放0.1微法和1微法并联。2.2 PIC24HJ256GP610的SPI模块能力盘点PIC24HJ256GP610有两个SPI模块分别叫SPI1和SPI2支持主从模式、8/16/32位数据传输、可编程时钟极性和相位。它的SPI时钟源来自系统时钟经过预分频最高可以跑到系统时钟的一半。假设你用80MHz的系统时钟SPI理论上能到40MHz刚好匹配MR25H40CDF的上限。但实际项目里我不建议一上来就跑40MHz。原因有两个一是PCB走线如果超过几厘米40MHz下信号完整性容易出问题尤其是SCK和SI的上升沿可能振铃二是PIC24的SPI模块在高速下对CS建立时间和保持时间有要求如果CS由软件GPIO控制中断延迟可能导致CS时序不满足。稳妥的做法是先跑10MHz把功能调通再逐步往上提。SPI模块的配置寄存器主要有几个SPIxCON1控制模式、时钟极性和相位、主从选择SPIxCON2控制帧模式和缓冲区宽度SPIxSTAT是状态寄存器用来判断发送缓冲是否空、接收缓冲是否有数据。PIC24的SPI有个特点它的发送和接收是同一个缓冲寄存器SPIxBUFF写进去是发送读出来是接收全双工操作时要注意读写顺序。2.3 硬件连接上最容易忽略的两个点第一个是CS片选。MR25H40CDF的CS必须由主控控制不能用SPI模块的硬件片选自动管理因为MRAM的命令序列要求CS在整条命令期间保持低电平中间不能拉高。PIC24的SPI模块虽然有SS引脚但在主模式下通常配置为普通GPIO使用我建议直接用普通IO口控制CS代码里手动拉低拉高时序完全可控。第二个是SOMISO的驱动能力。MR25H40CDF的SO在CS拉高后进入高阻态如果PCB上这条线没有上拉电阻浮空期间可能被干扰。虽然大多数情况下不影响但在工业现场电磁环境复杂时建议在SO上加一个10K上拉到VDD保证空闲时电平确定。对比项MR25H40CDF典型SPI NOR Flash写入前擦除不需要必须擦除写入延迟纳秒级毫秒级擦写寿命无限约10万次页写入对齐无要求必须页对齐写使能命令需要需要状态寄存器忙判断不需要必须轮询3. SPI模式配置与底层读写函数实现3.1 模式0还是模式3一个必须确认的细节MR25H40CDF支持SPI模式0CPOL0CPHA0和模式3CPOL1CPHA1。这两种模式的区别在于时钟空闲电平和数据采样边沿。模式0下SCK空闲为低数据在上升沿采样模式3下SCK空闲为高数据在上升沿采样。注意两者的采样边沿都是上升沿但空闲电平不同。PIC24HJ256GP610的SPI模块通过SPIxCON1的CKP位和CKE位来配置。CKP对应时钟极性CKE对应时钟相位。要配置成模式0CKP0CKE1模式3则CKP1CKE0。这里容易搞混因为CKE的定义和标准SPI的CPHA是反的。我建议直接用模式0因为大多数示例代码和逻辑分析仪默认都是模式0调试时对照方便。配置代码大概长这样// SPI1配置为模式0主模式8位数据时钟预分频 SPI1CON1bits.CKP 0; // 时钟极性空闲低 SPI1CON1bits.CKE 1; // 时钟相位上升沿采样 SPI1CON1bits.MSTEN 1; // 主模式 SPI1CON1bits.MODE16 0; // 8位模式 SPI1CON1bits.SPRE 0b110; // 次级预分频 2:1 SPI1CON1bits.PPRE 0b10; // 初级预分频 4:1 SPI1CON1bits.SMP 0; // 中间采样 SPI1STATbits.SPIEN 1; // 使能SPI假设系统时钟80MHz初级预分频4:1得20MHz次级预分频2:1得10MHzSPI时钟就是10MHz。这个速度对MR25H40CDF来说很保守但足够稳定。3.2 单字节读写函数的实现与陷阱MR25H40CDF的读命令是0x03写命令是0x02写使能是0x06读状态寄存器是0x05。读操作时序是CS拉低发送0x03发送24位地址实际只用低19位高5位忽略然后连续读取数据每来一个时钟出一个字节地址自动递增。写操作类似但每次写入前必须发写使能命令。先看单字节写函数void MRAM_WriteByte(uint32_t addr, uint8_t data) { MRAM_CS_LOW(); SPI1_ExchangeByte(0x06); // 写使能 MRAM_CS_HIGH(); MRAM_CS_LOW(); SPI1_ExchangeByte(0x02); // 写命令 SPI1_ExchangeByte((addr 16) 0xFF); SPI1_ExchangeByte((addr 8) 0xFF); SPI1_ExchangeByte(addr 0xFF); SPI1_ExchangeByte(data); MRAM_CS_HIGH(); }这里有个关键点写使能命令和写命令之间必须拉高CS再拉低。我见过有人把写使能和写命令放在同一个CS低电平周期里结果写入失败。原因是MR25H40CDF的写使能锁存是在CS上升沿完成的如果CS一直保持低写使能状态不会生效。读函数相对简单uint8_t MRAM_ReadByte(uint32_t addr) { uint8_t data; MRAM_CS_LOW(); SPI1_ExchangeByte(0x03); SPI1_ExchangeByte((addr 16) 0xFF); SPI1_ExchangeByte((addr 8) 0xFF); SPI1_ExchangeByte(addr 0xFF); data SPI1_ExchangeByte(0x00); MRAM_CS_HIGH(); return data; }SPI1_ExchangeByte函数负责把数据写进SPI1BUF等待发送完成然后读回接收数据。PIC24的SPI发送完成标志是SPI1STATbits.SPITBF接收完成标志是SPI1STATbits.SPIRBF。注意要先等SPITBF清零再写下一个字节否则会覆盖发送缓冲。3.3 连续读写与地址自动递增的边界MR25H40CDF支持连续读和连续写地址在芯片内部自动递增到达最高地址0x7FFFF后回卷到0x00000。这个特性在做批量数据存储时很有用比如你要存一个512字节的数据块不需要每写一个字节就发一次命令。连续写函数void MRAM_WriteBlock(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_CS_LOW(); SPI1_ExchangeByte(0x06); MRAM_CS_HIGH(); MRAM_CS_LOW(); SPI1_ExchangeByte(0x02); SPI1_ExchangeByte((addr 16) 0xFF); SPI1_ExchangeByte((addr 8) 0xFF); SPI1_ExchangeByte(addr 0xFF); for (uint16_t i 0; i len; i) { SPI1_ExchangeByte(buf[i]); } MRAM_CS_HIGH(); }连续写的时候要注意虽然MRAM没有页边界限制但如果你写入的数据跨越了0x7FFFF边界地址会回卷到0x00000继续写。这在环形缓冲区设计里反而是个便利但如果你不希望回卷就要在软件里做地址判断。连续读同理发完读命令和起始地址后连续调用ExchangeByte就能依次读出数据。实测在10MHz时钟下连续读512字节大约耗时0.5毫秒比通过I2C接口的EEPROM快一个数量级。4. 数据存储策略从参数保存到环形缓冲区4.1 工业参数存储的地址规划MR25H40CDF有512KB的地址空间对于大多数工业设备来说绰绰有余。但地址不能随便用我习惯把空间划分成几个区域0x00000到0x00FFF存设备参数校准系数、量程、通信地址等0x01000到0x01FFF存系统配置报警阈值、采样周期0x02000往后做数据记录区。参数区每个参数分配固定地址比如校准系数存在0x00010到0x0001F用float类型占4字节可以存4个系数。这种固定地址映射的好处是读写简单不需要文件系统上电直接读固定地址就行。缺点是参数数量固定后期扩展要改地址表。我一般会在参数区开头放一个魔数比如0x5A5A5A5A用来判断MRAM是否已经初始化过。上电时先读这个魔数如果是0xFFFFFFFFMRAM出厂状态说明是第一次使用固件里执行默认参数写入如果魔数正确再读其他参数。这个做法能避免MRAM空白时读出随机值导致设备行为异常。4.2 环形缓冲区在数据记录中的应用数据记录区用环形缓冲区结构最合适。MR25H40CDF的地址自动回卷特性让环形缓冲区的实现变得简单你只需要维护一个写指针每次写完数据后指针递增到达记录区末尾就回到记录区起始地址。读的时候从写指针往前推读最近N条记录。具体实现上我在记录区起始地址放一个4字节的写指针后面跟实际数据。每次写记录时先读写指针在指针位置写入数据然后更新写指针。写指针更新也要遵循MRAM的写使能流程。这里有个细节写指针的更新和数据的写入不是原子的如果在这两步之间掉电可能导致指针和数据不一致。解决办法是给每条记录加一个校验字段比如CRC16读的时候校验不通过就跳过这条记录。记录结构可以这样设计字段长度说明时间戳4字节记录产生时的系统时间数据长度2字节有效数据字节数数据内容变长实际采集数据CRC162字节整条记录的校验每条记录总长度控制在64字节以内这样512KB空间能存8000条左右。按每分钟记录一次算能存5天多的数据对大多数工业场景够用了。4.3 掉电保护与写入完整性MRAM虽然写入速度快但掉电瞬间如果正在写数据可能只写了一半。工业设备经常遇到突然断电的情况所以写入完整性必须考虑。我的做法是在参数区用双备份加标志位的方式参数存两份分别放在两个区域每个区域末尾放一个写入完成标志。更新参数时先写备份区写完后写标志再写主区最后写主区标志。上电读取时比较两个区域的标志和CRC选有效的那份。对于数据记录区因为每条记录都有CRC掉电导致的不完整记录在读取时会被CRC校验过滤掉不会影响其他记录。但写指针可能指向一条不完整的记录位置所以上电初始化时要扫描写指针附近的记录找到最后一条CRC有效的记录把写指针修正到它后面。这里有个实操经验MRAM的写入虽然不需要等待但在写操作后立即读同一地址读出的数据可能还是旧值因为芯片内部有写缓冲。我实测发现连续写后马上读大约有几十纳秒的窗口读到旧数据。所以在写入后如果需要立即验证建议加一个短延时或者重新发起一次读命令。不过正常应用里写完就完不需要立即读回这个问题影响不大。5. 实测中遇到的典型问题与排查过程5.1 读出来全是0xFF的排查链路这是最常见的问题现象是SPI通信看起来正常但读任何地址都返回0xFF。排查要按顺序来第一步用示波器或者逻辑分析仪抓CS、SCK、SI、SO四根线。先确认CS在命令期间是否保持低电平。我遇到过因为CS控制代码里多了一次拉高导致命令被截断的情况。第二步确认SCK是否有输出。如果SCK不动检查SPI模块是否使能、时钟配置是否正确。PIC24的SPI使能位是SPI1STATbits.SPIEN这个位在配置完其他寄存器后再置1。第三步看SI上是否有数据发出。如果SI一直高或一直低检查SPI1BUF的写入顺序确认在写之前SPITBF已经清零。第四步如果前三步都正常检查MRAM的电源和CS上拉。MR25H40CDF的CS如果浮空芯片可能处于不确定状态。确保CS在空闲时被拉高。我踩过的一个坑是SPI时钟极性配反了。模式0配成了模式3结果数据在错误的边沿被采样读出来全是0xFF。用逻辑分析仪对照MR25H40CDF数据手册的时序图一眼就能看出来。5.2 写入后读回不一致的几种原因写入成功但读回数据不对可能的原因有这几个一是写使能命令没有正确执行。前面说过写使能和写命令之间必须拉高CS。如果CS没有拉高写使能不生效后续写命令被忽略数据自然没写进去。二是地址发送顺序错了。MR25H40CDF的地址是24位高字节先发。如果发成了低字节先发地址就完全错了。这个错误在调试时不容易发现因为读的时候如果地址也错可能读到的恰好是空白区域返回0xFF看起来像没写进去。三是SPI时钟太快导致建立时间不够。在40MHz下如果PCB走线较长SI上的数据可能在SCK边沿时还没稳定。降低时钟到10MHz试试如果问题消失就是信号完整性问题。四是写操作后没有给芯片足够的内部处理时间。虽然MRAM写入是纳秒级但从CS拉高到下一次CS拉低之间建议至少间隔100纳秒。我一般会在写操作后加一个几微秒的延时确保万无一失。5.3 高速时钟下的数据错位问题当SPI时钟提到20MHz以上时可能出现数据错位表现为读出的数据整体偏移一个位或者一个字节。这种问题通常是采样边沿配置不对。PIC24的SPI模块有个SMP位控制采样时刻是在数据输出中间还是末尾。在高速下建议设置SMP0在数据中间采样留出足够的建立和保持时间。另外PIC24的SPI模块在高速下对SPI1BUF的读写时序有要求。写SPI1BUF后要等至少一个指令周期才能读SPI1STAT否则状态位可能还没更新。我在代码里会在ExchangeByte函数里加一个NOP或者短延时确保状态位稳定。如果以上都试过还是错位检查PCB上SCK和SI的走线是否等长。SPI虽然是同步总线但SCK和SI之间的偏斜如果超过时钟周期的四分之一采样就会出错。在40MHz下时钟周期25纳秒四分之一是6纳秒左右对应PCB走线长度差约3厘米。所以高速SPI的走线要尽量短且等长。6. 性能实测与优化建议6.1 不同时钟频率下的读写速度对比我在PIC24HJ256GP610上跑了一组实测数据系统时钟80MHzSPI时钟分别配置为5MHz、10MHz、20MHz测试连续读写1024字节的耗时SPI时钟连续写1024字节连续读1024字节5MHz1.85ms1.72ms10MHz0.95ms0.88ms20MHz0.52ms0.48ms可以看到读写速度基本与时钟频率成正比20MHz下写1KB只要0.5毫秒。这个速度对于工业数据记录来说完全够用即使每秒记录一次占用CPU时间也可以忽略。但要注意这是纯SPI传输时间不包括CS控制和函数调用开销。实际应用中如果每次写少量数据CS拉低拉高的开销占比会比较大。所以批量写入时尽量一次写多个字节减少CS切换次数。6.2 用DMA减轻CPU负担的思路PIC24HJ256GP610的SPI模块支持DMA可以在不占用CPU的情况下完成大批量数据传输。配置DMA通道关联SPI1接收和发送设置传输长度启动DMA后CPU可以去做其他事情传输完成产生中断。DMA方式适合数据记录区的大块写入比如一次写256字节的记录。配置步骤大致是设置DMA通道的源地址为数据缓冲区目的地址为SPI1BUF传输长度为256触发源选择SPI1发送。接收方向类似源地址为SPI1BUF目的地址为接收缓冲区。不过DMA配置相对复杂如果数据量不大用查询方式反而更简单可靠。我的建议是单次传输超过64字节再考虑DMA否则查询方式足够。6.3 长期运行中的磨损与数据保持MRAM的擦写寿命理论无限数据保持时间官方标称20年。但在工业高温环境下数据保持时间会缩短。如果设备长期工作在70摄氏度以上建议每几年做一次数据刷新把重要参数读出来重新写一遍。另外虽然MRAM不需要磨损均衡但如果你用环形缓冲区做数据记录写指针会不断递增整个记录区被均匀使用不存在某个地址被过度写入的问题。这一点比Flash省心很多不需要专门做磨损均衡算法。我在一个电力监测项目里连续运行了两年多MRAM每天写入约500条记录累计写入超过36万次没有出现任何写入失败或数据丢失。这个可靠性在工业场景里是很让人放心的。7. 几个容易被忽略的实操细节7.1 上电初始化的顺序上电后不要立即操作MRAM等电源稳定后再初始化SPI。PIC24的SPI模块配置要在系统时钟稳定之后进行否则时钟预分频可能不对。我的做法是在主循环开始前加一个100毫秒的延时然后配置SPI再读MRAM的魔数判断是否首次使用。如果MRAM是首次使用魔数读出0xFFFFFFFF固件写入默认参数。写入默认参数前先发写使能再发写命令。写完后再读回验证确认写入成功。这个验证步骤在产线批量生产时特别重要能筛出焊接不良或者芯片损坏的板子。7.2 中断优先级与SPI冲突如果系统里还有其他中断使用SPI比如另一个SPI模块接显示屏要注意中断优先级配置。PIC24的中断优先级有7级SPI传输期间如果被高优先级中断打断可能导致CS时序错乱。我一般把SPI传输放在主循环里做或者用中断优先级较低的任务处理避免在SPI命令序列中间被抢占。如果必须在中断里操作SPI建议在CS拉低后关全局中断命令发完再开中断。虽然会短暂影响中断响应但SPI命令序列通常只有几个字节时间很短对系统实时性影响可以接受。7.3 逻辑分析仪在调试中的正确用法调试SPI通信逻辑分析仪比示波器好用。设置采样率至少是SPI时钟的10倍比如10MHz SPI用100MHz采样率。触发条件设为CS下降沿这样每次命令都能抓到完整波形。分析时重点看几个点CS拉低到第一个SCK边沿的时间是否满足芯片要求MR25H40CDF要求至少5纳秒最后一个SCK边沿到CS拉高的时间是否足够数据在SCK边沿是否稳定。如果这些都没问题通信基本就稳了。我习惯把逻辑分析仪的协议解码功能打开直接看SPI总线上传输的十六进制数据。这样能快速确认命令码、地址、数据是否正确比手动数波形快得多。7.4 多片MRAM的片选管理有些项目需要多片MRAM扩展容量比如两片MR25H40CDF拼成1Mbit。这时候每片MRAM的CS要独立控制SCK、SI、SO可以共用。软件上维护一个片选映射表根据地址范围选择对应的CS引脚。切换片选时要注意先拉高当前片的CS再拉低目标片的CS中间留几个微秒的间隔。如果两片CS同时为低SO线上会出现总线冲突可能损坏芯片。虽然MRAM的SO有高阻态保护但稳妥起见还是不要同时选中两片。地址映射上第一片地址0x00000到0x7FFFF第二片地址0x80000到0xFFFFF。读写函数里根据地址高位的不同选择片选其他逻辑不变。这样上层应用看到的是一片1Mbit的连续空间不需要关心底层有几片芯片。
返回列表