generated from hulk/gd32e23x_template_cmake_vscode
138 lines
3.9 KiB
C
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);
|
|
}
|