350 lines
9.8 KiB
C
350 lines
9.8 KiB
C
/*
|
||
* 自然写智能点阵笔嵌入式固件软件 V1.0
|
||
* offline_storage.c - 离线Flash缓存存储
|
||
*
|
||
* 功能说明:
|
||
* 1. 在BLE断连时将笔迹数据缓存到外部SPI Flash
|
||
* 2. BLE重新连接后自动回传缓存数据
|
||
* 3. 环形缓冲区管理Flash存储空间
|
||
* 4. 掉电安全的写入机制(写入前擦除校验)
|
||
* 5. 存储使用统计与容量告警
|
||
*/
|
||
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
#include <string.h>
|
||
|
||
#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;
|
||
}
|