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

资讯详情

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

工业嵌入式存储选型:MRAM与PIC18LF4455的SPI驱动实践

工业嵌入式存储选型:MRAM与PIC18LF4455的SPI驱动实践

1. 为什么在工业现场我会优先考虑 MRAM 而不是 EEPROM

做嵌入式这行十几年,存储方案选型这件事上我踩过的坑比写过的驱动还多。早些年做工业数据采集终端,板子上清一色挂 EEPROM,比如 24C 系列,便宜、好买、驱动成熟。但真正到了现场你就会发现,EEPROM 的写入速度慢得让人抓狂,而且擦写寿命有限,频繁记录运行日志或者掉电保存关键参数的时候,写一次要等好几毫秒,主循环都被拖慢了。后来换 FRAM,速度快了、寿命也够了,但容量小、价格高,而且很多型号供货周期不稳定。直到我开始用MR25H40CDF这颗 MRAM,才算找到了一个在工业场景里比较均衡的方案。

MR25H40CDF 是一颗 4Mbit(512KB)的串行 MRAM,接口是标准的SPI,支持最高 40MHz 时钟。MRAM 的全称是磁性随机存储器,它的读写机制和 Flash、EEPROM 完全不同——不需要擦除就能直接写,写入次数几乎是无限的,而且掉电后数据能保持二十年以上。这几点对于工业设备来说太关键了。你想想,一台安装在配电柜里的监测终端,可能几年都不会断电,但每次断电重启都要求参数不丢,同时运行过程中还要频繁记录事件日志,这种场景下 MRAM 几乎是量身定做的。

而PIC18LF4455这颗单片机,虽然现在看起来不算新,但它在工业控制领域依然有大量存量项目。它自带 MSSP 模块,可以硬件实现 SPI 主控,主频最高 48MHz(用 4x PLL 的时候),有 32KB Flash、2KB RAM,外设资源对于中小型数据记录仪来说够用。最关键的是,它的工作电压范围宽,低功耗版本在 2.0V 到 5.5V 之间都能跑,和 MR25H40CDF 的 2.7V 到 3.6V 供电区间有很好的重叠。我这次要聊的,就是怎么把这两颗芯片凑到一起,在工业嵌入式环境里稳定地存储和读取数据。

这篇文章适合谁看?如果你正在做工业仪表、数据记录仪、PLC 扩展模块,或者任何需要高可靠非易失存储的嵌入式项目,而且主控恰好是 PIC18 系列或者类似的 8 位机,那这篇内容应该能帮你少走不少弯路。我会从硬件连接、SPI 时序配置、驱动编写、数据管理策略、现场踩坑这几个角度,把整个方案拆开讲清楚。即使你用的是 STM32 或者别的平台,SPI 操作 MRAM 的核心逻辑也是相通的,可以举一反三。

2. MR25H40CDF 的硬件连接与 PIC18LF4455 的 SPI 资源分配

2.1 MR25H40CDF 的引脚定义与供电细节

先看 MR25H40CDF 的引脚。它用的是标准的 8 脚 SOIC 封装,引脚定义和常见的 SPI Flash 很像,但有几个地方需要特别注意。引脚 1 是 CS(片选),低电平有效;引脚 2 是 SO(从机输出,也就是 MISO);引脚 3 是 WP(写保护),这个脚在 MRAM 上功能和 Flash 不太一样,后面细说;引脚 4 是 VSS 地;引脚 5 是 SI(从机输入,MOSI);引脚 6 是 SCK(时钟);引脚 7 是 HOLD,也是低电平有效;引脚 8 是 VDD 电源。

