/* * 自然写智能点阵笔嵌入式固件软件 V1.0 * offline_storage.c - 离线Flash缓存存储 * * 功能说明: * 1. 在BLE断连时将笔迹数据缓存到外部SPI Flash * 2. BLE重新连接后自动回传缓存数据 * 3. 环形缓冲区管理Flash存储空间 * 4. 掉电安全的写入机制(写入前擦除校验) * 5. 存储使用统计与容量告警 */ #include #include #include #include "hal_flash.h" /* ========== Flash存储参数 ========== */ /* 外部SPI Flash总容量(4MB) */ #define FLASH_TOTAL_SIZE (4 * 1024 * 1024) /* Flash扇区大小(4KB,最小擦除单元) */ #define FLASH_SECTOR_SIZE 4096 /* Flash页大小(256字节,最小写入单元) */ #define FLASH_PAGE_SIZE 256 /* 离线存储起始地址(前64KB保留给系统配置) */ #define STORAGE_START_ADDR (64 * 1024) /* 离线存储可用大小 */ #define STORAGE_AVAILABLE (FLASH_TOTAL_SIZE - STORAGE_START_ADDR) /* 可用扇区数量 */ #define STORAGE_SECTOR_COUNT (STORAGE_AVAILABLE / FLASH_SECTOR_SIZE) /* 每条笔迹记录大小(固定长度,便于管理) */ #define RECORD_SIZE 16 /* 每个扇区能存储的记录数 */ #define RECORDS_PER_SECTOR (FLASH_SECTOR_SIZE / RECORD_SIZE) /* 记录头标识 */ #define RECORD_MAGIC 0xAB /* ========== 数据结构 ========== */ /* 存储记录结构(16字节固定长度) */ typedef struct __attribute__((packed)) { uint8_t magic; /* 记录标识 0xAB */ uint8_t record_type; /* 记录类型:0=坐标, 1=笔落下, 2=笔抬起 */ uint32_t x; /* X坐标 */ uint32_t y; /* Y坐标 */ uint16_t pressure; /* 压力值 */ uint16_t timestamp_offset; /* 时间偏移(相对于session开始) */ uint8_t checksum; /* 校验和 */ } StorageRecord; /* 存储管理状态 */ typedef struct { uint32_t write_sector; /* 当前写入扇区索引 */ uint16_t write_offset; /* 当前扇区内写入偏移 */ uint32_t read_sector; /* 当前读出扇区索引 */ uint16_t read_offset; /* 当前扇区内读出偏移 */ uint32_t total_records; /* 缓存的总记录数 */ uint32_t session_start_time; /* 当前存储会话开始时间 */ bool is_full; /* 存储是否已满 */ } StorageState; /* ========== 静态变量 ========== */ /* 存储管理状态 */ static StorageState s_state; /* 写入页缓冲区(攒满一页再写入Flash) */ static uint8_t s_page_buffer[FLASH_PAGE_SIZE]; static uint16_t s_page_buffer_offset = 0; /* ========== 初始化 ========== */ /** * 初始化离线存储模块 * 扫描Flash查找上次的写入位置(掉电恢复) */ void offline_storage_init(void) { memset(&s_state, 0, sizeof(s_state)); memset(s_page_buffer, 0xFF, sizeof(s_page_buffer)); s_page_buffer_offset = 0; /* 扫描Flash查找最后写入位置 */ scan_storage_state(); } /** * 扫描Flash存储区,恢复写入/读出位置 * 通过检查每个扇区的第一个字节来判断是否已写入 */ static void scan_storage_state(void) { uint32_t sector; uint8_t header; s_state.write_sector = 0; s_state.total_records = 0; for (sector = 0; sector < STORAGE_SECTOR_COUNT; sector++) { uint32_t addr = STORAGE_START_ADDR + sector * FLASH_SECTOR_SIZE; hal_flash_read(addr, &header, 1); if (header == 0xFF) { /* 空扇区,写入位置在此 */ s_state.write_sector = sector; break; } else if (header == RECORD_MAGIC) { /* 已写入的扇区,继续扫描 */ /* 统计有效记录数 */ uint16_t offset; for (offset = 0; offset < FLASH_SECTOR_SIZE; offset += RECORD_SIZE) { uint8_t magic; hal_flash_read(addr + offset, &magic, 1); if (magic == RECORD_MAGIC) { s_state.total_records++; } else { break; } } } } /* 读出位置从最早的数据扇区开始 */ s_state.read_sector = 0; s_state.read_offset = 0; } /* ========== 校验和计算 ========== */ /** * 计算记录校验和(简单异或校验) */ static uint8_t calculate_checksum(const StorageRecord *record) { const uint8_t *data = (const uint8_t *)record; uint8_t sum = 0; uint8_t i; /* 对除checksum字段外的所有字节异或 */ for (i = 0; i < sizeof(StorageRecord) - 1; i++) { sum ^= data[i]; } return sum; } /** * 验证记录校验和 */ static bool verify_checksum(const StorageRecord *record) { return calculate_checksum(record) == record->checksum; } /* ========== 写入操作 ========== */ /** * 将一条笔迹记录写入离线缓存 * * @param type 记录类型(0=坐标, 1=笔落下, 2=笔抬起) * @param x X坐标 * @param y Y坐标 * @param pressure 压力值 * @param timestamp 时间戳 * @return 0成功, -1存储已满, -2写入失败 */ int offline_storage_write(uint8_t type, uint32_t x, uint32_t y, uint16_t pressure, uint32_t timestamp) { if (s_state.is_full) { return -1; } /* 构建记录 */ StorageRecord record; record.magic = RECORD_MAGIC; record.record_type = type; record.x = x; record.y = y; record.pressure = pressure; record.timestamp_offset = (uint16_t)(timestamp - s_state.session_start_time); record.checksum = calculate_checksum(&record); /* 将记录复制到页缓冲区 */ memcpy(&s_page_buffer[s_page_buffer_offset], &record, RECORD_SIZE); s_page_buffer_offset += RECORD_SIZE; /* 页缓冲区满,写入Flash */ if (s_page_buffer_offset >= FLASH_PAGE_SIZE) { int ret = flush_page_buffer(); if (ret != 0) { return -2; } } s_state.total_records++; return 0; } /** * 将页缓冲区内容写入Flash * 写入前检查目标扇区是否需要擦除 */ static int flush_page_buffer(void) { uint32_t sector_addr = STORAGE_START_ADDR + s_state.write_sector * FLASH_SECTOR_SIZE; uint32_t page_addr = sector_addr + s_state.write_offset; /* 如果是扇区的起始位置,先擦除扇区 */ if (s_state.write_offset == 0) { hal_flash_erase_sector(sector_addr); } /* 写入一页数据 */ hal_flash_write(page_addr, s_page_buffer, FLASH_PAGE_SIZE); /* 读回验证(写入校验) */ uint8_t verify_buf[FLASH_PAGE_SIZE]; hal_flash_read(page_addr, verify_buf, FLASH_PAGE_SIZE); if (memcmp(s_page_buffer, verify_buf, FLASH_PAGE_SIZE) != 0) { /* 写入验证失败 */ return -1; } /* 更新写入位置 */ s_state.write_offset += FLASH_PAGE_SIZE; if (s_state.write_offset >= FLASH_SECTOR_SIZE) { s_state.write_offset = 0; s_state.write_sector++; if (s_state.write_sector >= STORAGE_SECTOR_COUNT) { /* 回绕到起始位置(环形缓冲) */ s_state.write_sector = 0; s_state.is_full = true; } } /* 清空页缓冲区 */ memset(s_page_buffer, 0xFF, sizeof(s_page_buffer)); s_page_buffer_offset = 0; return 0; } /* ========== 读取操作 ========== */ /** * 从离线缓存读取一条记录 * * @param record 输出记录指针 * @return 0成功并返回记录, 1无更多数据, -1读取错误 */ int offline_storage_read(StorageRecord *record) { if (s_state.total_records == 0) { return 1; } uint32_t addr = STORAGE_START_ADDR + s_state.read_sector * FLASH_SECTOR_SIZE + s_state.read_offset; /* 从Flash读取记录 */ hal_flash_read(addr, (uint8_t *)record, RECORD_SIZE); /* 验证记录有效性 */ if (record->magic != RECORD_MAGIC) { return 1; /* 无更多有效数据 */ } if (!verify_checksum(record)) { /* 校验和错误,跳过损坏的记录 */ s_state.read_offset += RECORD_SIZE; return -1; } /* 更新读出位置 */ s_state.read_offset += RECORD_SIZE; if (s_state.read_offset >= FLASH_SECTOR_SIZE) { s_state.read_offset = 0; s_state.read_sector++; if (s_state.read_sector >= STORAGE_SECTOR_COUNT) { s_state.read_sector = 0; } } s_state.total_records--; return 0; } /* ========== 缓冲区刷新 ========== */ /** * 强制将页缓冲区中的数据写入Flash * 在进入深度睡眠前调用,确保数据不丢失 */ void offline_storage_flush(void) { if (s_page_buffer_offset > 0) { flush_page_buffer(); } } /* ========== 存储状态查询 ========== */ /** * 获取缓存的记录数量 */ uint32_t offline_storage_get_count(void) { return s_state.total_records; } /** * 获取存储使用百分比 */ uint8_t offline_storage_get_usage_percent(void) { uint32_t max_records = STORAGE_SECTOR_COUNT * RECORDS_PER_SECTOR; if (max_records == 0) return 0; return (uint8_t)((uint64_t)s_state.total_records * 100 / max_records); } /** * 清空所有离线缓存数据 * 通过批量擦除Flash实现 */ void offline_storage_clear(void) { uint32_t sector; for (sector = 0; sector < STORAGE_SECTOR_COUNT; sector++) { uint32_t addr = STORAGE_START_ADDR + sector * FLASH_SECTOR_SIZE; hal_flash_erase_sector(addr); } /* 重置管理状态 */ memset(&s_state, 0, sizeof(s_state)); memset(s_page_buffer, 0xFF, sizeof(s_page_buffer)); s_page_buffer_offset = 0; } /** * 开始新的离线存储会话 * @param start_time 会话开始时间戳 */ void offline_storage_start_session(uint32_t start_time) { s_state.session_start_time = start_time; }