描述:
通常在做IOT产品或者项目需要参数保存时,通常我们的逻辑是首先判断flash中是否有参数的痕迹,有则读出直接使用,否则认为参数不合法,或者没有参数则要进行参数的初始化。
typedef struct
{
uint16 Start_Init_Flag;
uint8 iFlashHardwareUniqueID[4]; //DEVICE ID
uint8 iFlashFactoryAESKey[16]; //出厂加密KEY
uint8 iFlashHardwareUniqueIDCipher[8]; //Cipher ID
uint16 End_Init_Flag;
}T_FLASH_FACTORY,*PT_FLASH_FACTORY;
如图在结构体中,有Start_Init_Flag、 End_Init_Flag两个参数,在参数保存时赋值一个值如,0X5A5A,则在读取参数时则判断此值是否为人为赋的正常值0X5A5A来判定参数区域是否合法。
此通过头尾变量的方式来做,是可以达到效果,但是不完善存在BUG,问题如下:
- 单个值不保险,如头尾变量标志在flash内存里,有一次异常操作没有修改到头尾,只改变了中间位置的参数,这样我们依旧是认为合法的。(这样是不对的)
- 若旧版本软件结构体参数为2个,然后后续发布了新版程序,参数增加了至3个,但是有可能因为结构体对齐原因导致整个结构体大小没有变化。这样读出的参数,头尾就还是正确的。所以认为合法参数,但是实际值并不合法,(因为把原本对齐的空间,现在给了新增参数,虽然里面的值是0X00,但是这样是不合法的,值不是人为合法给的)。
解决方法:
typedef struct
{
uint16 Start_Init_Flag; //参数存在标志
uint8 Allocation_Net_SSID[MAX_NET_SSID_LEN]; //连接网 络名称
uint8 Allocation_Net_PWD[MAX_NET_PWD_LEN]; //连接网络密码
uint8 iFlashNewIPAddress[MAX_SERVECE_IPADDRESS]; //新的入网IP
uint8 iFlashNewIPPort[MAX_SERVECE_IPPORT]; //新的入网端口号
uint8 iFlashGatewayUseIp; //使用域名或者IP标志
uint8 iFlashNewDomainName[MAX_SERVECE_DOMAINNAME]; //域名
uint16 iFlashMaxMessageID; //message ID最大值
T_SLEEP_MODE_PARAMETER Sleep_Parameter;
uint8 iFlashWifi_APMAC[20]; //WIFI MAC地址
uint16 Cat_Out_Scab_Time; //猫砂结痂时间
uint8 Device_WorkPattern; //设备工作模式
uint16 End_Init_Flag; //参数存在标志
uint8 Parameter_Crc;
}__attribute__ ((packed))T_FLASH_PROGRAM,*PT_FLASH_PROGRAM; //保存flash参数
如次结构:在结构体中除了头尾标志,增加 uint8 Parameter_Crc字段,在保存时加入crc。并且结构体采用单字节对齐方式,保证增加减少参数一定会改变结构长度,不会出现增加参数导致长度不变,导致对齐问题出现异常赋值问题。
如下为读取参数,保存机制:
void Device_FlashConfig_Parameter_Save(void)
{
uint8 Parameter_Crc;
Device_flashParam.Start_Init_Flag = FLASH_PARAMETER_INTACT_FLAG; //标志置位
Device_flashParam.End_Init_Flag = FLASH_PARAMETER_INTACT_FLAG; //标志置位
Parameter_Crc = Check_CRC((uint8 *)&Device_flashParam,sizeof(Device_flashParam)-sizeof(Device_flashParam.Parameter_Crc));
Device_flashParam.Parameter_Crc = Parameter_Crc;
STM32_Flash_ErasePage(DEVICE_CONFIG_PARAMETER_ADDR);
STM32_Flash_Write_NBytes(DEVICE_CONFIG_PARAMETER_ADDR,(uint8 *)&Device_flashParam,sizeof(Device_flashParam));
}
uint8 Device_FlashConfig_ParameterCheck(void)
{
uint8 Parameter_Crc;
STM32_Flash_Read_NBytes(DEVICE_CONFIG_PARAMETER_ADDR, (uint8 *)&Device_flashParam, sizeof(Device_flashParam)); //读出falsh参数
Parameter_Crc = Check_CRC((uint8 *)&Device_flashParam,sizeof(Device_flashParam)-sizeof(Device_flashParam.Parameter_Crc));
if((Device_flashParam.Start_Init_Flag != FLASH_PARAMETER_INTACT_FLAG) || (Device_flashParam.End_Init_Flag != FLASH_PARAMETER_INTACT_FLAG) ||\
(Parameter_Crc != Device_flashParam.Parameter_Crc))
{
#if Debug_Parameter
printf("*\r\n======FlashConfig_ParameterCheckInit=======*Parameter_Crc=0X%X,Device_flashParam.Parameter_Crc =0X%X\r\n",Parameter_Crc,Device_flashParam.Parameter_Crc);
#endif
Device_FlashConfig_ParameterInit();
Device_FlashConfig_Parameter_Save();
return 1;
}
#if Debug_Parameter
PCTxString("*======FlashConfig_Parameter Read successd=======*\r\n");
#endif
return 0;
}
注意:
尽管结构中增加头尾标志变量、crc校验、单字节对齐。但也应对不了一种情况,那就是结构体空间未出现大小变化,仅是将结构体成员进行位置调换,在这种情况下,读取数据后,crc校验正常,但是参数会错位,所以需要极其注意,最好是对每个参数都进行范围检测,判断是否非法!保证程序不会出现较大的隐患。