通过定义#define FLS_STORAGE_RAM_MIRROR ((uint32_t *)(&JM_Configs)),JM_Configs存储在RAM中,向片上flash中将其数据存储或读取,长度为#define FLS_STORAGE_LENGTH (sizeof(JM_Config_t)),flash起始地址为USER_EEPROM_FLASH_START_ADDR, 所使用的函数为E2P_ATTR void nor_flash_erase(nor_flash_config_t *cfg, uint32_t start_addr, uint32_t size) { uint32_t sector_size = cfg->sector_size; for (uint32_t i = 0; i < size / sector_size; i++) { nor_flash_erase_sector(cfg, start_addr + i * sector_size); } } E2P_ATTR hpm_stat_t nor_flash_read(nor_flash_config_t *cfg, uint8_t *buf, uint32_t addr, uint32_t size) { uint32_t aligned_start = HPM_L1C_CACHELINE_ALIGN_DOWN(addr); uint32_t aligned_end = HPM_L1C_CACHELINE_ALIGN_UP(addr + size); uint32_t aligned_size = aligned_end - aligned_start; (void)cfg; l1c_dc_invalidate(aligned_start, aligned_size); memcpy(buf, (void *)addr, size); return status_success; } E2P_ATTR hpm_stat_t nor_flash_write(nor_flash_config_t *cfg, uint8_t *buf, uint32_t addr, uint32_t size) { addr > cfg->base_addr ? (addr -= cfg->base_addr) : addr; hpm_stat_t status = rom_xpi_nor_program(cfg->xpi_base, xpi_xfer_channel_auto, \ &cfg->nor_config, (const uint32_t *)buf, addr, size); return status; } 逻辑部分代码要怎么写
时间: 2025-07-19 09:31:45 浏览: 4
<think>我们有一个结构体 JM_Configs 存储在RAM中,需要将其保存到片上Flash(使用nor_flash_erase, nor_flash_read, nor_flash_write函数)。同时,我们也需要从Flash中读取数据到RAM的JM_Configs结构体。
步骤:
1. 确定Flash中存储数据的地址和大小。
2. 写入操作:
a. 擦除目标扇区(因为Flash写入前必须先擦除,且擦除单位是扇区)。
b. 将JM_Configs结构体数据写入到Flash的指定地址。
3. 读取操作:
a. 从Flash的指定地址读取数据到JM_Configs结构体。
注意事项:
- 擦除操作会擦除整个扇区,所以需要确保该扇区没有其他重要数据,或者先备份再擦除。
- 写入的数据大小必须是Flash编程对齐大小的倍数(通常是1字节、2字节、4字节等,具体看芯片手册)。
- 由于Flash写入只能将1变为0,不能将0变为1,所以擦除(将整个扇区变为全1)是写入的必要前提。
假设:
- 我们有一个预定义的Flash地址:`#define JM_CONFIG_FLASH_ADDR 0x0800C000`
- JM_Configs结构体的大小为`sizeof(JM_Configs)`,我们假设为`JM_CONFIG_SIZE`。
- Flash的一个扇区大小是`JM_FLASH_SECTOR_SIZE`(例如4096字节),我们需要确保擦除的扇区覆盖要写入的地址范围。
实现步骤:
1. 定义Flash操作的地址和结构体大小:
```c
#include <stdint.h>
#include <string.h> // for memcpy
#define JM_CONFIG_FLASH_ADDR 0x0800C000
#define JM_CONFIG_SIZE sizeof(JM_Configs)
#define JM_FLASH_SECTOR_SIZE 4096 // 假设扇区大小为4KB,具体根据芯片手册
// 假设nor_flash_erase, nor_flash_read, nor_flash_write函数的原型如下:
// 擦除从指定地址开始的整个扇区,返回0表示成功,非0失败
int nor_flash_erase(uint32_t addr);
// 从Flash地址addr读取len字节到buf
int nor_flash_read(uint32_t addr, uint8_t *buf, size_t len);
// 将buf中的len字节写入到Flash地址addr(注意:写入前必须确保该区域已被擦除)
int nor_flash_write(uint32_t addr, const uint8_t *buf, size_t len);
```
2. 写入Flash函数:
```c
int save_config_to_flash(JM_Configs *config) {
// 擦除目标扇区(注意:擦除操作会擦除整个扇区,所以地址要对齐到扇区起始地址)
uint32_t sector_start = JM_CONFIG_FLASH_ADDR;
int ret = nor_flash_erase(sector_start);
if (ret != 0) {
// 擦除失败
return -1;
}
// 将结构体转换为字节流(因为nor_flash_write需要uint8_t指针)
// 注意:这里直接使用结构体的内存表示,如果结构体有填充字节或对齐问题,需要谨慎。
// 确保结构体是紧密排列的(可以使用编译器指令如__packed__)或者使用序列化方法。
uint8_t *data = (uint8_t*)config;
// 写入数据
ret = nor_flash_write(JM_CONFIG_FLASH_ADDR, data, JM_CONFIG_SIZE);
if (ret != 0) {
return -2;
}
return 0; // 成功
}
```
3. 读取Flash函数:
```c
int load_config_from_flash(JM_Configs *config) {
// 从Flash读取数据到结构体
uint8_t *data = (uint8_t*)config;
int ret = nor_flash_read(JM_CONFIG_FLASH_ADDR, data, JM_CONFIG_SIZE);
if (ret != 0) {
return -1;
}
return 0;
}
```
4. 注意事项:
- 结构体对齐:如果结构体不是1字节对齐(即存在填充字节),则直接写入和读取可能会因为填充字节的内容不一致而导致问题。建议使用编译器指令将结构体设置为1字节对齐(例如GCC的`__attribute__((packed))`)。
- 数据校验:写入后可以读取出来进行校验,或者加入CRC校验码。
- 跨扇区:如果结构体大小超过一个扇区,或者跨扇区,则需要擦除多个扇区。
改进版(增加CRC校验和结构体打包):
在结构体定义时,我们可以使用打包属性(以GCC为例):
```c
typedef struct __attribute__((packed)) {
// 结构体成员
} JM_Configs;
```
增加CRC校验:
```c
// 在结构体中加入一个CRC成员,或者在结构体后面附加一个CRC值
typedef struct __attribute__((packed)) {
// 配置项
// ...
uint32_t crc; // 放在结构体末尾
} JM_Configs;
// 计算CRC的函数(假设已有)
uint32_t calculate_crc(JM_Configs *config, size_t len_without_crc) {
// 计算从结构体开始到crc之前的CRC
// ...
}
// 保存时计算CRC并写入
int save_config_to_flash(JM_Configs *config) {
// 先计算CRC(假设结构体中crc成员前面的大小为整个结构体大小减去4)
size_t len_without_crc = sizeof(JM_Configs) - sizeof(uint32_t);
config->crc = calculate_crc(config, len_without_crc);
// 然后擦除和写入
// ...
}
// 加载时校验CRC
int load_config_from_flash(JM_Configs *config) {
// 读取
if (nor_flash_read(JM_CONFIG_FLASH_ADDR, (uint8_t*)config, JM_CONFIG_SIZE) != 0) {
return -1;
}
size_t len_without_crc = sizeof(JM_Configs) - sizeof(uint32_t);
uint32_t crc_calculated = calculate_crc(config, len_without_crc);
if (crc_calculated != config->crc) {
return -2; // CRC校验失败
}
return 0;
}
```
5. 如果写入的数据大小不是Flash编程对齐大小的倍数,可能需要分多次写入,每次写入对齐大小的数据。但通常这些函数内部会处理,只要地址和长度满足要求(例如,每次写入4字节对齐的地址和长度)。具体看nor_flash_write函数的要求。
6. 如果Flash驱动要求写入的地址必须对齐到某个边界(如4字节),则需要在写入函数中确保。如果结构体大小不是对齐大小的整数倍,可能需要填充至对齐大小。
7. 另外,如果Flash的写入操作有页限制(比如不能跨页写入),则需要注意。但通常nor_flash_write函数内部会处理分页写入,我们只需要保证传入的地址和长度在同一个擦除过的区域内。
总结代码:
我们假设Flash驱动函数已经处理了对齐和分页,我们只需要确保擦除整个扇区,然后写入数据即可。同时,我们使用CRC校验来确保数据的完整性。
注意:在实际操作中,为了避免在保存过程中掉电导致原有数据丢失,可以采用双备份(两个扇区交替存储)等方法,但这里只实现基本功能。
因此,完整的写入和读取流程如上所示。
如果用户需要,还可以提供更健壮的存储方案(如双扇区备份、原子操作等),但根据问题,我们只使用基本函数实现。
相关问题:
1. 如何计算结构体的CRC校验码?
2. 在Flash存储中,如何实现双备份(或更多备份)来防止写入过程中掉电导致的数据损坏?
3. 如果Flash的扇区大小远大于结构体大小,如何优化存储空间?
4. 如何确保结构体在不同编译环境下的内存布局一致(避免填充字节问题)?</think>### 将 JM_Configs 数据存储到片上 Flash 的实现方案
#### **1. 核心流程设计**
```mermaid
graph TD
A[RAM中的 JM_Configs] --> B[序列化为字节流]
B --> C[擦除目标 Flash 扇区]
C --> D[写入 Flash]
D --> E[读取验证]
E --> F[反序列化到 RAM]
```
#### **2. 关键代码实现**
##### **2.1 结构体定义与序列化**
```c
#include <stdint.h>
#include <string.h>
// 配置结构体定义
typedef struct __attribute__((packed)) {
uint32_t device_id;
uint8_t operation_mode;
float calibration_factor;
char device_name[32];
} JM_Configs;
// 序列化函数
void serialize_config(const JM_Configs* config, uint8_t* buf) {
memcpy(buf, config, sizeof(JM_Configs)); // 直接内存拷贝
}
```
##### **2.2 Flash 操作函数封装**
```c
#define CONFIG_FLASH_ADDR 0x0800F000 // Flash 存储地址
#define FLASH_SECTOR_SIZE 4096 // 扇区大小
// Flash 擦除封装
int erase_config_sector(void) {
return nor_flash_erase(CONFIG_FLASH_ADDR, FLASH_SECTOR_SIZE);
}
// Flash 写入封装
int write_config_to_flash(const uint8_t* data) {
return nor_flash_write(CONFIG_FLASH_ADDR, data, sizeof(JM_Configs));
}
// Flash 读取封装
int read_config_from_flash(uint8_t* buf) {
return nor_flash_read(CONFIG_FLASH_ADDR, buf, sizeof(JM_Configs));
}
```
##### **2.3 完整存储流程**
```c
int save_config(JM_Configs* config) {
uint8_t buffer[sizeof(JM_Configs)];
// 1. 序列化数据
serialize_config(config, buffer);
// 2. 擦除目标扇区
if (erase_config_sector() != 0) {
return -1; // 擦除失败
}
// 3. 写入Flash
if (write_config_to_flash(buffer) != 0) {
return -2; // 写入失败
}
// 4. 验证写入
uint8_t verify_buf[sizeof(JM_Configs)];
read_config_from_flash(verify_buf);
if (memcmp(buffer, verify_buf, sizeof(JM_Configs)) {
return -3; // 校验失败
}
return 0; // 成功
}
```
##### **2.4 数据加载流程**
```c
int load_config(JM_Configs* config) {
uint8_t buffer[sizeof(JM_Configs)];
// 1. 从Flash读取
if (read_config_from_flash(buffer) != 0) {
return -1; // 读取失败
}
// 2. 反序列化
memcpy(config, buffer, sizeof(JM_Configs));
return 0; // 成功
}
```
#### **3. 关键注意事项**
1. **内存对齐问题**
使用 `__attribute__((packed))` 确保结构体无填充字节,避免序列化时数据错位[^1]。
2. **写入前擦除**
Flash 必须先擦除后写入:
$$ \text{Flash 状态} = \begin{cases}
\text{擦除后} & \rightarrow 1 \\
\text{写入后} & \rightarrow 0
\end{cases} $$
未擦除直接写入会导致数据错误[^2]。
3. **写入粒度限制**
需遵守 Flash 的最小写入单位(通常 4/8/16 字节),`sizeof(JM_Configs)` 应满足:
$$ \text{size} \mod \text{write\_granularity} = 0 $$
4. **掉电保护**
建议增加 CRC 校验:
```c
uint32_t calculate_crc(const uint8_t* data, size_t len);
```
存储时在数据后追加 CRC 值,读取时验证[^3]。
#### **4. 错误处理增强**
```c
// 带重试机制的写入
int robust_write(const uint8_t* data, int max_retries) {
for (int i = 0; i < max_retries; i++) {
if (write_config_to_flash(data) == 0) {
return 0;
}
erase_config_sector(); // 失败后重新擦除
}
return -1;
}
```
#### **5. 性能优化建议**
1. **双扇区备份**
交替使用两个扇区存储,避免擦除失败导致数据丢失:
```c
#define ACTIVE_SECTOR 0
#define BACKUP_SECTOR 1
uint32_t get_sector_addr(int sector_id);
```
2. **差异更新**
仅写入变化的部分数据:
```c
void write_changes_only(const JM_Configs* new, const JM_Configs* old);
```
---
阅读全文
相关推荐
















