1. 项目背景与硬件选型解析在嵌入式系统开发中非易失性存储解决方案的选择往往决定了产品的可靠性和用户体验。M95M04 EEPROM与STM32F405RG微控制器的组合为存储用户偏好、日程设置和自定义配置提供了工业级的硬件基础。M95M04是STMicroelectronics推出的512Kbit SPI接口EEPROM具有以下关键特性工作电压范围1.8V至5.5V最大时钟频率20MHz页编程时间5ms典型值数据保存期限200年擦写次数400万次STM32F405RG则是基于ARM Cortex-M4内核的高性能微控制器其存储接口特性包括168MHz主频210DMIPS性能1MB Flash 192KB SRAM3个SPI接口支持最高42MHz硬件CRC计算单元这对组合的独特优势在于电气兼容性M95M04的宽电压范围与STM32的I/O电平完美匹配性能匹配STM32的SPI接口可充分发挥EEPROM的20MHz通信速率可靠性保障硬件CRC校验与EEPROM的写保护机制形成双重数据保护实际项目中我曾遇到因SPI时钟相位配置错误导致的写入失败。后来发现M95M04要求CPOL0且CPHA0的SPI模式这个细节在数据手册第23页有明确说明。2. 硬件电路设计与接口配置2.1 最小系统连接方案M95M04与STM32F405RG的标准连接方式如下M95M04引脚STM32F405RG引脚功能说明CSPA4片选信号SCKPA5时钟信号MISOPA6主入从出MOSIPA7主出从入WPPA1写保护HOLDPB13暂停控制VCC3.3V电源GNDGND地线2.2 关键电路设计要点上拉电阻配置CS引脚需接4.7kΩ上拉电阻WP和HOLD引脚建议接10kΩ上拉电源滤波VCC引脚就近放置0.1μF陶瓷电容建议增加10μF钽电容作为储能电容信号完整性SPI走线长度控制在10cm以内避免与高频信号线平行走线// SPI初始化代码示例 void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; SPI_HandleTypeDef hspi1 {0}; __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // SCK/MISO/MOSI引脚配置 GPIO_InitStruct.Pin GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // CS引脚配置 GPIO_InitStruct.Pin GPIO_PIN_4; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // SPI参数配置 hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_4; // 42MHz/410.5MHz hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; HAL_SPI_Init(hspi1); }3. 存储数据结构设计与实现3.1 用户配置存储方案针对用户偏好、日程设置和自定义配置建议采用以下存储结构0x0000 - 0x0FFF: 系统保留区存储硬件参数、校准数据等 0x1000 - 0x2FFF: 用户偏好区 0x3000 - 0x4FFF: 日程设置区 0x5000 - 0x7FFF: 自定义配置区每个区域采用相同的管理结构前16字节配置头含CRC32校验和后续空间实际配置数据3.2 数据存取API实现#define USER_PREF_START_ADDR 0x1000 #define SCHEDULE_START_ADDR 0x3000 #define CUSTOM_CFG_START_ADDR 0x5000 typedef struct { uint32_t crc; uint32_t timestamp; uint16_t data_len; uint8_t data_type; uint8_t reserved[5]; } ConfigHeader; uint8_t EEPROM_WriteConfig(uint32_t base_addr, void *data, uint16_t len) { ConfigHeader header; uint32_t crc HAL_CRC_Calculate(hcrc, (uint32_t*)data, len/4); header.crc crc; header.timestamp HAL_GetTick(); header.data_len len; header.data_type 0x01; // 用户数据类型 // 先写数据区 if(EEPROM_Write(base_addrsizeof(ConfigHeader), data, len) ! EEPROM_OK) return 1; // 再写头信息 if(EEPROM_Write(base_addr, header, sizeof(ConfigHeader)) ! EEPROM_OK) return 2; return 0; } uint8_t EEPROM_ReadConfig(uint32_t base_addr, void *buf, uint16_t buf_size) { ConfigHeader header; uint32_t crc; // 读取头信息 if(EEPROM_Read(base_addr, header, sizeof(ConfigHeader)) ! EEPROM_OK) return 1; // 检查缓冲区大小 if(header.data_len buf_size) return 2; // 读取数据 if(EEPROM_Read(base_addrsizeof(ConfigHeader), buf, header.data_len) ! EEPROM_OK) return 3; // 校验CRC crc HAL_CRC_Calculate(hcrc, (uint32_t*)buf, header.data_len/4); if(crc ! header.crc) return 4; return 0; }4. 系统集成与性能优化4.1 存储访问加速策略内存缓存机制typedef struct { uint8_t dirty; // 数据是否被修改 uint32_t last_access;// 最后访问时间 uint32_t base_addr; // EEPROM基地址 uint8_t data[64]; // 缓存数据 } EEBlockCache; EEBlockCache cache_pool[8]; // 8个缓存块 uint8_t EEPROM_CachedRead(uint32_t addr, void *buf, uint16_t len) { uint32_t block_num addr / 64; uint32_t block_offset addr % 64; // 查找缓存 for(int i0; i8; i) { if(cache_pool[i].base_addr block_num*64) { memcpy(buf, cache_pool[i].data[block_offset], len); cache_pool[i].last_access HAL_GetTick(); return 0; } } // 缓存未命中 int lru_index 0; uint32_t lru_time cache_pool[0].last_access; for(int i1; i8; i) { if(cache_pool[i].last_access lru_time) { lru_index i; lru_time cache_pool[i].last_access; } } // 写回脏数据 if(cache_pool[lru_index].dirty) { EEPROM_Write(cache_pool[lru_index].base_addr, cache_pool[lru_index].data, 64); } // 读取新数据 if(EEPROM_Read(block_num*64, cache_pool[lru_index].data, 64) ! EEPROM_OK) return 1; cache_pool[lru_index].base_addr block_num*64; cache_pool[lru_index].last_access HAL_GetTick(); cache_pool[lru_index].dirty 0; memcpy(buf, cache_pool[lru_index].data[block_offset], len); return 0; }批量写入优化void EEPROM_WriteBatch(uint32_t addr, uint8_t *data, uint16_t len) { uint16_t chunk_size; uint32_t end_addr addr len; while(addr end_addr) { // 计算当前页剩余空间 uint32_t page_end ((addr / 128) 1) * 128; chunk_size (end_addr page_end) ? (end_addr - addr) : (page_end - addr); // 启用写使能 EEPROM_WriteEnable(); // 发送写命令 HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_RESET); uint8_t cmd[3] {0x02, (addr 8) 0xFF, addr 0xFF}; HAL_SPI_Transmit(hspi1, cmd, 3, HAL_MAX_DELAY); HAL_SPI_Transmit(hspi1, data, chunk_size, HAL_MAX_DELAY); HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_SET); // 等待写入完成 while(EEPROM_IsBusy()); addr chunk_size; data chunk_size; } }4.2 低功耗设计考虑动态时钟调整void EEPROM_SetLowPowerMode(uint8_t enable) { if(enable) { // 降低SPI时钟频率至1MHz hspi1.Instance-CR1 ~SPI_BAUDRATEPRESCALER_256; hspi1.Instance-CR1 | SPI_BAUDRATEPRESCALER_32; // 禁用不需要的外设 __HAL_RCC_CRC_CLK_DISABLE(); } else { // 恢复全速模式 hspi1.Instance-CR1 ~SPI_BAUDRATEPRESCALER_256; hspi1.Instance-CR1 | SPI_BAUDRATEPRESCALER_4; // 重新启用外设 __HAL_RCC_CRC_CLK_ENABLE(); } }智能唤醒机制void EEPROM_EnterSleepMode(void) { // 发送深度休眠命令 HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_RESET); uint8_t cmd 0xB9; HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_SET); } void EEPROM_WakeUp(void) { // 通过CS引脚唤醒 HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_SET); HAL_Delay(1); }5. 故障处理与数据保护5.1 错误检测与恢复多重校验机制uint8_t EEPROM_VerifyData(uint32_t addr, void *data, uint16_t len) { uint8_t read_buf[256]; uint8_t retry 3; while(retry--) { if(EEPROM_Read(addr, read_buf, len) ! EEPROM_OK) continue; if(memcmp(data, read_buf, len) 0) return 0; // 数据不一致尝试重写 EEPROM_Write(addr, data, len); } return 1; // 验证失败 }坏块管理#define BAD_BLOCK_MARKER 0xFFFF uint8_t EEPROM_CheckBlock(uint32_t block_addr) { uint16_t marker; EEPROM_Read(block_addr, marker, 2); return (marker BAD_BLOCK_MARKER) ? 1 : 0; } void EEPROM_MarkBadBlock(uint32_t block_addr) { uint16_t marker BAD_BLOCK_MARKER; EEPROM_Write(block_addr, marker, 2); }5.2 数据备份策略双存储区轮换#define CONFIG_AREA_A 0x1000 #define CONFIG_AREA_B 0x3000 uint8_t LoadUserConfig(void *buf) { ConfigHeader headerA, headerB; // 读取两个区域的头部信息 EEPROM_Read(CONFIG_AREA_A, headerA, sizeof(ConfigHeader)); EEPROM_Read(CONFIG_AREA_B, headerB, sizeof(ConfigHeader)); // 选择较新的有效配置 if(headerA.timestamp headerB.timestamp) { if(EEPROM_ReadConfig(CONFIG_AREA_A, buf, 512) 0) return 0; if(EEPROM_ReadConfig(CONFIG_AREA_B, buf, 512) 0) return 0; } else { if(EEPROM_ReadConfig(CONFIG_AREA_B, buf, 512) 0) return 0; if(EEPROM_ReadConfig(CONFIG_AREA_A, buf, 512) 0) return 0; } return 1; // 两个区域都损坏 } void SaveUserConfig(void *data, uint16_t len) { static uint8_t current_area 0; uint32_t target_addr (current_area 0) ? CONFIG_AREA_A : CONFIG_AREA_B; if(EEPROM_WriteConfig(target_addr, data, len) 0) { current_area !current_area; // 切换存储区 } }关键数据三重备份void WriteCriticalData(uint32_t addr, void *data, uint16_t len) { uint32_t primary_addr addr; uint32_t secondary_addr addr 0x1000; uint32_t tertiary_addr addr 0x2000; // 主存储 EEPROM_Write(primary_addr, data, len); // 延时后写入第二备份 HAL_Delay(50); EEPROM_Write(secondary_addr, data, len); // 系统空闲时写入第三备份 while(1) { if(xTaskGetTickCount() % 1000 0) { // 每秒检查一次 if(uxTaskGetNumberOfTasks() 5) { // 系统空闲时 EEPROM_Write(tertiary_addr, data, len); break; } } osDelay(100); } }
STM32F405RG与M95M04 EEPROM嵌入式存储方案详解
发布时间:2026/7/5 7:27:38
1. 项目背景与硬件选型解析在嵌入式系统开发中非易失性存储解决方案的选择往往决定了产品的可靠性和用户体验。M95M04 EEPROM与STM32F405RG微控制器的组合为存储用户偏好、日程设置和自定义配置提供了工业级的硬件基础。M95M04是STMicroelectronics推出的512Kbit SPI接口EEPROM具有以下关键特性工作电压范围1.8V至5.5V最大时钟频率20MHz页编程时间5ms典型值数据保存期限200年擦写次数400万次STM32F405RG则是基于ARM Cortex-M4内核的高性能微控制器其存储接口特性包括168MHz主频210DMIPS性能1MB Flash 192KB SRAM3个SPI接口支持最高42MHz硬件CRC计算单元这对组合的独特优势在于电气兼容性M95M04的宽电压范围与STM32的I/O电平完美匹配性能匹配STM32的SPI接口可充分发挥EEPROM的20MHz通信速率可靠性保障硬件CRC校验与EEPROM的写保护机制形成双重数据保护实际项目中我曾遇到因SPI时钟相位配置错误导致的写入失败。后来发现M95M04要求CPOL0且CPHA0的SPI模式这个细节在数据手册第23页有明确说明。2. 硬件电路设计与接口配置2.1 最小系统连接方案M95M04与STM32F405RG的标准连接方式如下M95M04引脚STM32F405RG引脚功能说明CSPA4片选信号SCKPA5时钟信号MISOPA6主入从出MOSIPA7主出从入WPPA1写保护HOLDPB13暂停控制VCC3.3V电源GNDGND地线2.2 关键电路设计要点上拉电阻配置CS引脚需接4.7kΩ上拉电阻WP和HOLD引脚建议接10kΩ上拉电源滤波VCC引脚就近放置0.1μF陶瓷电容建议增加10μF钽电容作为储能电容信号完整性SPI走线长度控制在10cm以内避免与高频信号线平行走线// SPI初始化代码示例 void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; SPI_HandleTypeDef hspi1 {0}; __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // SCK/MISO/MOSI引脚配置 GPIO_InitStruct.Pin GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // CS引脚配置 GPIO_InitStruct.Pin GPIO_PIN_4; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // SPI参数配置 hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_4; // 42MHz/410.5MHz hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; HAL_SPI_Init(hspi1); }3. 存储数据结构设计与实现3.1 用户配置存储方案针对用户偏好、日程设置和自定义配置建议采用以下存储结构0x0000 - 0x0FFF: 系统保留区存储硬件参数、校准数据等 0x1000 - 0x2FFF: 用户偏好区 0x3000 - 0x4FFF: 日程设置区 0x5000 - 0x7FFF: 自定义配置区每个区域采用相同的管理结构前16字节配置头含CRC32校验和后续空间实际配置数据3.2 数据存取API实现#define USER_PREF_START_ADDR 0x1000 #define SCHEDULE_START_ADDR 0x3000 #define CUSTOM_CFG_START_ADDR 0x5000 typedef struct { uint32_t crc; uint32_t timestamp; uint16_t data_len; uint8_t data_type; uint8_t reserved[5]; } ConfigHeader; uint8_t EEPROM_WriteConfig(uint32_t base_addr, void *data, uint16_t len) { ConfigHeader header; uint32_t crc HAL_CRC_Calculate(hcrc, (uint32_t*)data, len/4); header.crc crc; header.timestamp HAL_GetTick(); header.data_len len; header.data_type 0x01; // 用户数据类型 // 先写数据区 if(EEPROM_Write(base_addrsizeof(ConfigHeader), data, len) ! EEPROM_OK) return 1; // 再写头信息 if(EEPROM_Write(base_addr, header, sizeof(ConfigHeader)) ! EEPROM_OK) return 2; return 0; } uint8_t EEPROM_ReadConfig(uint32_t base_addr, void *buf, uint16_t buf_size) { ConfigHeader header; uint32_t crc; // 读取头信息 if(EEPROM_Read(base_addr, header, sizeof(ConfigHeader)) ! EEPROM_OK) return 1; // 检查缓冲区大小 if(header.data_len buf_size) return 2; // 读取数据 if(EEPROM_Read(base_addrsizeof(ConfigHeader), buf, header.data_len) ! EEPROM_OK) return 3; // 校验CRC crc HAL_CRC_Calculate(hcrc, (uint32_t*)buf, header.data_len/4); if(crc ! header.crc) return 4; return 0; }4. 系统集成与性能优化4.1 存储访问加速策略内存缓存机制typedef struct { uint8_t dirty; // 数据是否被修改 uint32_t last_access;// 最后访问时间 uint32_t base_addr; // EEPROM基地址 uint8_t data[64]; // 缓存数据 } EEBlockCache; EEBlockCache cache_pool[8]; // 8个缓存块 uint8_t EEPROM_CachedRead(uint32_t addr, void *buf, uint16_t len) { uint32_t block_num addr / 64; uint32_t block_offset addr % 64; // 查找缓存 for(int i0; i8; i) { if(cache_pool[i].base_addr block_num*64) { memcpy(buf, cache_pool[i].data[block_offset], len); cache_pool[i].last_access HAL_GetTick(); return 0; } } // 缓存未命中 int lru_index 0; uint32_t lru_time cache_pool[0].last_access; for(int i1; i8; i) { if(cache_pool[i].last_access lru_time) { lru_index i; lru_time cache_pool[i].last_access; } } // 写回脏数据 if(cache_pool[lru_index].dirty) { EEPROM_Write(cache_pool[lru_index].base_addr, cache_pool[lru_index].data, 64); } // 读取新数据 if(EEPROM_Read(block_num*64, cache_pool[lru_index].data, 64) ! EEPROM_OK) return 1; cache_pool[lru_index].base_addr block_num*64; cache_pool[lru_index].last_access HAL_GetTick(); cache_pool[lru_index].dirty 0; memcpy(buf, cache_pool[lru_index].data[block_offset], len); return 0; }批量写入优化void EEPROM_WriteBatch(uint32_t addr, uint8_t *data, uint16_t len) { uint16_t chunk_size; uint32_t end_addr addr len; while(addr end_addr) { // 计算当前页剩余空间 uint32_t page_end ((addr / 128) 1) * 128; chunk_size (end_addr page_end) ? (end_addr - addr) : (page_end - addr); // 启用写使能 EEPROM_WriteEnable(); // 发送写命令 HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_RESET); uint8_t cmd[3] {0x02, (addr 8) 0xFF, addr 0xFF}; HAL_SPI_Transmit(hspi1, cmd, 3, HAL_MAX_DELAY); HAL_SPI_Transmit(hspi1, data, chunk_size, HAL_MAX_DELAY); HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_SET); // 等待写入完成 while(EEPROM_IsBusy()); addr chunk_size; data chunk_size; } }4.2 低功耗设计考虑动态时钟调整void EEPROM_SetLowPowerMode(uint8_t enable) { if(enable) { // 降低SPI时钟频率至1MHz hspi1.Instance-CR1 ~SPI_BAUDRATEPRESCALER_256; hspi1.Instance-CR1 | SPI_BAUDRATEPRESCALER_32; // 禁用不需要的外设 __HAL_RCC_CRC_CLK_DISABLE(); } else { // 恢复全速模式 hspi1.Instance-CR1 ~SPI_BAUDRATEPRESCALER_256; hspi1.Instance-CR1 | SPI_BAUDRATEPRESCALER_4; // 重新启用外设 __HAL_RCC_CRC_CLK_ENABLE(); } }智能唤醒机制void EEPROM_EnterSleepMode(void) { // 发送深度休眠命令 HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_RESET); uint8_t cmd 0xB9; HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_SET); } void EEPROM_WakeUp(void) { // 通过CS引脚唤醒 HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_SET); HAL_Delay(1); }5. 故障处理与数据保护5.1 错误检测与恢复多重校验机制uint8_t EEPROM_VerifyData(uint32_t addr, void *data, uint16_t len) { uint8_t read_buf[256]; uint8_t retry 3; while(retry--) { if(EEPROM_Read(addr, read_buf, len) ! EEPROM_OK) continue; if(memcmp(data, read_buf, len) 0) return 0; // 数据不一致尝试重写 EEPROM_Write(addr, data, len); } return 1; // 验证失败 }坏块管理#define BAD_BLOCK_MARKER 0xFFFF uint8_t EEPROM_CheckBlock(uint32_t block_addr) { uint16_t marker; EEPROM_Read(block_addr, marker, 2); return (marker BAD_BLOCK_MARKER) ? 1 : 0; } void EEPROM_MarkBadBlock(uint32_t block_addr) { uint16_t marker BAD_BLOCK_MARKER; EEPROM_Write(block_addr, marker, 2); }5.2 数据备份策略双存储区轮换#define CONFIG_AREA_A 0x1000 #define CONFIG_AREA_B 0x3000 uint8_t LoadUserConfig(void *buf) { ConfigHeader headerA, headerB; // 读取两个区域的头部信息 EEPROM_Read(CONFIG_AREA_A, headerA, sizeof(ConfigHeader)); EEPROM_Read(CONFIG_AREA_B, headerB, sizeof(ConfigHeader)); // 选择较新的有效配置 if(headerA.timestamp headerB.timestamp) { if(EEPROM_ReadConfig(CONFIG_AREA_A, buf, 512) 0) return 0; if(EEPROM_ReadConfig(CONFIG_AREA_B, buf, 512) 0) return 0; } else { if(EEPROM_ReadConfig(CONFIG_AREA_B, buf, 512) 0) return 0; if(EEPROM_ReadConfig(CONFIG_AREA_A, buf, 512) 0) return 0; } return 1; // 两个区域都损坏 } void SaveUserConfig(void *data, uint16_t len) { static uint8_t current_area 0; uint32_t target_addr (current_area 0) ? CONFIG_AREA_A : CONFIG_AREA_B; if(EEPROM_WriteConfig(target_addr, data, len) 0) { current_area !current_area; // 切换存储区 } }关键数据三重备份void WriteCriticalData(uint32_t addr, void *data, uint16_t len) { uint32_t primary_addr addr; uint32_t secondary_addr addr 0x1000; uint32_t tertiary_addr addr 0x2000; // 主存储 EEPROM_Write(primary_addr, data, len); // 延时后写入第二备份 HAL_Delay(50); EEPROM_Write(secondary_addr, data, len); // 系统空闲时写入第三备份 while(1) { if(xTaskGetTickCount() % 1000 0) { // 每秒检查一次 if(uxTaskGetNumberOfTasks() 5) { // 系统空闲时 EEPROM_Write(tertiary_addr, data, len); break; } } osDelay(100); } }