Files
2026-03-22 15:24:40 +08:00

350 lines
9.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 自然写智能点阵笔嵌入式固件软件 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;
}