工业控制类的项目做多了,你会越来越清楚一个道理:真正卡住进度的往往不是算法,也不是通信协议,而是“数据往哪儿放”这件小事。设备要记录运行曲线、掉电要保存最后状态、产线上要留一批日志给售后分析,这些需求落到单片机层面,一下就变成了几个很现实的追问:Flash 写入太麻烦,每个字节都得搬整个扇区;EEPROM 寿命不够,写多了心里发虚;SRAM 加电池的方案看着稳,但三五年后换电池的成本和麻烦又落到自己头上。
这次做的工业数据采集模块,我选了两颗器件来回答上面的问题:存储侧用 Everspin 的 MR25H40CDF,一颗 4Mbit 串行 SPI MRAM;主控侧用 Microchip 的 PIC18F57Q43,一颗 8 位 PIC 系列里外设比较全的 MCU。这套组合把“频繁改写”“掉电不丢”“免擦除管理”这几个需求一次解决掉了。这篇就把项目里的选型逻辑、硬件连接、驱动实现和踩坑经验完整过一遍,给同样在做工业嵌入式存储设计的朋友做个参考。
1. 为什么我放弃 NOR Flash 和 EEPROM,改用 MRAM
1.1 三个传统存储方案各自的别扭之处
过去在这个位置我会直接放一颗 SPI NOR Flash,比如 25 系列的 4Mbit 芯片,理由是容量够、价格低、代码里也有成熟驱动可以抄。但实际做产线设备的数据记录时,问题很快就暴露了:NOR Flash 的最小擦除单位是扇区,哪怕你只想改一个 4 字节的时间戳,也要先把整个扇区(通常 4KB 或 64KB)读到 RAM,擦除,再整块写回去。中途一旦断电,可能连其他参数一起损坏。
EEPROM 没有扇区擦除的麻烦,按字节写就行,但两个短板让人难受:一个是写周期长,典型 SPI EEPROM 单字节写要 5ms 级别,连续写大量的数据时时间开销很大;另一个就是寿命,传统浮栅 EEPROM 大概十万次擦写,看着不少,但如果设备每 10 秒存一次状态,一天就是 8640 次,撑不到两周就把寿命霍霍完了。工业显示屏、边缘网关这类设备工作环境又特别恶,环境温度高会进一步加速寿命衰减。
SRAM 加后备电池的方案我也认真考虑过,性能和寿命是够看的,但它在工业现场有两个天然毛病:一是电池本身的工作温度范围经常到不了 -40℃ 到 +85℃ 的严苛区间,锂电池在低温下容量缩水严重;二是产品的可维护性变差,用户每隔两三年就得换一次电池,换不好就丢数据,售后团队会非常头疼。
1.2 MRAM 的存储原理和它带来的几个优势
MRAM(磁性随机存储器)和 Flash、EEPROM 走的是完全不同的路线。它用磁隧道结(MTJ)来存储数据,写入数据时改变的是磁性层的磁化方向,而不是在浮栅里注入电荷。这意味着它既不依赖电荷积累,也不存在“擦除”这个物理动作。
落到工程上,这几条直接解决了前面所有痛点:
- 非易失:掉电之后数据不丢,不需要电池。
- 写入无需擦除:想写哪儿就写哪儿,一个字节一个字节写和整块写速度一样。
- 寿命极高:Everspin 官方给 MRAM 的写耐久度在 10^14 次这个数量级,对嵌入式项目来说基本等于无限次。
- 读写速度接近 RAM:SPI 时钟拉高之后,读写就跟普通 SRAM 外设一样快,没有 EEPROM/Floating Gate 那样的慢写入周期。
- 写数据是“当场生效”的,不需要等待内部编程/擦除完成信号。
MR25H40CDF 这颗芯片就是典型的串行 MRAM:4Mbit,也就是 512KB,容量对标最常见的 25 系列 SPI NOR Flash;工作电压 3.3V,工业温度范围;接口是标准 SPI,支持模式 0 和模式 3。它的管脚定义也和 25 系列 Flash 非常接近,CS#、SCK、SI、SO、WP#、HOLD#,硬件上甚至可以直接照搬 SPI Flash 的布局,只是软件思路要彻底换一下。
1.3 主控为何选 PIC18F57Q43
PIC18F57Q43 是 Microchip 的 8 位 MCU,属于 PIC18-Q43 系列。选它不是因为性能极致,而是因为这一系列的外设在“工业现场数据记录”这个场景下匹配度很高:自带 SPI 外设,带 DMA 控制器,可以把 SPI 接收的数据自动搬到内存,CPU 不用逐字节搬运;CLCC(可配置逻辑单元)可以用来做一些信号调理,省一颗逻辑芯片;12 位 ADC 带计算功能,直接把传感器采到的原始数据和 MCU 内部温度值扔进日志一起记录。
更重要的是这颗 MCU 做成工业级,温度范围能覆盖 -40℃ 到 +125℃,和 MRAM 的工业级规格搁在一起没有短板。8 位架构也符合很多老牌工业客户的软件团队习惯,C 代码在 PIC18 上跑得很顺,量产后供货稳定性和成本都容易控制。对这类项目,我不追求新潮主控,反而更看重生态、工具链和长期供货。
2. MR25H40CDF 硬件细节盘点:引脚、命令集、状态寄存器与写保护
2.1 引脚和上下拉设计
MR25H40CDF 的引脚不多,典型的 8 引脚 SPI 器件。我按项目里的实际连接方式整理如下:
| 引脚 | 方向 | 连接说明 |
|---|---|---|
| CS# | 输入 | 片选,低有效;务必接一个 4.7kΩ 到 10kΩ 上拉 |
| SCK | 输入 | SPI 时钟 |
| SI | 输入 | 主机 SDO 连到 SI,命令和数据输入 |
| SO | 输出 | 连到主控 SDI,数据输出 |
| WP# | 输入 | 硬件写保护;不用时绝不能悬空,直接接上拉到 VCC |
| HOLD# | 输入 | 暂停串行通信;不用时同样必须上拉 |
| VDD | 电源 | 2.7V~3.6V,典型 3.3V |
| VSS | 地 | 接地,周围加 100nF 去耦电容 |
这个表里我最想强调的就是 WP# 和 HOLD# 这两个脚。很多工程师习惯“不用就悬空”,这在实验室里可能一切正常,到了工业现场就会出现莫名其妙的问题:HOLD# 悬空时,如果总线上有毛刺,有可能把芯片误拉进 Hold 状态,表现为“读着读着一半就卡住了”;WP# 悬空更危险,如果电源时序异常,芯片随时可能被写入错误数据。
我在这类设计上的结论是:所有功能引脚要么由 MCU 明确控制,要么硬件上拉,坚决不留浮空脚。尤其是 CS#,主机复位期间如果 CS# 有低电平毛刺,MRAM 可能把 SCK 线上残留的脉冲当成命令,误写误读。所以 CS# 不仅要上拉,我还会把它接到 MCU 里一个默认输出高电平的 GPIO 上,并且放在所有外设初始化之前先置高。
2.2 SPI 命令集:其实和 SPI Flash 长得很像
MR25H40CDF 的指令集对写过 25 系列 Flash 的人来说非常亲切:
| 命令 | 操作码 | 功能说明 |
|---|---|---|
| WREN | 0x06 | 写使能,修改内存数据前必须执行 |
| WRDI | 0x04 | 写禁止 |
| RDSR | 0x05 | 读状态寄存器 |
| WRSR | 0x01 | 写状态寄存器,配置 BP0/BP1 保护位 |
| READ | 0x03 | 普通读,24 位地址,连续读 |
| FREAD | 0x0B | 快速读,比普通读多一个 dummy 字节,适合高频连续读 |
| WRITE | 0x02 | 写数据,24 位地址,连续写,无需擦除 |
| SLEEP | 0xB9 | 进入深度休眠 |
| WAKE | 0xAB | 唤醒芯片 |
读写操作都使用 24 位地址,但实际上只需要 19 位就能覆盖 512KB 空间(2^19=524288),所以地址字节的高 5 位会被忽略。每次读写都是按字节地址递增进行的,地址可以从 0x00000 一直递增到 0x7FFFF,然后回卷。这个特性和 SPI Flash 完全不同,Flash 必须按页(通常 256B)写,跨页还得拆;MRAM 完全没这回事,地址可以连续走,写到最后回卷也合法。
2.3 状态寄存器和块保护
MR25H40CDF 的状态寄存器里有 WEL(写使能锁存)、BP0、BP1 这几个位。WEL 很好理解,和大多数 SPI 存储芯片一样的套路:每次写内存前要先发 WREN 命令置位 WEL,否则写入命令不生效。这也是防止误写的第一道关卡。
BP0、BP1 是块保护位,可以保护地址空间的上半区或全部区域呢,还是其他区间呢,得看数据手册那张“Protected Area”表。实际含义是针对某些地址区段启用写保护,被保护的范围内,即使执行了 WREN 也不能改数据。
我第一次用这颗芯片时,下意识想“MRAM 寿命都无限了,块保护还有啥用”?后来现场出过一次事故才想明白:块保护保护的不是寿命,而是防止固件 bug 在意外情况下把关键参数区改坏。所以成熟产品的做法是在启动时配置好 BP0/BP1,把校准参数、序列号、安全密钥这些很金贵的字段锁进保护区域,日志区保持开放随便写。改起来也容易,初始化的时候先 WRSR 设置好,之后正常读写不用反复管它。
这里要提醒一句,WRSR 命令本身通常最好也执行一次 WREN 再写,保证状态寄存器不会被误操作。不同批次芯片的具体细节有差异,上机前把官方数据手册的 WRSR 时序段看两遍,别只抄代码。
3. PIC18F57Q43 侧接口设计:引脚映射、SPI 配置和电源处理
3.1 有功底的接线方案
整机供电如果是 5V 系统,这里有个必须提前决定的事:MR25H40CDF 工作电压上限是 3.6V,不能直接挂到 5V 的 SPI 总线上。
我这次做的是 3.3V 系统,PIC18F57Q43 直接在 3.3V 下运行,所以电平完全兼容。如果应用必须用 5V 主控,那最稳妥的方案是加电平转换芯片,比如 TXS0108 或具有方向控制的双向电平转换器;不要试图靠分压电阻硬扛,工业现场的干扰和信号完整性不允许这种偷懒做法。
电路连接不复杂,总计也就 6 根线,列表如下:
- MRAM CS# —— PIC18F57Q43 GPIO,比如 RA0,上拉到 VCC。
- MRAM SCK —— PIC18F57Q43 SPI SCK 引脚,走 PPS 分配。
- MRAM SI —— PIC18F57Q43 SDO 引脚,走 PPS 分配。
- MRAM SO —— PIC18F57Q43 SDI 引脚,走 PPS 分配。
- MRAM WP# —— 直接 10kΩ 上拉到 VCC。
- MRAM HOLD# —— 直接 10kΩ 上拉到 VCC。
电源端我会在 MRAM 的 VDD 和 VSS 之间放 100nF 陶瓷电容,尽量靠近引脚。PIC18F57Q43 的 VDD 也有多组电源脚,每一对电源脚附近都要放去耦电容,这在工业级 EMC 测试里是拿经验换来的教训。
3.2 PIC18F-Q43 的 PPS 引脚映射,容易被忽略的一步
PIC18F57Q43 的 SPI 外设引脚本不是硬固死在某个引脚上的,它靠 PPS(Peripheral Pin Select,外设引脚选择)机制把外设功能映射到器件的物理引脚上。
这意味着,如果直接用寄存器初始化 SPI,你必须先做两步映射工作:
- 把 SCK、SDO 这两个输出信号通过“外设输出选择寄存器”指派到具体的 IO 引脚。
- 把 SDI 这个输入信号通过“输入引脚选择寄存器”绑定到外设模块。
如果漏了这一步,SPI 配置得再对也不会有输出。在 MCC(MPLAB Code Configurator)里操作最直观,图形化界面里直接选中 SPI 外设、选引脚、选模式,代码生成器会自动填好 PPS 寄存器。手写代码时则要根据具体引脚编号查 Q43 数据手册的 PPS 映射表,填入对应的寄存器。
工程实践中我喜欢把所有 PPS 配置代码集中在初始化函数最前面,还有注释说明用了哪个物理引脚,这样三个月后回来维护的人不至于对着寄存器配置一脸懵。
3.3 SPI 模式选择和频率设置
MR25H40CDF 支持 SPI Mode 0 和 Mode 3,两者的区别就是时钟极性/相位组合不同。Mode 0 是 CPOL=0、CPHA=0,也就是 SCK 空闲为低电平,数据在 SCK 上升沿采样;Mode 3 是 CPOL=1、CPHA=1,SCK 空闲为高电平,数据也在上升沿采样。
在 PIC18F57Q43 上,SPI 模式由 SPI 控制寄存器里的 CKP 和 CKE 位决定,具体的 CKP/CKE 与 CPOL/CPHA 对应关系要严格按数据手册配置。用 MCC 时直接在 SPI 设置界面里选“SPI Mode 0”,生成器会处理好一切。
时钟频率上,MR25H40CDF 是能承受几十 MHz 级别 SPI 时钟的(数据手册标称在 40MHz 量级),但工业现场我不会把总线拉满。我这次用的是 10MHz 到 16MHz 的 SPI 时钟,原因有三:
- 线缆不可能像实验室飞线那样短,工业设备里布线和干扰都要留裕量。
- MCU 还有其他中断任务,太快会让中断和 DMA 调度的时序紧张。
- 512KB 容量本来就不大,即使 10MHz 时钟跑,等效传输率也有 1.25MB/s 理论值,日志记录场景根本用不到更高。
4. 驱动层实现:从裸读写到数据记录流水线
4.1 底层 SPI 字节收发函数
不管上层做成什么样,底子就是“拉低片选、收发一个字节、拉高片选”。
下面的代码是简化 Demo 思路,PIC18 的具体寄存器名以你用的型号为准,我用极简的伪寄存器写法突出逻辑:
uint8_t spi_xfer(uint8_t tx) { // 将 tx 写入 PIC18F57Q43 的 SPI 发送寄存器 // 等 SPI 忙标志清掉 // 返回接收寄存器里的值 }这个函数同时完成发送和接收,因为 SPI 是全双工,发送一个字节的同时就会收到一个字节。
4.2 MRAM 读写驱动
有了 spi_xfer 之后,MRAM 的驱动就清晰了。
写使能:
void mram_write_enable(void) { mram_cs_low(); spi_xfer(0x06); // WREN mram_cs_high(); }读指定字节数据:
void mram_read(uint32_t addr, uint8_t *buf, uint32_t len) { mram_cs_low(); spi_xfer(0x03); // READ spi_xfer((addr >> 16) & 0xFF); spi_xfer((addr >> 8) & 0xFF); spi_xfer(addr & 0xFF); for (uint32_t i = 0; i < len; i++) { buf[i] = spi_xfer(0x00); // 只需要时钟,发 0x00 即可 } mram_cs_high(); }写入指定数据:
void mram_write(uint32_t addr, const uint8_t *buf, uint32_t len) { mram_write_enable(); // 每次写之前必须 WREN mram_cs_low(); spi_xfer(0x02); // WRITE spi_xfer((addr >> 16) & 0xFF); spi_xfer((addr >> 8) & 0xFF); spi_xfer(addr & 0xFF); for (uint32_t i = 0; i < len; i++) { spi_xfer(buf[i]); } mram_cs_high(); }注意 READ 和 WRITE 都支持任意长度连续读写,没有页边界。只要 CS# 低电平期间把时钟和地址数据送完,数据就能连续走完。这一点是 MRAM 和 SPI Flash 差异最大的地方,原来在 Flash 上写的“到页尾要暂停/换页”逻辑完全可以删掉。
4.3 把 MRAM 当“环形日志”用,一次擦除都不用做
这是 LZ 数据结构上最直接受益的应用场景:数据记录器。
以前用 SPI Flash 做环形日志,流程是:记录指针走到尾部,先擦除整个扇区,再等擦除完成(有时几十毫秒到几百毫秒),然后才能写新数据。擦除期间如果来了更高优先级的报警事件,日志就断了。
MRAM 方案下根本不需要擦除。我定义一个循环区域,比如从 0x1000 开始到 0x7FFF 共 28KB 专门放日志,每一条日志固定 32 字节:
// 日志头部,固定放在区域最前面 typedef struct { uint32_t magic; // 固定 0x5A5A1234,用于识别有效日志区域 uint32_t nextIndex; // 下一条日志的写入偏移 } log_header_t;每次追加日志时,读 header 里的 nextIndex,直接把新日志写到 nextIndex 处,更新 nextIndex,写完后再把 header 写回。nextIndex 超过区域末尾时直接回到区域起始,因为 MRAM 不需要擦除,旧数据被自然覆盖掉就行。
在掉电恢复的检查上,因为 MRAM 是非易失的,断电后 nextIndex 还在原地,但为了应对“正在写一条日志时断电”的半条数据,我会在日志结构里加一个 magic 和 CRC 字段。CRC 校验失败就认为该条无效,从下一条继续。这个流程比 Flash 的“检查扇区擦除标志位”要简单太多。
4.4 用 DMA 减轻 CPU 负担
PIC18F57Q43 的 DMA 控制器可以完成“从 SPI 接收数据到 SRAM”和“从 SRAM 数据到 SPI 发送”这类搬运工作,CPU 只需要在传输完成时收到一个中断。
比如要把 MRAM 里某一段 1KB 的出厂配置读到内存做 CRC 校验,理想流程是:
- CPU 配置好 DMA 源地址/大小、目标地址/大小,触发源选 SPI 接收事件。
- DMA 每次收到一个字节自动搬到目标 SRAM。
- 计数到达 1KB 后 DMA 产生中断,CPU 再做校验。
这样做最大的好处是,在 DMA 搬运期间 CPU 可以继续跑传感器采集任务,整个系统更像一个流水线。MCC 里配置 DMA 也很直观:创建 DMA 通道,选择传输方向,指定 SPI 接收为触发源,生成代码后检查寄存器配置是否正确。
需要提醒的是,DMA 的缓存区大小要注意 PIC18 SRAM 只有 12KB。如果打算用 DMA 搬大块数据,就得把缓冲切成小段,比如每 256 字节做一次 DMA 传输,避免老想着一次性搬 512KB。
5. 工业现场最容易踩的三个坑:掉电毛刺、PPS 漏配、块保护误设置
5.1 掉电瞬间的幽灵写:CS 毛刺是元凶
实验室里功能完全正常,一上真实工业电网就出幺蛾子。最常见的就是电源跌落或复位瞬间,主控 GPIO 变成高阻状态,CS# 信号由于没有上拉,电压随着噪声飘来飘去。如果此时 SPI 总线上恰好还有残余时钟,MRAM 就可能把 CS# 低电平期间的噪声当成一次 WRITE 命令,往错误地址写入垃圾数据。
解决办法分两层:
- 硬件上,CS# 必须加外部上拉电阻,并且 MCU 侧的 GPIO 在上电复位阶段要配置为默认输出高。
- 软件上,启动初始化最开头先把 CS GPIO 拉高,然后再配置 PPS、SPI 和 MRAM 驱动。
在整机掉电测试时,用示波器同时抓 VDD 和 CS#,确认 VDD 降到 2.0V 以下之前 CS# 一直是高电平。若发现 CS# 有毛刺,就加强上拉或加个小电容滤波(比如 1nF)。
另外一个细节是 MRAM 的 WP# 在掉电期间的逻辑电平也要看好。WP# 一旦低于阈值,芯片会对整个阵列启用写保护,通常这是好的,但如果在“写使能之后、正在写数据”的路上 WP# 瞬间抖动,那一次写操作既可能被中断也可能被废弃。安全做法是把 WP# 也接上拉到 VCC,让它整个生命周期内都保持“允许写”状态,是否允许写完全交给代码里的 WEL 和 BP 控制。
5.2 PPS 没配好:读回全是 0xFF 或 0x00
PIC18F57Q43 项目里遇到最多的问题,不是 SPI 配置错,而是 PPS 引脚没配或者配错。现象很经典:初始化函数看起来一切都对,寄存器也写了,但读 MRAM 老是全 0xFF,示波器一看 SCK 压根没有波形。
原因是 SPI 的 SCK、SDO 属于“外设输出”,必须通过 PPS 输出选择寄存器映射到物理引脚;SDI 是“外设输入”,要通过 PPS 输入选择寄存器绑定。很多从老式 PIC 转过来的工程师没有这个概念,以为 SPI 引脚是硬件固定的,于是浪费半天时间。
排查顺序我一般是这样:
- 示波器先看 SCK、SI、SO 三个信号有没有电平活动。
- 如果 SCK 没有,先检查 PPS 输出配置是否指向了当前实际连线的引脚。
- 如果 SCK 有,再看 CS# 是否由 GPIO 正确控制。
- 如果一切都对,再看 SPI 模式是不是 Mode 0/3。
检查完 PPS 和 SPI 模式之后,再对着数据手册核对一遍 SCK 极性和相位,这步不能省。
5.3 写保护位被误设置:读正常,写不进去
听上去像废话,但真发生过:设备出厂前做老化和测试时,某个测试脚本偷偷执行了 WRSR,把 BP 保护位全部拉起来了。结果到客户现场,日志写不进去,读数据却完全正常,单看读操作一点线索都没有。
排查方法是先读状态寄存器:
uint8_t mram_read_status(void) { uint8_t val; mram_cs_low(); spi_xfer(0x05); // RDSR val = spi_xfer(0x00); mram_cs_high(); return val; }如果返回值的 BP0/BP1 位不为 0,先按数据手册的步骤解除保护。解除保护时最好按这个顺序:
- 发送 WREN。
- 发送 WRSR,写入 new status 值(例如清零 BP0/BP1)。
- 再次读状态寄存器确认。
更严谨一点:量产线做 MRAM 测试时,最后一步应该主动写入一组“只允许开放日志区,锁住参数区”的状态寄存器值,然后由产线测试程序验证关键区确实被保护了,再发货。
5.4 磁场干扰该不该担心
工业现场有很多电机、变压器、大电感,工程师会天然担心磁性存储器件会不会被干扰。实际情况是,MRAM 的封装内部有磁屏蔽层,正常安装位置下抗磁场干扰能力是够用的;像 MR25H40CDF 这类产品本身也针对工业环境做了电磁兼容设计。
但设计时还是要有基本常识:不要把它贴着电源电感、接触器线圈或大电流母线放,尽量让 MRAM 的长轴方向和可能的大磁场源保持一定空间距离。另外,产品做 EMC 测试时,加上“工频磁场抗扰度”这一项再来验证,省得后面认证阶段返工。
6. 从调通到量产:性能计算、校验测试和代码审查清单
6.1 实际吞吐量心里要有数
MRAM 快,但很多开发者在写代码前并没有认真算过自己的数据吞吐需求。以我这次用的 10MHz SPI 时钟为例:
- 10MHz / 8bit ≈ 1.25MB/s 的理论字节速率。
- 读写 512 字节:512 / 1.25 ≈ 0.4ms。
- 读写 64 字节:64 / 1.25 ≈ 0.05ms,也就是 50µs 上下。
所以一个典型场景“每秒存 5 条 32 字节日志”,占用总线时间只有约 160µs/s,MCU 负担非常小。如果需要更激进,把 SPI 配置到 20MHz 甚至 40MHz,还能再快一倍到数倍。对大部分工业数据记录需求来说,瓶颈反而在 MCU 中断频率和 DMA 调度,而不在 MRAM 本身。
6.2 量产自检程序怎么写
每一批板子回来,我建议在产线测试阶段花几秒把 MRAM 完整测一遍,因为 MRAM 是外置芯片,焊接虚焊、芯片本身运输损坏都可能存在。方法也不难:
- 全片写固定模式,比如 0xA5,然后全片读回验证。
- 再写 0x5A,读回验证。
- 在首地址、中间地址、末尾地址各写一串随机数,读回校验。
- 写一个字节后断电重启,再上电读回,确认非易失性。
第四步如果产线不方便断电,也可以把“掉电保持”挪到老化测试阶段来验证。此处有一点要诚实说明:全片写满 512KB 要写入 524288 个字节,在 10MHz 时钟下约 0.4 秒,两次模式加读回也就 1 秒多,完全可接受。
6.3 代码审查清单:针对本方案容易忽略的地方
做设计评审或者自己最后复盘时,我习惯对着清单逐条过:
- 所有 GPIO(尤其 CS#、WP#、HOLD#)是否都有明确上拉状态,是否有浮空脚。
- PPS 映射代码是否与原理图引脚一致。
- SPI 时钟频率是否低于 MRAM 数据手册上限,并留有现场抗干扰裕量。
- WRSR/块保护设置在启动时是否一次性完成,运行过程中有没有被意外覆盖的可能。
- 每次写入前是否都执行了 WREN。
- 读回数据和写入数据的 CRC 校验有没有覆盖“断电写一半”的场景。
- MRAM 供电电压是否和主控总线电平兼容,有没有偷偷超压。
- 掉电时序测试的示波器截图是否存档,CS# 在 VDD 跌落前始终保持高电平。
这些问题看起来细碎,但它们决定了产品到客户现场之后是“没人提”还是“售后一天到晚接电话”。
6.4 最后一点个人体会
这套 MRAM + PIC18F57Q43 组合做完之后,最直接的感受是整个存储代码变“干净”了:不用维护擦除函数,不用考虑坏块表,不用在中断里晒擦除等待时间,环形日志只需要维护头部索引加 CRC。对工业嵌入式工程师来说,能少维护一类存储介质的状态机,就是把时间花在更值得处理的问题上。如果后续项目容量需求再往上走,Everspin 也有 8Mbit、16Mbit、32Mbit 的串行/并行 MRAM 可选,驱动移植成本并不高,整套架构弹性是够的。