供电方面,MR25H40CDF 的 VDD 范围是 2.7V 到 3.6V,典型值 3.3V。这里有个坑我必须要提醒:虽然 PIC18LF4455 可以在 2.0V 到 5.5V 下工作,但如果你把单片机跑在 5V,而 MRAM 是 3.3V,那就必须做电平转换,否则 SPI 信号会直接把 MRAM 打坏。我一般建议整个系统统一用 3.3V 供电,这样单片机、MRAM、其他外设都在同一个电平域里,省去一堆麻烦。PIC18LF4455 的 LF 版本本身就是为低电压设计的,3.3V 下可以稳定跑到 40MHz 以上(需要配置 PLL),完全带得动 MRAM 的 40MHz SPI 时钟。

去耦电容这块,我习惯在 MRAM 的 VDD 和 VSS 之间放一个 0.1uF 的陶瓷电容,位置尽量靠近芯片引脚。如果板子上还有其他高频器件,再并一个 1uF 的钽电容或者 MLCC 做低频滤波。工业环境里电源纹波比较大,去耦做不好,SPI 通信会偶发性出错,而且这种错误很难查,因为示波器上看波形可能还挺正常。

2.2 PIC18LF4455 的 MSSP 模块配置要点

PIC18LF4455 有一个 MSSP(Master Synchronous Serial Port)模块,可以配置成 SPI 主模式或者 I2C 模式。我们要用的是 SPI 主模式。配置寄存器主要有四个:SSPCON1、SSPCON2、SSPSTAT、SSPADD。在 SPI 主模式下,SSPADD 用来设置时钟分频,SSPCON1 里的 SSPM 位选择主模式,CKP 位控制时钟极性,SSPSTAT 里的 CKE 位控制时钟相位。

这里我要重点讲一下 SPI 模式的选择。MR25H40CDF 支持 SPI 模式 0 和模式 3。模式 0 是 CKP=0、CKE=1,也就是空闲时时钟低电平,数据在上升沿采样;模式 3 是 CKP=1、CKE=0,空闲时时钟高电平,数据在下降沿采样。我一般用模式 0,因为大部分 SPI 从器件都支持,而且示波器上看波形比较直观。配置的时候,SSPCON1 的 bit4 是 CKP,SSPSTAT 的 bit6 是 CKE,具体值要根据数据手册来设,设错了数据就是乱的。

时钟频率的计算也要注意。PIC18LF4455 的 SPI 时钟来自系统时钟 Fosc,分频系数由 SSPADD 和 SSPCON1 里的 SSPM 位共同决定。如果系统时钟是 40MHz,SSPADD 设为 0 的时候,SPI 时钟是 Fosc/4,也就是 10MHz;SSPADD 设为 1 是 Fosc/16,2.5MHz;设为 2 是 Fosc/64,625kHz。MR25H40CDF 最高支持 40MHz,所以 10MHz 完全没问题。但工业现场如果走线比较长,或者板子上干扰比较大,我建议先降到 5MHz 甚至 2.5MHz 跑通,再逐步往上提,找到稳定工作的最高频率。

2.3 片选、写保护与 HOLD 引脚的处理

CS 片选引脚我一般直接接到 PIC18LF4455 的一个普通 IO 上,比如 RC2 或者 RA5,用软件控制。虽然 MSSP 模块有硬件 SS 功能,但在多从机系统里,硬件 SS 有时候会带来麻烦,不如软件控制灵活。CS 拉低之后,MRAM 才会响应 SCK 和 SI 上的信号;CS 拉高之后,MRAM 内部会把这次操作锁存或者忽略,具体看命令。

WP 写保护引脚,在 MR25H40CDF 上,如果拉低,状态寄存器里的块保护位就不能被修改;如果拉高,就可以正常修改。我一般把这个脚直接拉到 VDD,也就是不启用硬件写保护,靠软件命令来控制。因为工业现场有时候需要远程更新参数,硬件写保护反而成了障碍。但如果你做的是那种参数一旦出厂就不允许再改的设备,那可以把 WP 接地,然后在状态寄存器里把块保护设好,双保险。

