Files
GD32E230_VL53L4CD/Src/calib_store.c
T

138 lines
3.9 KiB
C

/**
* @file calib_store.c
* @brief VL53L4CD 校准数据 Flash 持久化存储实现
* @details 校准记录保存在主 Flash 的最后一页(1KB)。页地址根据上电检测到的
* Flash 容量(16K/32K/64K)动态计算,同一份固件可适配不同容量器件:
* 16K: 0x0800 3C00 32K: 0x0800 7C00 64K: 0x0800 FC00
* 记录为 16 字节(2 个双字),带魔数、版本与累加和校验;
* 空白 Flash 全 0xFF,不会误判为有效记录。
* GD32E230 页擦除为 1KB,编程以 64 位双字为单位。
*/
#include "gd32e23x.h"
#include "calib_store.h"
#include "board_config.h"
#include "gd32e23x_fmc.h"
#include "uart.h"
#include <string.h>
/** 校准记录魔数,"CVL4" 的 ASCII 小端编码 */
#define CALIB_RECORD_MAGIC 0x344C5643U
#define CALIB_RECORD_VERSION 1U
#define CALIB_RECORD_SIZE 16U
typedef struct {
uint32_t magic; /**< 魔数,用于识别有效记录 */
uint16_t version; /**< 记录格式版本 */
uint16_t flags; /**< 保留标志位 */
int16_t offset_mm; /**< 偏移校准值,单位 mm */
uint16_t xtalk_kcps; /**< 串扰校准值,单位 kcps */
uint16_t checksum; /**< 前 12 字节累加和 */
uint16_t pad; /**< 填充到 16 字节 */
} calib_record_t;
/**
* @brief 计算校准记录所在页的起始地址(Flash 最后一页)
* @retval uint32_t 页起始地址
*/
static uint32_t calib_page_address(void)
{
uint32_t size_kb = get_flash_size();
if (size_kb == 0U) {
size_kb = 16U; /* 读取异常时按最小容量处理 */
}
return 0x08000000U + (size_kb * 1024U) - 1024U;
}
/**
* @brief 计算记录前 12 字节的 16 位累加和
* @param[in] rec: 校准记录指针
* @retval uint16_t 累加和
*/
static uint16_t calib_checksum(const calib_record_t *rec)
{
const uint8_t *p = (const uint8_t *)rec;
uint16_t sum = 0U;
uint8_t i;
for (i = 0U; i < 12U; i++) {
sum += p[i];
}
return sum;
}
bool calib_store_load(int16_t *offset_mm, uint16_t *xtalk_kcps)
{
const calib_record_t *rec = (const calib_record_t *)(uintptr_t)calib_page_address();
if (offset_mm == NULL || xtalk_kcps == NULL) {
return false;
}
if (rec->magic != CALIB_RECORD_MAGIC || rec->version != CALIB_RECORD_VERSION) {
return false;
}
if (rec->checksum != calib_checksum(rec)) {
return false;
}
*offset_mm = rec->offset_mm;
*xtalk_kcps = rec->xtalk_kcps;
return true;
}
bool calib_store_save(int16_t offset_mm, uint16_t xtalk_kcps)
{
calib_record_t rec;
uint64_t data[2];
uint32_t page_addr;
fmc_state_enum fmc_state;
rec.magic = CALIB_RECORD_MAGIC;
rec.version = CALIB_RECORD_VERSION;
rec.flags = 0U;
rec.offset_mm = offset_mm;
rec.xtalk_kcps = xtalk_kcps;
rec.checksum = 0U;
rec.pad = 0U;
rec.checksum = calib_checksum(&rec);
/* GD32E230 按双字(64 位)编程,直接按内存布局拆成两个 64 位 */
memcpy(&data[0], &rec, 8U);
memcpy(&data[1], (const uint8_t *)&rec + 8U, 8U);
page_addr = calib_page_address();
/* Flash 操作期间总线停顿,暂停串口接收避免丢字节 */
uart_rx_irq_pause();
fmc_unlock();
fmc_state = fmc_page_erase(page_addr);
if (fmc_state == FMC_READY) {
fmc_state = fmc_doubleword_program(page_addr, data[0]);
if (fmc_state == FMC_READY) {
fmc_state = fmc_doubleword_program(page_addr + 8U, data[1]);
}
}
fmc_lock();
uart_rx_irq_resume();
return (fmc_state == FMC_READY);
}
bool calib_store_erase(void)
{
uint32_t page_addr = calib_page_address();
fmc_state_enum fmc_state;
uart_rx_irq_pause();
fmc_unlock();
fmc_state = fmc_page_erase(page_addr);
fmc_lock();
uart_rx_irq_resume();
return (fmc_state == FMC_READY);
}