software copyright

This commit is contained in:
jiahong
2026-03-22 15:24:40 +08:00
parent e303bb868a
commit 60f336e345
155 changed files with 127262 additions and 0 deletions
@@ -0,0 +1,349 @@
/*
* 自然写智能点阵笔嵌入式固件软件 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;
}