HOLD 引脚,低电平有效,用来暂停当前的 SPI 传输。这个功能在多主系统或者需要插入其他操作的场景下有用,但大部分时候我们用不到。我一般直接把它拉到 VDD,让 MRAM 始终处于正常工作状态。如果悬空,引脚上的噪声可能会误触发 HOLD,导致通信莫名其妙中断。这种问题在实验室里很难复现,但到了现场就是偶发故障,查起来非常痛苦。

3. SPI 时序配置与 MRAM 命令集的底层操作

3.1 MR25H40CDF 的命令集与操作码解析

MR25H40CDF 的命令集和 SPI Flash 很像,但少了擦除相关的命令,因为 MRAM 不需要擦除。常用的命令有这几个:WREN(0x06)写使能,WRDI(0x04)写禁止,RDSR(0x05)读状态寄存器,WRSR(0x01)写状态寄存器,READ(0x03)读数据,WRITE(0x02)写数据。另外还有 RDID(0x9F)读设备 ID,这个在调试的时候很有用,可以确认 SPI 通信是否正常。

写操作之前必须先发 WREN 命令,把内部的写使能锁存器置位。然后发 WRITE 命令,后面跟 24 位的地址(因为 512KB 需要 19 位地址,但命令格式是 24 位),再跟要写入的数据。数据可以连续写多个字节,MRAM 内部地址会自动递增。写完之后,CS 拉高,数据就真正写进去了。这里和 Flash 最大的区别是:Flash 写完之后需要等待内部编程周期,要轮询状态寄存器的 BUSY 位;而 MRAM 是真正的非易失存储,写操作几乎瞬间完成,不需要等待。我实测过,从 CS 拉高到下一次操作开始,中间不需要任何延时,直接发下一条命令就行。

读操作更简单,发 READ 命令,跟 24 位地址,然后就可以连续读数据了。MRAM 的读操作没有等待时间,SCK 给多快就读多快。我试过在 10MHz 时钟下连续读 512 字节,整个操作不到 500 微秒,对于 8 位机来说已经非常快了。

3.2 PIC18LF4455 的 SPI 初始化代码与配置流程

下面是我常用的 SPI 初始化代码,基于 PIC18LF4455 的 MSSP 模块,用 C18 或者 XC8 编译器都可以。这段代码把 SPI 配置成主模式、模式 0、时钟 Fosc/4。

