很多是否用单片机内部的FLASH或者外部NOR FLASH存储参数很不方便,不如24系列。于是我就写了一个简单的文件系统,但是我觉得代码不太易于读懂,如实最近通过ai重新写了代码,ai生成的还是有bug的,但是结构很好,经过修改调试后我把他贴上来!欢迎大家测试,并提供宝贵意见!
EERPOM结构 EEPROM由两个页组成:页0和页1,在使用的时候,1个页处于有效状态,另外一个页处于擦除 状态,读取或者写入数据都在有效状态的页进行。 第一个字节为页使用标志,0xAA在用,0xFF空闲 数据存储结构 当页0写满时,将页0中的有效数据转移至页1
需要实现三个函数,供程序调用 extern void flash_read(uint16_t addr, uint8_t* buf, uint8_t len); extern void flash_write(uint16_t addr, const uint8_t* buf, uint8_t len); extern void flash_erase(uint16_t block_addr);
头文件
// 定义块大小和块数量 #define BLOCK_SIZE 128 #define BLOCK_COUNT 2
// 定义块使用标记 #define BLOCK_IN_USE 0xAA #define BLOCK_FREE 0xFF
// 定义变量结构中的标记 #define VAR_FREE 0xFF #define VAR_VALID 0xFF #define VAR_DISCARDED 0x7F
// 初始化状态码定义 #define FLASHLOG_INIT_NEW 0 // 两个块都未被初始化 #define FLASHLOG_INIT_DONE 1 // 块已被初始化 #define FLASHLOG_INIT_FAILED -1 // 初始化失败
// 错误码定义 #define FLASHLOG_SUCCESS 0 // 操作成功 #define FLASHLOG_ERROR_INVALID_LENGTH 1 // 变量长度不匹配 #define FLASHLOG_ERROR_NOT_FOUND 2 // 变量未找到 #define FLASHLOG_ERROR_WRITE_FAILED 3 // 写入失败 #define FLASHLOG_ERROR_READ_FAILED 4 // 读取失败 #define FLASHLOG_ERROR_INIT_FAILED 5 // 初始化失败
#ifndef true #define true 1 #endif
#ifndef false #define false 0 #endif
// 定义变量头结构体 typedef struct { uint8_t length; // 变量数据长度 uint8_t valid; // 变量有效位 uint8_t id; // 变量ID } var_header_t;
uint8_t FLASH[BLOCK_SIZE*BLOCK_COUNT];
// 函数声明 /** * @brief 格式化Flash * * 格式化后,一定要初始化flashlog_init() * */ void flashlog_format(void);
/** * @brief 初始化FlashLog文件系统 * * 检查两个块的使用标记,确定当前使用的极,并初始化next_write_addr。 * 如果两个块都空闲,则先格式化两个块。 * * @return int8_t 状态码: * - FLASHLOG_INIT_NEW:两个块都未被初始化(已格式化) * - FLASHLOG_INIT_DONE:块已被初始化 * - FLASHLOG_INIT_FAILED:初始化失败 */ int8_t flashlog_init(void);
/** * @brief 写入变量到FlashLog文件系统 * * @param id 变量ID * @param buf 数据指针 * @param len 数据长度 * @return int8_t 错误码:FLASHLOG_SUCCESS 表示成功,其他表示失败 */ int8_t flashlog_write(uint8_t id, const uint8_t* buf, uint8_t len);
/** * @brief 从FlashLog文件系统读取变量 * * @param id 变量ID * @param buf 数据指针 * @param len 数据长度 * @return int8_t 错误码:FLASHLOG_SUCCESS 表示成功,其他表示失败 */ int8_t flashlog_read(uint8_t id, uint8_t* buf, uint8_t len);
/** * @brief 删除FlashLog文件系统中的变量 * * @param id 变量ID * @return int8_t 错误码:FLASHLOG_SUCCESS 表示成功,其他表示失败 */ int8_t flashlog_delete(uint8_t id);
代码文件
// 外部Flash操作函数声明 extern void flash_read(uint16_t addr, uint8_t* buf, uint8_t len); extern void flash_write(uint16_t addr, const uint8_t* buf, uint8_t len); extern void flash_erase(uint16_t block_addr);
// 全局变量定义 uint16_t current_block = 0; // 当前使用的块 uint16_t next_write_addr = 1; // 下一个写入地址
// 函数声明 void migrate_data(void); uint16_t find_free_space(uint16_t start_addr, uint16_t end_addr); uint8_t find_variable(uint8_t id, uint16_t* addr);
/** * @brief 格式化Flash * * 格式化后,一定要初始化flashlog_init() * */ void flashlog_format(void) { flash_erase(0); flash_erase(BLOCK_SIZE); }
/** * @brief 初始化FlashLog文件系统 * * 检查两个块的使用标记,确定当前使用的极,并初始化next_write_addr。 * 如果两个块都空闲,则先格式化两个块。 * * @return int8_t 状态码: * - FLASHLOG_INIT_NEW:两个块都未被初始化(已格式化) * - FLASHLOG_INIT_DONE:块已被初始化 * - FLASHLOG_INIT_FAILED:初始化失败 */ int8_t flashlog_init(void) { uint8_t block_mark; // 读取块0的使用标记 flash_read(0, &block_mark, 1); if (block_mark == BLOCK_IN_USE) { current_block = 0; return FLASHLOG_INIT_DONE; // 块已被初始化 } // 读取块1的使用标记 flash_read(BLOCK_SIZE, &block_mark, 1); if (block_mark == BLOCK_IN_USE) { current_block = BLOCK_SIZE; return FLASHLOG_INIT_DONE; // 块已被初始化 } // 如果两个块都空闲,格式化两个块 flash_erase(0); flash_erase(BLOCK_SIZE); // 默认使用块0 current_block = 0; block_mark = BLOCK_IN_USE; flash_write(current_block, &block_mark, 1); // 初始化next_write_addr next_write_addr = current_block + 1; return FLASHLOG_INIT_NEW; // 两个块都未被初始化(已格式化) }
/** * @brief 写入变量到FlashLog文件系统 * * @param id 变量ID * @param buf 数据指针 * @param len 数据长度 * @return int8_t 错误码:FLASHLOG_SUCCESS 表示成功,其他表示失败 */ int8_t flashlog_write(uint8_t id, const uint8_t* buf, uint8_t len) { uint16_t free_addr,var_addr; var_header_t header; // 查找空闲区域 free_addr = find_free_space(next_write_addr, current_block + BLOCK_SIZE); if( (free_addr == 0)||(free_addr+sizeof(var_header_t)+len > current_block + BLOCK_SIZE) ) { // 如果没有找到空闲区域或者空闲区域尺寸不能存下变量数据,则迁移数据 migrate_data(); free_addr = find_free_space(next_write_addr, current_block + BLOCK_SIZE); if ( (free_addr == 0)||(free_addr+sizeof(var_header_t)+len > current_block + BLOCK_SIZE) ) { return FLASHLOG_ERROR_WRITE_FAILED; // 仍然没有空闲区域,写入失败 } } // // 查找变量 var_addr = 0; if ( find_variable(id, &var_addr) ) {//如果找到,则var_addr不为0,且匹配数据是否相同 uint8_t i,dat; // 读取变量头 flash_read(var_addr, (uint8_t*)&header, sizeof(var_header_t)); // 检查变量长度是否匹配 if (header.length != len) { return FLASHLOG_ERROR_INVALID_LENGTH; } //判断数据是否相同 for( i=0;i<len;i++ ) { // 读取数据 flash_read(var_addr + sizeof(var_header_t) + i, &dat, 1); if( (*(buf+i)) != dat ) {//如果原来数据和新数据不同,则跳出此处,另行存储 break; } } if( i >= len )//判断过程执行完了,则原来数据和新数据相同,直接返回 { return FLASHLOG_SUCCESS; } } //如果没找到,则var_addr = 0,直接查找空闲区域写入; // 构建变量头 header.length = len; header.valid = VAR_VALID; header.id = id; // 写入变量头和数据 //flash_write(free_addr, (uint8_t*)&header, sizeof(var_header_t)); flash_write(free_addr+0, (uint8_t*)&header.length, 1); flash_write(free_addr+2, (uint8_t*)&header.id, 1); flash_write(free_addr + sizeof(var_header_t), buf, len); // 更新next_write_addr next_write_addr = free_addr + sizeof(var_header_t) + len; //如果变量之间存在,则删除之前变量 if(var_addr) { // 标记变量为丢弃 header.valid = VAR_DISCARDED; flash_write(var_addr + 1, &header.valid, 1); } return FLASHLOG_SUCCESS; }
/** * @brief 从FlashLog文件系统读取变量 * * @param id 变量ID * @param buf 数据指针 * @param len 数据长度 * @return int8_t 错误码:FLASHLOG_SUCCESS 表示成功,其他表示失败 */ int8_t flashlog_read(uint8_t id, uint8_t* buf, uint8_t len) { uint16_t var_addr; var_header_t header; // 查找变量 if (!find_variable(id, &var_addr)) { return FLASHLOG_ERROR_NOT_FOUND; } // 读取变量头 flash_read(var_addr, (uint8_t*)&header, sizeof(var_header_t)); // 检查变量长度是否匹配 if (header.length != len) { return FLASHLOG_ERROR_INVALID_LENGTH; } // 读取数据 flash_read(var_addr + sizeof(var_header_t), buf, len); return FLASHLOG_SUCCESS; }
/** * @brief 删除FlashLog文件系统中的变量 * * @param id 变量ID * @return int8_t 错误码:FLASHLOG_SUCCESS 表示成功,其他表示失败 */ int8_t flashlog_delete(uint8_t id) { uint16_t var_addr; uint8_t discarded = VAR_DISCARDED; // 查找变量 if (!find_variable(id, &var_addr)) { return FLASHLOG_ERROR_NOT_FOUND; } // 标记变量为丢弃 flash_write(var_addr + 1, &discarded, 1); return FLASHLOG_SUCCESS; }
/** * @brief 迁移数据到另一个块 * * 将当前块中的所有有效数据迁移到另一个块中,然后格式化当前块。 */ void migrate_data(void) { uint16_t new_block; uint16_t new_next_write_addr; uint16_t addr; var_header_t header; uint8_t buffer[8]; // 用于分批迁移的缓冲区 uint8_t bytes_to_copy,t8; uint16_t data_addr; uint16_t remaining_bytes; new_block = (current_block == 0) ? BLOCK_SIZE : 0; new_next_write_addr = new_block + 1; // 遍历当前块中的所有变量 for (addr = current_block + 1; addr < current_block + BLOCK_SIZE; ) { if(addr + sizeof(var_header_t) >= current_block + BLOCK_SIZE) {//如果超出当前block的范围,则退出 break; } flash_read(addr, (uint8_t*)&header, sizeof(var_header_t));
if ( (header.length != VAR_FREE) && (header.valid == VAR_VALID) ) { // 写入变量头到新块 //flash_write(new_next_write_addr, (uint8_t*)&header, sizeof(var_header_t)); flash_write(new_next_write_addr+0, (uint8_t*)&header.length, 1); flash_write(new_next_write_addr+2, (uint8_t*)&header.id, 1); // 迁移数据(合并逻辑) data_addr = addr + sizeof(var_header_t); remaining_bytes = header.length; while (remaining_bytes > 0) { bytes_to_copy = (remaining_bytes > sizeof(buffer)) ? sizeof(buffer) : remaining_bytes; flash_read(data_addr, buffer, bytes_to_copy); flash_write(new_next_write_addr + sizeof(var_header_t) + (header.length - remaining_bytes), buffer, bytes_to_copy); data_addr += bytes_to_copy; remaining_bytes -= bytes_to_copy; } new_next_write_addr += sizeof(var_header_t) + header.length; } addr += sizeof(var_header_t) + header.length; } // 格式化当前块 flash_erase(current_block); t8 = BLOCK_IN_USE; flash_write(new_block, &t8, 1); // 更新当前块和next_write_addr current_block = new_block; next_write_addr = new_next_write_addr; }
/** * @brief 查找空闲区域 * * @param start_addr 起始地址 * @param end_addr 结束地址 * @return uint16_t 空闲区域的起始地址,0表示没有找到 */ uint16_t find_free_space(uint16_t start_addr, uint16_t end_addr) { uint16_t addr; var_header_t header; for (addr = start_addr; addr < end_addr; ) { if(addr + sizeof(var_header_t) >= current_block + BLOCK_SIZE) {//如果超出当前block的范围,则退出 break; } flash_read(addr, (uint8_t*)&header, sizeof(var_header_t)); if (header.length == VAR_FREE) { return addr; }
addr += sizeof(var_header_t) + header.length; } return 0; }
/** * @brief 查找变量 * * @param id 变量ID * @param addr 变量地址指针 * @return true 找到变量 * @return false 未找到变量 */ uint8_t find_variable(uint8_t id, uint16_t* addr) { uint16_t i; var_header_t header; for (i = current_block + 1; i < current_block + BLOCK_SIZE; ) { if(i + sizeof(var_header_t) >= current_block + BLOCK_SIZE) {//如果超出当前block的范围,则退出 break; } flash_read(i, (uint8_t*)&header, sizeof(var_header_t)); if (header.length != VAR_FREE && header.valid == VAR_VALID && header.id == id) { *addr = i; return true; } i += sizeof(var_header_t) + header.length; } return false; }
|