/** * @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 /** 校准记录魔数,"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); }