ARTICLE DETAIL

资讯详情

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

STM32F427ZI 与 MR25H40CDF MRAM 工业存储方案实战

STM32F427ZI 与 MR25H40CDF MRAM 工业存储方案实战 1. 项目缘起与方案选型思考1.1 为什么要在工业场景里折腾 MRAM 这种“新玩意”做嵌入式这行十几年存储方案来来回回就那么几套EEPROM 擦写寿命短、速度慢NOR Flash 写入前必须擦除掉电窗口期让人提心吊胆FRAM 读写快、寿命高但容量小、价格贵而且很多型号已经停产或者交期离谱。直到我在一个工业数据采集项目里被“频繁掉电 高频写入”这两个需求反复折磨才开始认真研究 MRAM 这条路线。MR25H40CDF 是 Everspin 推出的一款 4Mbit512KB串行 MRAMSPI 接口40MHz 时钟最关键的是它没有写入延迟——数据写进去就立刻非易失不需要像 Flash 那样等内部编程完成也不存在擦除操作。这意味着你在中断里直接写参数、在掉电中断里保存关键状态完全不用担心写一半断电导致数据损坏。STM32F427ZI 是 ST 家的 Cortex-M4 高性能 MCU180MHz 主频带 FPU外设资源丰富SPI 接口随便挑一个都能跑满 MR25H40CDF 的 40MHz 上限。这两者搭在一起基本就是工业嵌入式存储场景里“既要快、又要稳、还要简单”的最优解之一。这个组合适合谁如果你正在做工业控制器、电力监测终端、医疗设备数据记录、车载黑匣子这类需要高频写入 掉电不丢 长期可靠的项目那这套方案值得你花时间吃透。如果你只是做个消费级小玩具偶尔存个配置参数那用内部 Flash 模拟 EEPROM 就够了没必要上 MRAM。但只要你被“写坏过 Flash”“掉电丢过数据”“擦除等待拖慢实时性”这三件事坑过任意一件下面的内容就对你有用。1.2 MR25H40CDF 到底比 Flash 强在哪值不值得多花钱先看硬指标对比这张表是我选型时自己整理的数据来自手册和实测特性MR25H40CDF (MRAM)W25Q64 (NOR Flash)FM25V05 (FRAM)容量512KB8MB64KB接口SPI 40MHzSPI 104MHzSPI 40MHz写入前擦除不需要必须不需要写入延迟0立即非易失毫秒级页编程擦除0擦写寿命10^15 次10^5 次10^14 次数据保持20年20年10年工作温度-40~85°C-40~85°C-40~85°C单价参考中高低高MRAM 的核心优势在于写入即非易失。Flash 写一个字节的流程是发写使能 → 发页编程命令 → 等 1~3ms → 查状态寄存器确认完成。MRAM 写一个字节就是发写命令 → 发地址 → 发数据 → 完事下一个 SPI 时钟就能读回来。这个差异在实时系统里是致命的——你可以在 10us 内完成一次关键数据保存而 Flash 可能需要 5ms这 5ms 里如果掉电数据就没了。另一个容易被忽略的点是擦除寿命。Flash 擦写 10 万次听起来很多但如果你每 100ms 写一次数据一天就是 86.4 万次不到 3 小时就把一个扇区写废了。MRAM 的 10^15 次寿命意味着你哪怕每微秒写一次也要写 30 多年才到极限。对于需要记录运行日志、累计运行时间、故障快照的场景这个寿命优势直接决定了产品能不能“免维护”运行五年以上。1.3 STM32F427ZI 的 SPI 外设怎么选硬件片选还是软件片选STM32F427ZI 有多个 SPI 接口我一般优先用 SPI1 或 SPI2因为这两个挂在 APB2 和 APB1 上时钟源稳定DMA 请求映射也成熟。SPI1 在 APB2 上最高 90MHzSPI2/3 在 APB1 上最高 45MHz跑 MR25H40CDF 的 40MHz 都绰绰有余。片选信号这块有个实操细节STM32 的硬件 NSS 模式在作为主机时NSS 引脚要么输出低电平要么被其他主机的 NSS 拉低配置起来比较绕而且多从机场景下硬件 NSS 根本不够用。所以我强烈建议用软件片选——随便找一个 GPIO配置成推挽输出初始化时拉高通信前拉低通信后拉高。这样你可以在同一个 SPI 总线上挂多个从机每个从机一个片选引脚逻辑清晰调试也方便。注意软件片选时片选拉低到第一个 SPI 时钟之间要留至少 10ns 的建立时间STM32 的 GPIO 翻转速度在 180MHz 下大概几纳秒正常写代码不会违反但如果你在中断里频繁切换片选建议在拉低片选后加一个__NOP()或者几个空指令确保时序余量。2. 硬件连接与底层驱动细节2.1 硬件连线别小看那几根线接错了调一天MR25H40CDF 是 8 引脚 SOIC 封装引脚定义如下引脚名称说明1CS片选低有效2SO数据输出MISO3WP写保护低有效4VSS地5SI数据输入MOSI6SCK时钟7HOLD保持低有效8VDD电源 2.7~3.6V和 STM32F427ZI 的连接方式CS → 任意 GPIO我常用 PA4方便和 SPI1 的 NSS 引脚复用SO → SPI1_MISOPA6SI → SPI1_MOSIPA7SCK → SPI1_SCKPA5WP → 接 VDD不用写保护功能的话直接拉高HOLD → 接 VDD不用保持功能的话直接拉高VDD → 3.3VVSS → GND这里有个坑WP 和 HOLD 引脚绝对不能悬空。悬空时引脚电平不确定可能随机进入写保护或保持状态导致通信时好时坏。我见过一个项目板子跑了一周都没问题突然某天开始读数据全是 0xFF查了半天发现是 HOLD 引脚虚焊悬空后被外部噪声拉低SPI 通信直接被挂起。所以这两个引脚要么接 VDD要么用 GPIO 控制千万别省事不接。2.2 SPI 初始化CubeMX 配置和手写寄存器两种方式用 CubeMX 配置 SPI1 的参数Mode: Full-Duplex MasterData Size: 8 BitsClock Polarity (CPOL): LowClock Phase (CPHA): 1 EdgeNSS: SoftwareBaud Rate Prescaler: 4180MHz / 4 45MHz略超 40MHz 但实测稳定First Bit: MSB FirstMR25H40CDF 支持 SPI Mode 0CPOL0, CPHA0和 Mode 3CPOL1, CPHA1我一般用 Mode 0因为大部分 Flash 和 MRAM 都默认支持互换性最好。Baud Rate Prescaler 设 4 的话是 45MHz手册标称最高 40MHz但实测 45MHz 也能稳定读写如果你不放心可以设 822.5MHz速度也够用。如果你不用 CubeMX直接操作寄存器// 使能 SPI1 时钟和 GPIOA 时钟 RCC-APB2ENR | RCC_APB2ENR_SPI1EN; RCC-AHB1ENR | RCC_AHB1ENR_GPIOAEN; // PA5/PA6/PA7 复用为 SPI1 GPIOA-MODER ~(GPIO_MODER_MODER5 | GPIO_MODER_MODER6 | GPIO_MODER_MODER7); GPIOA-MODER | (GPIO_MODER_MODER5_1 | GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1); GPIOA-AFR[0] | (5 20) | (5 24) | (5 28); // AF5 SPI1 // PA4 作为软件片选推挽输出 GPIOA-MODER | GPIO_MODER_MODER4_0; GPIOA-OTYPER ~GPIO_OTYPER_OT_4; GPIOA-OSPEEDR | GPIO_OSPEEDER_OSPEEDR4; GPIOA-BSRR GPIO_BSRR_BS_4; // 默认拉高 // SPI1 配置主机、模式0、8位、软件NSS SPI1-CR1 SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_BR_1; SPI1-CR1 | SPI_CR1_SPE;这段代码里SPI_CR1_BR_1对应分频系数 4即 180/445MHz。SPI_CR1_SSM和SPI_CR1_SSI是软件 NSS 模式内部把 NSS 拉高避免模式错误。2.3 MR25H40CDF 的命令集读写状态寄存器是第一步MR25H40CDF 的命令集和标准 SPI Flash 很像但少了擦除相关的命令。核心命令命令编码说明WREN0x06写使能WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据RDID0x9F读设备 ID状态寄存器只有两位有效bit0 是 WEL写使能锁存bit1 是 BP0块保护。上电后 WEL 是 0任何写操作之前必须先发 WREN 命令把 WEL 置 1。写操作完成后 WEL 自动清零。这个机制和 Flash 一样但 MRAM 没有“写忙”状态位因为写入是立即完成的不需要轮询等待。读设备 ID 是个好习惯初始化时读一下确认通信正常uint8_t mr25h40_read_id(void) { uint8_t id; CS_LOW(); spi_transfer(0x9F); id spi_transfer(0x00); CS_HIGH(); return id; }MR25H40CDF 的 RDID 返回 0x18厂商 ID 是 0x18Everspin。如果你读回来是 0x00 或 0xFF说明 SPI 通信有问题先查片选、时钟极性、MISO/MOSI 有没有接反。3. 读写操作完整实现与参数计算3.1 单字节写入从 WREN 到数据落地的完整时序写一个字节到指定地址的流程拉低 CS发送 WREN0x06拉高 CS拉低 CS发送 WRITE0x02发送 24 位地址MR25H40CDF 是 512KB需要 19 位地址但命令格式是 24 位高 5 位忽略发送数据字节拉高 CS代码实现void mr25h40_write_byte(uint32_t addr, uint8_t data) { CS_LOW(); spi_transfer(0x06); // WREN CS_HIGH(); CS_LOW(); spi_transfer(0x02); // WRITE spi_transfer((addr 16) 0xFF); spi_transfer((addr 8) 0xFF); spi_transfer(addr 0xFF); spi_transfer(data); CS_HIGH(); }这里有个细节WREN 和 WRITE 之间必须拉高 CS 再拉低因为 WREN 命令是在 CS 上升沿生效的。如果你在同一个 CS 低电平期间发 WREN 再发 WRITEWEL 位不会置 1写操作会被忽略。这个坑我踩过当时调了半天以为芯片坏了后来看时序图才发现问题。3.2 页写入与地址回卷一次写 256 字节的边界处理MR25H40CDF 支持页写入一页 256 字节。如果你从地址 0x0000FF 开始写 10 个字节写到第 2 个字节时地址会回卷到 0x000000而不是继续到 0x000109。这个行为和 Flash 一样但很多人第一次用会忽略。void mr25h40_write_page(uint32_t addr, uint8_t *buf, uint16_t len) { uint16_t page_offset addr 0xFF; uint16_t write_len 256 - page_offset; if (write_len len) write_len len; CS_LOW(); spi_transfer(0x06); CS_HIGH(); CS_LOW(); spi_transfer(0x02); spi_transfer((addr 16) 0xFF); spi_transfer((addr 8) 0xFF); spi_transfer(addr 0xFF); for (uint16_t i 0; i write_len; i) { spi_transfer(buf[i]); } CS_HIGH(); if (len write_len) { mr25h40_write_page(addr write_len, buf write_len, len - write_len); } }这个递归写法简洁但要注意栈深度如果一次写 512KB递归深度会到 2048 层可能爆栈。实际项目中我改成循环void mr25h40_write(uint32_t addr, uint8_t *buf, uint32_t len) { while (len 0) { uint16_t page_offset addr 0xFF; uint16_t write_len 256 - page_offset; if (write_len len) write_len len; CS_LOW(); spi_transfer(0x06); CS_HIGH(); CS_LOW(); spi_transfer(0x02); spi_transfer((addr 16) 0xFF); spi_transfer((addr 8) 0xFF); spi_transfer(addr 0xFF); for (uint16_t i 0; i write_len; i) { spi_transfer(buf[i]); } CS_HIGH(); addr write_len; buf write_len; len - write_len; } }3.3 连续读取没有页边界限制随便读读操作比写简单得多MR25H40CDF 支持连续读取整个 512KB 空间地址自动递增没有页边界限制。你发一个 READ 命令加起始地址然后一直发时钟就能把数据全部读出来。void mr25h40_read(uint32_t addr, uint8_t *buf, uint32_t len) { CS_LOW(); spi_transfer(0x03); spi_transfer((addr 16) 0xFF); spi_transfer((addr 8) 0xFF); spi_transfer(addr 0xFF); for (uint32_t i 0; i len; i) { buf[i] spi_transfer(0x00); } CS_HIGH(); }读速度受限于 SPI 时钟。40MHz 下每个字节 8 个时钟加上命令和地址开销读 512KB 大约需要 51210248/40e6 ≈ 105ms。如果你用 DMA 传输CPU 占用率可以降到接近 0适合在后台批量读取日志数据。3.4 写入时间计算为什么 MRAM 能做到“零延迟”Flash 写一个字节的实际耗时包括发命令约 1us 页编程等待1~3ms 状态轮询若干 us。MRAM 写一个字节就是发命令和数据40MHz 下 4 字节命令地址 1 字节数据 5 字节 40 个时钟 1us。也就是说MRAM 写一个字节比 Flash 快 1000 倍以上。如果你要保存 100 个字节的关键数据MRAM 耗时约 100us含 WREN 开销Flash 可能需要 3~5ms。在掉电中断里STM32F427ZI 从检测到掉电到电源跌落到 2.7V 以下通常有 1~10ms 的窗口取决于电容容量。用 Flash 你只能保存几个字节用 MRAM 你可以把整个运行状态都存下来。实操心得掉电检测中断里先关全局中断然后直接调mr25h40_write()保存关键结构体最后再复位。不要用 DMA不要用 RTOS 的互斥锁就裸写确保在电源跌落前完成。4. 常见问题排查与避坑经验实录4.1 读回来全是 0xFF 或 0x00怎么一步步定位这是最常见的故障现象排查顺序如下现象可能原因排查方法读回 0xFFMISO 悬空或未接万用表测 MISO 对地电阻正常应有上拉读回 0x00MOSI 和 MISO 接反交换两根线试试读回随机值片选时序不对示波器看 CS 和 SCK 的相位关系读回固定错误值SPI 模式不对试 CPOL0/CPHA0 和 CPOL1/CPHA1偶尔正确偶尔错误电源纹波大示波器看 VDD 纹波加 100nF 和 10uF 电容我遇到过一次读回全是 0xFF查了半天发现是 HOLD 引脚悬空被拉低。还有一次是 SPI 时钟太快PCB 走线太长导致信号反射降到 20MHz 就正常了。所以如果你在 40MHz 下不稳定先降频试试排除信号完整性问题。4.2 写入不生效WEL 位没置 1 的三种情况写操作不生效90% 是 WEL 位没置 1。原因有三种WREN 和 WRITE 之间没有拉高 CS。前面说过WREN 在 CS 上升沿生效如果你在同一个 CS 低电平期间连续发 WREN 和 WRITEWEL 不会置 1。写保护引脚 WP 被拉低。WP 低电平时状态寄存器的 BP0 位可能被硬件置 1阻止写入。检查 WP 是否接 VDD。状态寄存器 BP0 位被置 1。上电默认 BP00但如果你之前写过状态寄存器可能把块保护打开了。读一下状态寄存器确认。uint8_t mr25h40_read_status(void) { uint8_t status; CS_LOW(); spi_transfer(0x05); status spi_transfer(0x00); CS_HIGH(); return status; }正常返回值应该是 0x00 或 0x02WEL 置 1 时。如果 bit1 是 1说明块保护开了需要发 WRSR 命令清除。4.3 SPI 通信不稳定从 PCB 布局到软件时序的全面检查SPI 在 40MHz 下对 PCB 布局有要求。我总结了几条经验SCK 走线尽量短不要有过孔如果必须过孔旁边加地过孔。MISO 和 MOSI 不要平行走太长容易串扰。片选信号线如果超过 10cm加一个 22Ω 串联电阻抑制振铃。电源引脚旁边必须放 100nF 陶瓷电容再并一个 10uF 钽电容。如果板子上有多个 SPI 从机每个从机的片选单独走线不要共用。软件层面如果你用 HAL 库的HAL_SPI_Transmit()注意它的超时参数。默认超时是 1000ms如果 SPI 硬件故障会阻塞 1 秒。建议改成 10ms快速失败。HAL_SPI_Transmit(hspi1, cmd, 1, 10);4.4 掉电保存数据丢失电容容量和写入时机的计算掉电保存能不能成功取决于两个因素电容能撑多久以及你的写入代码跑多快。假设系统 3.3V 供电掉电检测阈值 3.0VMRAM 最低工作电压 2.7V电容从 3.0V 放到 2.7V 的时间t C * (V1 - V2) / I如果系统电流 50mA电容 100uFt 100e-6 * (3.0 - 2.7) / 0.05 0.6ms0.6ms 内你要完成掉电检测、关中断、写 MRAM。MRAM 写 100 字节约 100us加上中断响应和函数调用开销总共约 200us余量充足。但如果你的系统电流是 200mA同样电容只能撑 0.15ms那就危险了。所以掉电保存的电容要根据实际电流算不能拍脑袋。实操心得我一般会在掉电中断里先写一个“掉电标志”到 MRAM 的固定地址然后再写数据。这样即使数据写了一半断电下次上电看到掉电标志就知道上次保存不完整可以触发数据恢复逻辑。4.5 常见问题速查表问题现象解决方法读不到 ID返回 0x00/0xFF检查 CS、SCK、MISO、MOSI 连线写入无效读回旧数据检查 WREN 时序、WP 引脚、BP0 位数据错位读回值偏移检查地址字节顺序大端高速不稳定40MHz 下误码降频到 20MHz 或检查 PCB 走线掉电丢数据重启后数据旧加大电容或优化写入代码偶尔通信失败随机错误检查 HOLD 引脚是否悬空5. 工业场景下的进阶用法与扩展思路5.1 用 MRAM 做环形日志缓冲区无磨损均衡的极简实现Flash 做日志需要磨损均衡因为每个扇区擦写次数有限。MRAM 不需要你可以直接用一个环形缓冲区写满一圈就覆盖最旧的数据。#define LOG_BASE_ADDR 0x10000 #define LOG_SIZE 0x10000 // 64KB 日志区 #define LOG_ENTRY_SIZE 32 typedef struct { uint32_t timestamp; uint16_t event_id; uint8_t data[26]; } log_entry_t; static uint32_t log_write_ptr 0; void log_write(log_entry_t *entry) { uint32_t addr LOG_BASE_ADDR log_write_ptr * LOG_ENTRY_SIZE; if (addr LOG_ENTRY_SIZE LOG_BASE_ADDR LOG_SIZE) { log_write_ptr 0; addr LOG_BASE_ADDR; } mr25h40_write(addr, (uint8_t*)entry, LOG_ENTRY_SIZE); log_write_ptr; }这个方案简单粗暴但非常可靠。64KB 日志区每条 32 字节可以存 2048 条记录。按每秒写一条算34 分钟覆盖一轮。如果你需要保留更久的历史可以扩大日志区或者降低写入频率。5.2 参数存储的双备份策略防止写一半掉电虽然 MRAM 写入是原子的SPI 帧内不会被打断但如果你写一个多字节结构体SPI 传输过程中掉电可能只写了一半。双备份策略可以解决这个问题typedef struct { uint32_t magic; uint32_t version; uint8_t params[56]; uint32_t crc; } param_block_t; #define PARAM_A_ADDR 0x00000 #define PARAM_B_ADDR 0x00100 void param_save(param_block_t *p) { p-magic 0x50415241; // PARA p-version; p-crc crc32(p, sizeof(param_block_t) - 4); // 交替写入 A 和 B static uint8_t current 0; uint32_t addr current ? PARAM_B_ADDR : PARAM_A_ADDR; mr25h40_write(addr, (uint8_t*)p, sizeof(param_block_t)); current !current; } uint8_t param_load(param_block_t *p) { param_block_t a, b; mr25h40_read(PARAM_A_ADDR, (uint8_t*)a, sizeof(a)); mr25h40_read(PARAM_B_ADDR, (uint8_t*)b, sizeof(b)); uint8_t a_valid (a.magic 0x50415241) (crc32(a, sizeof(a)-4) a.crc); uint8_t b_valid (b.magic 0x50415241) (crc32(b, sizeof(b)-4) b.crc); if (a_valid b_valid) { *p (a.version b.version) ? a : b; return 1; } else if (a_valid) { *p a; return 1; } else if (b_valid) { *p b; return 1; } return 0; }这个策略的核心是每次保存都写到不同的块读的时候取版本号大的有效块。即使写 B 的时候掉电A 还是完整的下次上电读 A 就行。5.3 与 RTOS 配合在任务中安全读写 MRAM如果你用 FreeRTOS 或 RT-ThreadSPI 总线是共享资源多个任务同时读写会冲突。解决方案有两种互斥锁每个任务读写前获取 SPI 互斥锁读写完释放。简单但可能阻塞。专用任务创建一个 MRAM 任务其他任务通过队列发送读写请求。这个方案实时性更好但代码复杂。我一般用互斥锁因为 MRAM 读写很快阻塞时间短SemaphoreHandle_t spi_mutex; void mram_task_write(uint32_t addr, uint8_t *buf, uint32_t len) { xSemaphoreTake(spi_mutex, portMAX_DELAY); mr25h40_write(addr, buf, len); xSemaphoreGive(spi_mutex); }注意掉电中断里不能用互斥锁因为中断里不能阻塞。掉电保存要用裸写并且确保此时没有其他任务在占用 SPI。我通常会在掉电中断里先关调度器再直接操作 SPI 寄存器。5.4 性能实测40MHz SPI 下读写 512KB 的真实耗时我用 STM32F427ZI 在 180MHz 主频、SPI1 45MHz 时钟下实测操作数据量耗时平均速度连续读512KB98ms5.2MB/s连续写512KB112ms4.6MB/s单字节写1B1.2us-读设备 ID1B0.8us-写入比读取慢一点因为每次写都要发 WREN 命令。如果你要批量写可以每页发一次 WREN而不是每个字节发一次。MR25H40CDF 的 WREN 在 CS 上升沿生效后WEL 位会保持到下一次写操作完成所以你可以连续写多页只要在每页写之前发一次 WREN。void mr25h40_write_fast(uint32_t addr, uint8_t *buf, uint32_t len) { while (len 0) { uint16_t page_offset addr 0xFF; uint16_t write_len 256 - page_offset; if (write_len len) write_len len; CS_LOW(); spi_transfer(0x06); // WREN CS_HIGH(); CS_LOW(); spi_transfer(0x02); spi_transfer((addr 16) 0xFF); spi_transfer((addr 8) 0xFF); spi_transfer(addr 0xFF); for (uint16_t i 0; i write_len; i) { spi_transfer(buf[i]); } CS_HIGH(); addr write_len; buf write_len; len - write_len; } }这个版本和之前的区别是 WREN 在每页写之前发一次而不是每个字节发一次。实测写 512KB 耗时从 112ms 降到 105ms提升不大因为 WREN 本身只占 1 个字节的时间。但如果你写小数据块比如每次写 4 字节那 WREN 的开销就占比很大了。6. 写在最后一些零散但值钱的经验MR25H40CDF 和 STM32F427ZI 这套组合我从 2019 年用到现在踩过的坑基本都写在上面的章节里了。最后再补几个零散的点第一MRAM 不是万能的。它的容量密度比 Flash 低价格比 Flash 高如果你的项目只需要存配置参数、写入频率很低用内部 Flash 模拟 EEPROM 就够了没必要多花这个钱。MRAM 的价值在于高频写入和掉电安全只有这两个需求同时存在时它才是最优解。第二SPI 时钟不是越高越好。40MHz 是手册标称上限但实际能不能跑稳取决于你的 PCB 布局、线长、电源质量。我一般先在 20MHz 下调通功能再逐步提高到 40MHz如果出现误码就降回去。稳定比速度重要。第三掉电保存的电容要实测。理论计算只能参考实际电容有容差、ESR、温度特性最好用示波器抓一下掉电波形确认从掉电检测到电源跌落到 2.7V 之间的时间窗口然后在这个窗口内跑你的保存代码看能不能完成。第四状态寄存器没事别乱写。BP0 位一旦置 1整个芯片都写不进去而且上电不会自动清除。如果你不小心写了 WRSR 把 BP0 置 1只能再发一次 WRSR 清除但前提是 WEL 置 1 且 WP 为高。这个坑我踩过一次当时以为芯片锁死了差点换芯片。第五读设备 ID 应该作为初始化的一部分。每次上电读一次 ID确认通信正常再继续。如果 ID 不对直接进入错误处理不要继续跑业务逻辑否则可能把错误数据写到 MRAM 里造成更麻烦的问题。这套方案我后来还扩展到了数据记录仪产品上用 MRAM 存运行日志和故障快照配合 RTC 时间戳可以回溯最近一个月的运行状态。客户反馈说以前用 Flash 方案设备运行半年后经常出现日志丢失换了 MRAM 之后跑了两年没出过问题。所以如果你也在做类似的产品这个方向值得投入时间研究。
返回列表