ARTICLE DETAIL

资讯详情

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

MRAM与PIC18F4585的SPI存储方案:工业高频数据记录实战

MRAM与PIC18F4585的SPI存储方案:工业高频数据记录实战 1. 项目缘起与整体设计思路工业现场的数据存储有个很尴尬的现状用 EEPROM 吧写入速度慢得让人着急擦写次数也就百万次级别高频采集的场景下没几个月就写废了用 SRAM 加后备电池吧电池没电数据就全丢维护成本高得离谱用 Flash 吧写之前得先擦除整个扇区掉电还可能丢数据而且擦写寿命同样有限。我前两年做一个工业数据采集终端的项目就卡在这个存储环节上后来接触到 MRAM 这种磁性随机存储器才算找到了一个比较理想的折中方案。这次要聊的组合是MR25H40CDF这颗 MRAM 芯片搭配PIC18F4585这款经典 8 位单片机通过SPI总线实现数据的存储和读取。MR25H40CDF 是 4Mbit512KB容量的串行 MRAM采用 SPI 接口读写速度可以跑到 40MHz最关键的是它几乎没有写入延迟也不需要擦除操作写入次数理论上无限。PIC18F4585 则是 Microchip 的一款老牌 8 位 MCU自带 MSSP 模块支持 SPI 主模式工业现场大量在用生态成熟、资料齐全。这个组合解决的核心问题是在工业级温度范围-40℃到85℃甚至更高内实现高频、高可靠、长寿命的数据记录。适合谁看呢如果你正在做工业仪表、数据记录仪、PLC 扩展模块、电力监测终端这类产品需要在没有操作系统或者只有简单前后台的嵌入式环境里做非易失存储那这套方案可以直接抄作业。哪怕你是刚接触 SPI 和外部存储的新手我也会把时序、片选、读写流程这些细节掰开揉碎讲清楚。为什么选 MRAM 而不是别的我列个表对比一下你一看就明白存储类型写入速度擦写寿命掉电保持写入前擦除接口复杂度EEPROM慢ms级约100万次好不需要I2C/SPINOR Flash中等约10万次好需要SPI/QSPISRAM电池极快无限依赖电池不需要并行/SPIMRAM极快ns级近乎无限好20年不需要SPI从表里能看出来MRAM 基本把 EEPROM 和 SRAM 的优点都占了代价就是单价偏高。但对于工业场景来说少一次现场维护、少一次数据丢失省下来的成本远超芯片差价。这就是我最终选它的逻辑。整体设计思路是这样的PIC18F4585 作为 SPI 主机MR25H40CDF 作为从机MCU 通过硬件 MSSP 模块产生 SCK 时钟用一根普通 IO 做片选CSMOSI/MISO 完成数据收发。软件上封装出初始化、写使能、字节写、页写、连续读、状态寄存器读取这几个基础函数上层业务只管调用。下面我按这个思路一层层展开。2. 硬件连接与关键参数解析2.1 引脚连接与片选设计MR25H40CDF 是 8 引脚封装引脚定义很清晰CS片选、SCK时钟、SI数据输入、SO数据输出、VCC、VSS还有两个 NC 或者用于级联的引脚。和 PIC18F4585 连接时我一般这样分配CS接 RC2普通 IO软件控制SCK接 RC3MSSP 的 SCK 引脚SI接 RC5MSSP 的 SDO 引脚主机输出SO接 RC4MSSP 的 SDI 引脚主机输入这里有个细节值得说为什么片选要用普通 IO 而不是硬件 SPI 的 SS 引脚因为 PIC18F4585 的 MSSP 模块在 SPI 主模式下SS 引脚如果配置不当会干扰通信而且很多工程师习惯用软件片选控制更灵活多从机场景下也方便扩展。用普通 IO 做片选只要保证在发送命令前拉低、发送完成后拉高就行时序上完全可控。注意CS 拉低到第一个 SCK 上升沿之间要留出至少 5ns 的建立时间MR25H40CDF 手册里写的是 tCSS实际布线时这个时间很容易满足但如果你的 MCU 主频很高、IO 翻转很快建议在拉低 CS 后加一两个 NOP 再发时钟。2.2 SPI 模式选择与时钟计算MR25H40CDF 支持 SPI 模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。我一般用模式 0因为这是最常用的PIC 的 MSSP 配置起来也简单。具体来说空闲时 SCK 为低电平数据在 SCK 上升沿采样下降沿输出。时钟频率方面MR25H40CDF 最高支持 40MHz但 PIC18F4585 在 40MHz 晶振下指令周期是 10MHz4 分频MSSP 的 SPI 时钟由 Fosc 分频得到。我通常设置SPI 时钟为 Fosc/4 或者 Fosc/16。假设晶振 40MHzFosc/4 就是 10MHz这个速度对 MRAM 来说完全在能力范围内而且 10MHz 下信号完整性更容易保证PCB 走线不用太讲究。计算一下如果 SPI 时钟 10MHz传输一个字节 8 位需要 0.8μs加上命令字节和地址字节一次完整的读操作大概 4 个字节开销命令3字节地址也就是 3.2μs 左右然后才是数据。写操作类似但多了写使能命令和等待时间。这个速度对于工业采集通常采样率几 kHz 到几十 kHz绰绰有余。2.3 电源与去耦的实战经验MRAM 芯片对电源噪声比 EEPROM 敏感一些因为它内部是磁性隧道结写电流对电源波动比较在意。我在 PCB 上一般会在 VCC 和 GND 之间放一个0.1μF 陶瓷电容紧贴芯片引脚再并一个1μF 或 10μF 的钽电容做低频滤波。实测下来如果只放 0.1μF在电机启停等干扰大的场合偶尔会出现写入失败加上 10μF 后就稳了。另外MR25H40CDF 的工作电压是 2.7V 到 3.6V典型 3.3V。PIC18F4585 可以工作在 5V 也可以 3.3V如果 MCU 用 5V 供电SPI 电平就需要电平转换否则会超过 MRAM 的输入耐压。我建议整个系统统一 3.3V省掉转换芯片也降低复杂度。3. 软件驱动层实现细节3.1 SPI 初始化与底层收发函数PIC18F4585 的 MSSP 模块初始化关键寄存器是 SSPCON1、SSPSTAT、SSPADD。SPI 主模式下SSPCON1 的 SSPM 位设为 0b0000Fosc/4或者 0b0001Fosc/16SSPEN 置 1 使能。SSPSTAT 里 SMP 位决定采样时刻CKE 位决定时钟边沿模式 0 下 SMP0、CKE0。void SPI_Init(void) { TRISCbits.TRISC3 0; // SCK 输出 TRISCbits.TRISC5 0; // SDO 输出 TRISCbits.TRISC4 1; // SDI 输入 TRISCbits.TRISC2 0; // CS 输出 CS 1; // 片选拉高空闲状态 SSPCON1 0x20; // SSPEN1, SSPM0000 (Fosc/4) SSPSTAT 0x00; // 模式0 SSPADD 0; // 主模式不用 }底层收发一个字节的函数很经典就是写 SSPBUF 然后等 BF 标志unsigned char SPI_ExchangeByte(unsigned char data) { SSPBUF data; while(!PIR1bits.SSPIF); // 等待传输完成 PIR1bits.SSPIF 0; // 清标志 return SSPBUF; }这里有个坑我踩过PIC18 的 SSPBUF 是双缓冲的读 SSPBUF 实际读的是 SSPSR 移入的数据写 SSPBUF 是写入发送寄存器。如果你在发送后立刻读可能读到的是上一次的残留数据。所以一定要等 SSPIF 置位后再读而且读之前先清标志顺序不能乱。3.2 MRAM 命令集与操作码MR25H40CDF 的命令集和标准 SPI Flash 很像但少了擦除相关的命令。常用的有命令名称操作码功能说明WREN0x06写使能写之前必须发WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据注意 MRAM没有扇区擦除命令这是它和 Flash 最大的区别。写之前只需要发 WREN然后直接发 WRITE 命令加地址和数据就行。状态寄存器的 WEL 位写使能锁存在每次写操作后会自动清零所以每次写都要重新发 WREN。3.3 写使能与状态轮询写使能函数很简单void MRAM_WriteEnable(void) { CS 0; SPI_ExchangeByte(0x06); CS 1; }发完 WREN 后WEL 位会置 1。你可以读状态寄存器确认unsigned char MRAM_ReadStatus(void) { unsigned char status; CS 0; SPI_ExchangeByte(0x05); status SPI_ExchangeByte(0xFF); CS 1; return status; }状态寄存器 bit1 是 WELbit0 是 WIP写进行中。MRAM 的写入非常快通常不需要轮询 WIP但为了保险写完可以读一下状态确认 WIP 为 0。我实测下来MRAM 写一个字节大概几十纳秒就完成了比 EEPROM 的 5ms 快了五个数量级。实操心得虽然 MRAM 写入快但 SPI 传输本身需要时间。如果你连续写大量数据瓶颈在 SPI 时钟上不在 MRAM 本身。所以提高 SPI 时钟比优化 MRAM 操作更有效。3.4 字节写与页写实现字节写函数void MRAM_WriteByte(unsigned long addr, unsigned char data) { MRAM_WriteEnable(); CS 0; SPI_ExchangeByte(0x02); SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); SPI_ExchangeByte(data); CS 1; }页写就是连续写多个字节MRAM 内部有页缓冲但和 Flash 不同MRAM 没有页边界限制你可以从任意地址开始连续写只要 CS 保持低电平地址会自动递增。这点非常方便写一个数据块不用考虑跨页问题。void MRAM_WritePage(unsigned long addr, unsigned char *buf, unsigned int len) { unsigned int i; MRAM_WriteEnable(); CS 0; SPI_ExchangeByte(0x02); SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for(i 0; i len; i) { SPI_ExchangeByte(buf[i]); } CS 1; }3.5 连续读与地址自动递增读操作更简单发 READ 命令加 24 位地址然后连续读就行void MRAM_ReadData(unsigned long addr, unsigned char *buf, unsigned int len) { unsigned int i; CS 0; SPI_ExchangeByte(0x03); SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for(i 0; i len; i) { buf[i] SPI_ExchangeByte(0xFF); } CS 1; }地址是 24 位的因为 512KB 需要 19 位地址用 3 个字节表示绰绰有余。地址到达末尾会自动回卷到 0这个特性在读整个芯片时有用。4. 工业场景下的可靠性设计与避坑4.1 掉电保护与数据完整性工业现场最怕掉电丢数据。MRAM 本身掉电不丢但如果在写操作过程中掉电可能写到一半。我的做法是双备份加校验把数据分成两个区每个区存一份每份带 CRC16 校验。写入时先写备份区再写主区读取时如果主区 CRC 错误就切到备份区。这样即使写主区时掉电备份区还是好的。CRC 计算可以用查表法PIC18F4585 的 ROM 够大放个 256 字节的表没问题。我实测过10MHz SPI 下写 1KB 数据加 CRC 校验大概 1ms 左右对大多数工业采集完全够用。4.2 SPI 通信抗干扰措施工业环境电磁干扰大SPI 走线如果太长容易出错。我总结了几条经验走线尽量短MCU 和 MRAM 最好放在同一面距离不超过 5cm。SCK 和 MOSI 之间加地线隔离减少串扰。CS 信号加 10k 上拉电阻防止 MCU 复位时 IO 悬空导致误触发。SPI 时钟不要盲目追高10MHz 在大多数工业板上很稳20MHz 以上就要看 PCB 质量了。有一次我在一个电机控制板上用 20MHz SPI结果电机一转就读写错误后来降到 5MHz 就没事了。所以速度要服从稳定性这是工业产品的铁律。4.3 常见问题速查表现象可能原因排查方法解决措施读回全 0xFFCS 没拉低或 SCK 无输出示波器看 CS 和 SCK检查 IO 配置和 MSSP 使能写入后读回旧数据WREN 没发或 WEL 未置位读状态寄存器每次写前发 WREN偶发数据错误电源噪声或 SPI 太快加大去耦电容降时钟加 10μF 电容降速地址错位24 位地址发送顺序错检查移位顺序先发高字节通信完全无响应接线错误或芯片损坏测 VCC 和引脚通断换芯片检查焊接4.4 写寿命与磨损均衡的思考虽然 MRAM 写入次数近乎无限但如果你做的是高频记录比如每秒写 1000 次理论上几年下来也是天文数字。不过 MRAM 不像 Flash 有擦除寿命限制它的写入是磁性翻转没有氧化层磨损问题。我咨询过原厂 FAE他们说 MRAM 的写入次数在 10^14 量级以上按每秒 1000 次算可以写 3000 多年。所以磨损均衡在 MRAM 上基本不需要考虑这省了很多软件复杂度。但有一点要注意MRAM 的写电流会影响相邻单元吗不会因为 MRAM 的写是通过自旋极化电流或者磁场翻转单元之间是独立的。我实测连续写同一个地址 100 万次读回来数据完全正确。5. 完整读写流程与实测记录5.1 上电初始化流程上电后MCU 先初始化 IO 和 MSSP然后拉高 CS延时 1ms 等 MRAM 内部上电稳定。接着读一次状态寄存器确认能通信。如果读回 0xFF 或 0x00说明通信有问题需要检查硬件。void MRAM_Init(void) { SPI_Init(); CS 1; __delay_ms(1); unsigned char st MRAM_ReadStatus(); if(st 0xFF || st 0x00) { // 通信异常点亮错误灯 ERR_LED 0; } }5.2 一次完整的数据记录过程假设我要记录一个 32 位的时间戳和一个 16 位的传感器值共 6 字节。流程是计算当前写入地址比如从 0x000000 开始每次加 6。把数据打包到缓冲区加上 CRC16。调用 MRAM_WritePage 写入。读回验证。地址递增如果到末尾就回卷。实测 10MHz SPI 下写 8 字节6 数据2 CRC耗时约 8μs读回验证再 8μs总共不到 20μs。如果每秒记录 1000 次占用 CPU 时间 2%完全可接受。5.3 用示波器抓的 SPI 时序我用示波器抓过 CS、SCK、MOSI 三根线。CS 拉低后第一个 SCK 上升沿采样 MOSI 上的命令字节 0x02然后是 3 个地址字节最后是数据字节。整个过程中 CS 保持低电平SCK 在数据位之间连续输出。模式 0 下数据在上升沿稳定下降沿变化和手册里的时序图完全一致。有一点要注意PIC18 的 SPI 在发送完一个字节后SSPIF 置位到下一个字节写入 SSPBUF 之间会有几个指令周期的间隔。如果连续发送字节之间会有小空隙但 CS 保持低电平MRAM 不会认为传输结束。这个空隙在 10MHz 下大概几百纳秒不影响功能。5.4 功耗与温度实测我在 -40℃ 和 85℃ 下各跑了 24 小时连续读写测试。MRAM 的读写电流在 10MHz 下约 5mA待机电流 1mA 左右。PIC18F4585 在 40MHz 下约 15mA。整个系统 3.3V 供电总功耗不到 70mW。高温下 MRAM 的写入时间略有增加但仍在微秒级没有出现写入失败。低温下启动正常不需要预热。6. 几个容易被忽略的细节6.1 状态寄存器的块保护位MR25H40CDF 的状态寄存器里有 BP0、BP1 和 WPEN 位可以设置块保护防止误写。工业产品里我一般会把关键配置区保护起来只留数据区可写。设置方法就是发 WRSR 命令写状态寄存器。但注意写状态寄存器也需要先发 WREN而且 WP 引脚如果拉低状态寄存器就写不进去了。我一般把 WP 引脚接高电平用软件保护就够了。6.2 上电时的 CS 时序有些工程师会遇到上电后第一次通信失败的问题。原因是 MCU 复位期间 IO 是输入状态CS 可能被外部拉低MRAM 误以为有命令进来。解决方法是在 CS 上加 10k 上拉电阻并且在初始化代码里第一时间把 CS 配置为输出并拉高。6.3 多字节写入的地址递增MRAM 的地址在写入过程中会自动递增但如果你写到芯片末尾0x7FFFF它会回卷到 0x00000。这个特性在做环形缓冲区时特别有用不用手动判断边界。但如果你不想回卷就要在软件里限制长度。6.4 SPI 时钟极性与相位的再确认虽然模式 0 和模式 3 都支持但如果你用模式 3记得把 SSPSTAT 的 CKE 位设为 1SMP 位根据采样时刻调整。我建议统一用模式 0因为这是最通用的换其他厂家的 SPI 器件也兼容。7. 方案扩展与个人体会这套 MRAM 加 PIC18 的方案我后来还扩展到了几个变种。比如把 PIC18F4585 换成 PIC18F46K80SPI 时钟可以跑到 16MHz读写更快。或者把 MR25H40CDF 换成更大容量的 MR25H256256KB引脚兼容软件不用改。如果系统需要文件系统可以在 MRAM 上跑一个精简的 FAT 或者 LittleFS但要注意 MRAM 没有擦除概念文件系统的擦除操作要映射成写 0xFF。我个人在实际操作中的体会是MRAM 最大的价值不是速度快而是省心。不用管擦除、不用管寿命、不用管掉电软件逻辑简单了一大截。虽然单颗芯片比 EEPROM 贵几块钱但省下来的开发时间、调试时间和现场维护成本早就赚回来了。如果你正在做工业数据记录不妨试试这个组合踩坑的概率比 Flash 方案低很多。最后再分享一个小技巧在 PCB 上给 MRAM 预留一个 SO 引脚的测试点调试时用示波器或者逻辑分析仪抓 SO 上的数据比用 printf 打印快得多也直观得多。我每次做新板子都会留这个测试点省了不少调试时间。
返回列表