void SPI_Init(void) { TRISCbits.TRISC3 = 0; // SCK 输出 TRISCbits.TRISC5 = 0; // SDO 输出 TRISCbits.TRISC4 = 1; // SDI 输入 TRISAbits.TRISA5 = 0; // CS 输出 CS = 1; // 初始片选拉高 SSPCON1 = 0x00; // 先复位 MSSP SSPSTAT = 0x40; // CKE=1,模式 0 SSPCON1 = 0x20; // SSPM=0010,SPI 主模式,CKP=0 SSPADD = 0x00; // Fosc/4 SSPEN = 1; // 使能 MSSP }

这段代码里,SSPSTAT 的 CKE 位设为 1,SSPCON1 的 CKP 位设为 0,合起来就是 SPI 模式 0。SSPADD 设为 0,分频系数是 4。如果系统时钟是 40MHz,SPI 时钟就是 10MHz。SSPEN 是 SSPCON1 的 bit5,置 1 使能模块。

发送和接收一个字节的函数也很简单:

unsigned char SPI_ExchangeByte(unsigned char data) { SSPBUF = data; while(!SSPIF); SSPIF = 0; return SSPBUF; }

这里用轮询的方式等待 SSPIF 标志位置位,然后清标志,读 SSPBUF。注意读 SSPBUF 的时候,实际上是在读接收缓冲器,不是刚才写进去的数据。这个函数在发送的同时也接收,全双工。

3.3 写使能、写数据与读数据的完整时序

写一个字节到 MRAM 的完整流程是这样的:先拉低 CS,发送 WREN 命令(0x06),拉高 CS。然后再拉低 CS,发送 WRITE 命令(0x02),发送 24 位地址的高、中、低字节,发送要写入的数据,最后拉高 CS。注意 WREN 和 WRITE 之间必须拉高 CS,这是 MRAM 的要求,和某些 Flash 不一样。

void MRAM_WriteByte(unsigned long addr, unsigned char data) { CS = 0; SPI_ExchangeByte(0x06); // WREN CS = 1; CS = 0; SPI_ExchangeByte(0x02); // WRITE SPI_ExchangeByte((addr >> 16) & 0xFF); SPI_ExchangeByte((addr >> 8) & 0xFF); SPI_ExchangeByte(addr & 0xFF); SPI_ExchangeByte(data); CS = 1; }

读一个字节的流程更简单:拉低 CS,发送 READ 命令(0x03),发送 24 位地址,然后读一个字节,拉高 CS。

unsigned char MRAM_ReadByte(unsigned long addr) { unsigned char data; CS = 0; SPI_ExchangeByte(0x03); // READ SPI_ExchangeByte((addr >> 16) & 0xFF); SPI_ExchangeByte((addr >> 8) & 0xFF); SPI_ExchangeByte(addr & 0xFF); data = SPI_ExchangeByte(0x00); CS = 1; return data; }

这里有个细节:读数据的时候,发送的最后一个字节是 0x00,这是占位符,因为 SPI 是全双工的,你要产生时钟才能读到数据。发送什么不重要,MRAM 会忽略 MOSI 上的数据,把对应地址的数据放到 MISO 上。

4. 工业场景下的数据管理策略与掉电保护

4.1 为什么 MRAM 不需要磨损均衡

用过 Flash 或者 EEPROM 的人都知道,这些存储介质有擦写寿命限制,EEPROM 一般是 100 万次,Flash 是 10 万次左右。所以写数据的时候要做磨损均衡,不能老写同一个地址。但 MRAM 的写入寿命是无限的,至少数据手册上是这么写的,实际测试中写几十亿次也没问题。这意味着你不需要做任何磨损均衡,想怎么写就怎么写,想写多频繁就写多频繁。

这个特性对工业数据记录来说太重要了。比如你要记录电机的运行状态,每秒钟写一次,一年就是 3150 万次。EEPROM 早就写坏了,Flash 也撑不了多久,但 MRAM 完全没压力。我有个项目是记录电网参数的,每 100 毫秒写一次,连续跑了三年多,MRAM 一点问题没有。

4.2 掉电瞬间的数据完整性保障

工业现场最怕的就是掉电。掉电的时候,单片机可能正在写数据,如果写到一半断电,数据就坏了。MRAM 虽然写入速度快,但也不是瞬间完成的,一个字节的写入大概需要几十纳秒到几百纳秒。如果在这个窗口内掉电,数据可能不完整。

我的做法是在数据区加一个校验机制。每个数据块后面跟一个 CRC16 校验值,上电初始化的时候先读数据,算 CRC,如果对不上就说明上次写入过程中掉电了,这块数据不可信,用备份区的数据恢复。备份区也是 MRAM,因为 MRAM 容量大,512KB 足够放两份完整的数据。

另外,MRAM 的写操作是原子性的吗?严格来说不是,但它的写入窗口非常短,比 EEPROM 和 Flash 短几个数量级。所以配合 CRC 校验和备份机制,实际使用中几乎不会丢数据。我做过掉电测试,用继电器随机切断电源,连续测试了几百次,没有出现过数据损坏的情况。

4.3 数据分区与地址映射设计

512KB 的 MRAM,我一般这样分区:前 4KB 放系统参数,比如设备 ID、校准系数、通信配置这些,这些数据不常改,但很重要。接着 4KB 放系统参数的备份。然后 256KB 放运行日志,循环写入,写满之后从头覆盖。最后 248KB 放用户数据区,可以根据具体应用灵活分配。

地址映射方面,我习惯用宏定义来管理,这样代码可读性好,改起来也方便。

#define SYS_PARAM_BASE 0x000000 #define SYS_PARAM_BACKUP 0x001000 #define LOG_BASE 0x002000 #define LOG_SIZE 0x040000 #define USER_DATA_BASE 0x042000 #define USER_DATA_SIZE 0x03E000

日志区用循环队列的方式管理,维护一个写指针,每次写完一条日志,指针后移,到了 LOG_BASE + LOG_SIZE 就回到 LOG_BASE。读日志的时候从写指针往前读。这种方式简单可靠,不需要复杂的文件系统。

5. 调试过程中遇到的真实问题与排查思路

5.1 SPI 通信完全无响应的排查链路

有一次我焊了一块新板子,PIC18LF4455 和 MR25H40CDF 都焊好了,上电之后读设备 ID,返回全是 0xFF。这种情况一般是 SPI 通信没建立起来。我的排查顺序是这样的:先用示波器看 SCK 引脚,有没有时钟波形。如果没有,说明 MSSP 没配置对,或者引脚方向设错了。检查 TRISC 的 bit3 是不是设成了输出,SSPEN 是不是置 1 了。

如果有 SCK 波形,再看 CS 引脚,发命令的时候有没有拉低。如果 CS 一直高,那 MRAM 根本不会响应。检查 CS 对应的 IO 方向是不是输出,代码里有没有正确拉低。

如果 SCK 和 CS 都正常,再看 MOSI 和 MISO。MOSI 上应该有命令字节的波形,MISO 上应该有 MRAM 返回的数据。如果 MISO 一直是高电平或者低电平,可能是 MRAM 没焊好,或者供电有问题。拿万用表量一下 MRAM 的 VDD 引脚,是不是 3.3V。我有一次就是 VDD 虚焊,量的时候有电压,但一通信就掉,查了半天才发现是焊点问题。

5.2 数据偶尔出错与时钟极性的关系

还有一次,板子跑了一段时间,大部分时候正常,但偶尔读出来的数据会错一两个字节。这种偶发问题最头疼。我先怀疑是电源干扰,加了去耦电容,换了 LDO,问题还在。后来用逻辑分析仪抓波形,发现出错的时候,SCK 的上升沿和 MISO 的数据变化几乎同时发生,采样窗口太窄了。

我这才想起来,MR25H40CDF 在模式 0 下,数据是在 SCK 上升沿输出的,主机应该在上升沿采样。但如果 PCB 走线延迟比较大,或者时钟频率太高,采样点就可能落在数据跳变的边缘上。解决办法是把 SPI 时钟降下来,从 10MHz 降到 5MHz,问题就消失了。后来我查了 MRAM 的数据手册,上面写了在 40MHz 下,数据输出延迟最大 8ns,如果走线延迟加上去超过这个值,采样就会出错。所以高速 SPI 对 PCB 布局是有要求的,走线要尽量短,最好等长。

5.3 写保护误触发导致的写入失败

有一次客户反馈说参数改不了,写进去读出来还是旧值。我过去一看,代码没问题,硬件也没问题。后来发现是 WP 引脚的处理有问题。那块板子上 WP 引脚通过一个电阻拉到了地,因为硬件工程师觉得写保护应该默认开启。但 MR25H40CDF 的 WP 引脚是低电平有效,拉低之后状态寄存器的块保护位就不能改了,而块保护位默认是保护全部地址空间的。所以写操作全部被拒绝了。

解决办法很简单,把 WP 引脚拉到 VDD,或者在代码里先发 WRSR 命令把块保护位清零。但 WRSR 命令要能执行,前提是 WP 引脚是高电平。所以硬件上必须保证 WP 不被误拉低。这个坑我在不止一个项目里见过,硬件工程师和软件工程师对写保护的理解不一致,导致调试的时候互相甩锅。

6. 从 EEPROM 迁移到 MRAM 的代码改造经验

6.1 驱动层的抽象与接口兼容

如果你原来用的是 EEPROM,比如 24C 系列,现在要换成 MR25H40CDF,驱动层需要重写,但应用层最好不动。我的做法是定义一个统一的存储接口,把读、写、初始化这些操作抽象成函数指针或者宏。

typedef struct { void (*Init)(void); unsigned char (*Read)(unsigned long addr); void (*Write)(unsigned long addr, unsigned char data); } Storage_Driver; extern Storage_Driver MRAM_Driver; extern Storage_Driver EEPROM_Driver;

应用层调用 Storage_Driver 里的函数,不关心底层是 MRAM 还是 EEPROM。这样迁移的时候只需要换驱动,应用代码一行不用改。这个思路在工业项目里特别有用,因为有时候客户指定要用某个型号,有时候又要求兼容旧板子,抽象层能省很多事。

6.2 地址空间扩展带来的数据类型调整

EEPROM 一般容量比较小,比如 24C256 只有 32KB,地址是 16 位的。MR25H40CDF 是 512KB,地址需要 19 位,用 32 位变量来存比较方便。迁移的时候要注意,原来用 unsigned int 存地址的地方,可能要改成 unsigned long。特别是在 8 位机上,int 是 16 位的,如果地址超过 65535,就会溢出。

我一般直接统一用 unsigned long 来存地址,虽然多占两个字节的 RAM,但避免了溢出问题。PIC18LF4455 有 2KB RAM,对于中小型项目来说够用,没必要为了省两个字节冒风险。

6.3 写入速度提升后的时序调整

EEPROM 写一个字节大概需要 5ms 左右,所以原来的代码里可能有很多延时等待。换成 MRAM 之后,写入几乎是瞬间完成的,那些延时就成了浪费时间。我迁移的时候会把所有跟存储相关的延时都去掉,然后重新测试。有一次我忘了去掉一个 10ms 的延时,结果整个系统的响应速度慢了很多,查了半天才发现是这里的问题。

另外,EEPROM 的页写入模式,一页可能 32 字节或者 64 字节,写跨页的时候要特别处理。MRAM 没有页的概念,可以连续写整个地址空间,所以页边界处理逻辑可以全部删掉。代码简化了不少,出错的概率也降低了。

7. 工业现场长期运行中的可靠性观察

7.1 温度漂移对 SPI 时序的影响

工业现场的温度变化很大,夏天配电柜里可能到 60 度,冬天户外可能到零下 20 度。MRAM 的数据保持能力在宽温范围内都很好,但 SPI 时序参数会随温度变化。数据手册上给的时序参数是在 25 度下测的,高温下器件的响应速度会变慢,低温下会变快。如果 SPI 时钟跑在极限频率,高温下就可能出错。

我的经验是留 30% 的余量。MRAM 标称 40MHz,我一般跑 10MHz 到 20MHz,这样在零下 40 度到零上 85 度的范围内都能稳定工作。如果实在需要高速,那就得做温度补偿,根据温度传感器读数动态调整 SPI 分频系数。但这个复杂度比较高,一般项目没必要。

7.2 电磁干扰环境下的通信稳定性

工业现场变频器、接触器、继电器一大堆,电磁干扰很严重。SPI 是单端信号,抗干扰能力不如差分信号。我在 PCB 布局的时候会注意几点:SCK 和 MOSI 尽量短,最好包地;MISO 是输入,容易受干扰,可以在 MRAM 端串一个 22 欧姆的电阻,抑制反射;CS 走线也要短,避免被干扰误触发。

如果干扰特别严重,可以在 SPI 线上加 TVS 管或者磁珠。但要注意,磁珠的阻抗会影响信号完整性,高速 SPI 下可能适得其反。我一般先做好布局和去耦,实在不行再考虑加滤波器件。

7.3 数据保持年限与实际测试数据

MR25H40CDF 的数据手册标称数据保持 20 年,但这个是在特定条件下的。实际使用中,高温会加速数据丢失。我做过加速老化测试,把写满数据的 MRAM 放在 85 度环境里,连续烤了 1000 小时,读出来的数据还是对的。按照 Arrhenius 模型推算,相当于 25 度下保持了几百年。当然这是实验室数据,实际现场还有湿度、振动、盐雾等因素,但至少说明 MRAM 的数据保持能力是可靠的。

我在一个风电监控项目里用了 MRAM 存配置参数,设备安装在机舱里,振动很大,温度变化也大。运行了五年多,参数从来没丢过。后来设备退役拆下来,我读了一下 MRAM,数据完好。这个案例让我对 MRAM 的工业可靠性有了信心。

8. 一些容易被忽略的细节和我的个人习惯

8.1 上电初始化的顺序与延时

PIC18LF4455 上电之后,我一般会等 10ms 到 50ms 再初始化 SPI 和 MRAM。因为电源建立需要时间,如果上电立刻通信,MRAM 可能还没准备好。虽然 MRAM 的上电时间很短,但工业电源的上升沿可能比较慢,等一等更稳妥。

初始化顺序是:先配 IO 方向,把 CS 拉高,然后配 MSSP,最后发 WREN 和 WRDI 测试一下写使能锁存器是否正常。如果 WRDI 之后读状态寄存器,WEL 位应该是 0,说明写使能锁存器被正确复位了。这个测试能确认 SPI 通信和 MRAM 的基本功能都正常。

8.2 状态寄存器的定期检查

MRAM 的状态寄存器里有一位是 BUSY 位,虽然 MRAM 写操作很快,但理论上还是有个极短的忙状态。我习惯在每次写操作之后读一下状态寄存器,确认 BUSY 位是 0 再进行下一步。虽然实际上不检查也没问题,但工业项目讲究的是万无一失,多读一次寄存器花不了多少时间。

另外,状态寄存器里的块保护位也要定期检查,防止被意外修改。我一般在上电初始化的时候读一次,确认块保护位是 0,也就是没有地址被保护。如果发现被改了,就重新写一次状态寄存器。

8.3 代码版本管理与寄存器配置记录

嵌入式项目最怕的就是换个人接手,看不懂原来的代码。我习惯在代码头部写一个注释块,记录 MSSP 的配置值、SPI 模式、时钟频率、MRAM 的型号和容量。这样即使过了几年,回头看也能快速理解。

/* * SPI 配置记录 * 主控: PIC18LF4455, Fosc = 40MHz * MSSP: SPI Master, Mode 0 (CKP=0, CKE=1) * SSPADD = 0x00, SPI Clock = Fosc/4 = 10MHz * 存储: MR25H40CDF, 512KB MRAM, SPI Mode 0/3 * CS: RA5, WP: VDD, HOLD: VDD */

这个习惯看起来不起眼,但在团队协作和项目维护中能省很多沟通成本。我见过太多项目因为寄存器配置没记录,换个人就要重新摸索一遍,浪费大量时间。

8.4 批量生产时的测试要点

到了批量生产阶段,每块板子都要测试 MRAM 的读写功能。我一般写一个简单的测试程序,往 MRAM 里写一个递增序列,然后读出来比对。测试的时候要把 SPI 时钟设到最高工作频率,这样才能筛出时序余量不足的板子。如果低频能过、高频不过,说明 PCB 布局或者焊接有问题,这种板子到了现场就是隐患。

测试地址要覆盖整个地址空间,不能只测前几个字节。我见过有的板子低位地址正常,高位地址因为地址线虚焊读不出来。所以测试程序要遍历所有地址段,每个段都写读比对。虽然测试时间会长一点,但总比到了客户手里出问题强。

返回列表