在嵌入式项目中,经常使用FLASH来存储关键的参数。这些参数影响整个设备系统的运行,保证参数存储的正确性是至关重要,常用的方法是通过校验参数存储以及参数备份的方式去降低出错的风险,本文将讲述具体的实现方法。
在FLASH上开放两个扇区,用于备份数据,可有效降低丢失数据风险。
#define SPI_FLASH_PARAM_SIZE 0x1000
#define SPI_FLASH_PARAM_BASE (0x00000000U)
#define SPI_FLASH_PARAM_BKUP (SPI_FLASH_PARAM_BASE + SPI_FLASH_PARAM_SIZE)
#define flash_param_base_erase bsp_spi_flash_sector_erase(SPI_FLASH_SECTOR0)
#define flash_param_bkup_erase bsp_spi_flash_sector_erase(SPI_FLASH_SECTOR1)
定义校验参数的结构体,以参数ID识别类型,校验存储的参数值。
typedef struct{
uint32_t param_id; //参数ID
uint16_t crc; //校验值
uint16_t len; //参数长度
uint8_t data[1];
}single_save_t;
对于需要存储的参数,可放在一个或多个结构体中,可在设备上电时导入存储在FLASH里的参数。
#define PARAM_ID 0x12345678
typedef struct {
uint8_t param_1;
uint8_t param_2;
uint8_t param_3;
uint8_t param_4;
uint8_t param_5;
}sys_param_t;
sys_param_t SysParam;
sys_param_t *gp_sys_param = &SysParam;
16位CRC校验,可采用寻表的方式去校验,另外其他的校验也可以使用。具体的实现方法可另外到网上查询。
FLASH的写操作是需要先擦除,在存储参数时可检查存储的参数是否需要修改。
/*
* @brief 检查参数
**/
int bsp_spi_flash_check_param(uint32_t readaddr, uint8_t *pbuffer, uint32_t num)
{
int i = 0;
uint8_t temp;
uint8_t *pbuf = (uint8_t *)pbuffer;
FLASH_SPI_CS_ENABLE();
bsp_spi_flash_sendbyte(FLASH_READ);
bsp_spi_flash_sendbyte((readaddr >> 16) & 0xFF);
bsp_spi_flash_sendbyte((readaddr >> 8) & 0xFF);
bsp_spi_flash_sendbyte(readaddr & 0xFF);
for (i = 0; i < num; i++, pbuf++) {
temp = bsp_spi_flash_sendbyte(0xff);
if (temp != *pbuf) break;
}
FLASH_SPI_CS_DISABLE();
if (i >= num) return 1;
return 0;
}
/*
* @brief 保存设备参数
**/
static int save_single_param(uint32_t param_id, uint32_t flash_addr, uint8_t *ram_addr, uint32_t len)
{
single_save_t psave;
psave.param_id = param_id;
psave.len = len;
psave.crc = CRC16(ram_addr, len);
if (bsp_spi_flash_check_param(flash_addr, (uint8_t*)&psave, 8)) {
if (bsp_spi_flash_check_param(flash_addr + 8, ram_addr, len)) {
return 0;
}
}
if (SPI_FLASH_PARAM_BASE == flash_addr) {
flash_param_base_erase();
} else {
flash_param_bkup_erase();
}
bsp_spi_flash_buffer_write((uint8_t *)&psave, flash_addr, 8);
bsp_spi_flash_buffer_write(ram_addr, flash_addr + 8, len);
return 0;
}
/*
* @brief 保存设备参数以及备份参数
**/
void save_param(void)
{
save_single_param(PARAM_ID, SPI_FLASH_PARAM_BASE, (uint8_t *)&SysParam, sizeof(sys_param_t));
save_single_param(PARAM_ID, SPI_FLASH_PARAM_BKUP, (uint8_t *)&SysParam, sizeof(sys_param_t));
}
定义默认参数值,可在设备第一次运行时导入或者FLASH出错时自动导入。
/*
* @brief 可设置设备运行的默认参数
**/
void load_default_param(void)
{
gp_sys_param->param_1 = 0;
gp_sys_param->param_2 = 0;
gp_sys_param->param_3 = 0;
gp_sys_param->param_4 = 0;
gp_sys_param->param_5 = 0;
}
/*
* @brief 导入分区存储参数
**/
int load_single_param(uint32_t param_id, uint32_t flash_addr, uint8_t *ram_addr, uint32_t maxlen)
{
single_save_t psave;
uint16_t crc;
bsp_spi_flash_buffer_read((uint8_t*)&psave, flash_addr, 8);
if (param_id != psave.param_id) return 1;
if (maxlen != psave.len) return 1;
bsp_spi_flash_buffer_read(ram_addr, flash_addr + 8, maxlen);
crc = CRC16(ram_addr, maxlen);
if (crc != psave.crc) return 1;
return 0;
}
/*
* @brief 导入存储参数
* @return 0:导入存储参数成功,1:导入默认参数
**/
int load_param(void)
{
if (load_single_param(PARAM_ID, SPI_FLASH_PARAM_BASE, (uint8_t*)&SysParam, sizeof(sys_param_t))) {
if (load_single_param(PARAM_ID, SPI_FLASH_PARAM_BKUP, (uint8_t*)&SysParam, sizeof(sys_param_t))) {
load_default_param();
save_param();
return 1;
}
save_param();
}
return 0;
}