Flash存取数据的另一种思路

在嵌入式项目中,为了让设备在断电后某些关键参数不丢失,比如设备ID,网络配置,外设配置等。我们会将这些关键的参数存储到片内的Flash中。一般的做法都是在Flash划分一块空间做存储参数用,并且里面有一个空间存储一个标志,这个标志指示了Flash中是否存储了有效的参数。在第一次烧录程序时,Flash空间内没有存过参数,上电后这个标志读出来就是0XFF,此时程序就会将默认参数写入到Flash中,并且把标志位设置为某个特定值写入Flash中,这样下次上电再从Flash中读标志就会发现不是0XFF,如果是特定值,说明FLASH中参数有效,我们就会把FLASH中的参数读出来,拷贝给RAM中的全局变量。当然,为了更可靠,我们一般还会加入CRC校验。

#define PARA_FLASH_ADDR 0X80003000

typde struct{
    uint8_t flag;
    uint32_t id;
    uint8_t baud;
    uint16t crc;
}stPara_t;

stPara_t gPara = {0};
uint8_t Flash_ParamRead(stPara_t* para)
{
    FlashRead((uint8_t*)para,PARA_FLASH_ADDR,sizeof(stPara_t));
    return 1;
}

uint8_t Flash_ParamWrite(stPara_t* para)
{
    FlashWrite((uint8_t*)para,PARA_FLASH_ADDR,sizeof(stPara_t));
    return 1;
}

存取参数的时候

//取参数
Flash_ParamRead(&gPara);

//存参数
Flash_ParamWrite(&gPara);

我一般的常规操作都是像上面这样。但是最近我遇到了另外一个方法。


#define  STORAGE_ROM_BYTES (1024)
#define  STORAGE_MAX_BYTES (STORAGE_ROM_BYTES-4)
typedef struct {
	unsigned short Head;
	unsigned char Data[STORAGE_MAX_BYTES];
	unsigned short Crc;
}stFlash, *stptrFlash;
static const stParaConfig* ParaConfigPtr = NULL;
eFlash_Err FlashGetParaRomPtr(unsigned int addr, void** data)
{
	stptrFlash promtr = NULL;
	eFlash_Err err = FlashError;
	promtr = (stptrFlash)addr;
	if (promtr->Head == FLASH_HEAD) {
		if (crc_16_modbus(promtr->Data, STORAGE_MAX_BYTES) == promtr->Crc) {
			(*data) = promtr->Data;
			err = FlashNomal;
		}
	}
	if (err != FlashNomal) {
		if (promtr->Head == STORAGE_HEAD_FALSH_DEFAULT)
		{
			err = FlashFirstStart;
		}
		else
		{
			err = FlashError;
		}
	}
	return err;
}


eFlash_Err FlashParaSet(unsigned int addr, const void* data, int datalen)
{
	eFlash_Err err = FlashError;
	stFlash stg = { 0,{0},0 };
	unsigned char *data_tmp = NULL;
	err = FlashGetParaRomPtr(addr, (void**)&data_tmp);
	switch (err)
	{
	case FlashFirstStart:
	{
		//如果是first Start ,写入初始值
		stg.Head = STORAGE_HEAD;
		memset(stg.Data, 0, STORAGE_MAX_BYTES);
		memcpy(stg.Data, data, datalen);
		stg.Crc = crc_16_modbus(stg.Data, STORAGE_MAX_BYTES);
		Flash_WriteData(addr, (unsigned char*)&stg, STORAGE_ROM_BYTES);
	}break;
	case FlashNomal:
	{
    stg.Head = STORAGE_HEAD;
		memcpy(stg.Data, data, datalen);
		stg.Crc = crc_16_modbus(stg.Data, STORAGE_MAX_BYTES);
		Flash_WriteData(addr, (unsigned char*)&stg, STORAGE_ROM_BYTES);
	}break;
        case CfgDtFlashError:
	{
		//如果是first Start ,写入初始值
		stg.Head = STORAGE_HEAD;
		memset(stg.Data, 0, STORAGE_MAX_BYTES);
		memcpy(stg.Data, data, datalen);
		stg.Crc = crc_16_modbus(stg.Data, STORAGE_MAX_BYTES);
		Flash_WriteData(addr, (unsigned char*)&stg, STORAGE_ROM_BYTES);
	}break;
	default:return err;
	}
	return err;
}
int ParaConfigInit(void)
{
	stParaConfig* pactr = NULL;
	eFlash_Err err = FlashError;
	stParaConfig cfg;

	ParaConfigPtr = NULL;
	memset(&cfg, 0, sizeof(stParaConfig));
	err = FlashGetParaRomPtr(PARA_DATA_STORAGE_ADDR, (void**)&pactr);
	switch (err)
	{
	case FlashFirstStart:
	{
		FlashParaSet(PARA_DATA_STORAGE_ADDR, (const void*)&cfg,sizeof(stParaConfig));
        ParaConfigPtr = &ParaConfigrationDef;
        rc = 1;
	}
	break;
	case FlashNomal:
	{
		ParaConfigPtr = pactr;
                rc = 1;
	}
	break;
	default:
		break;
	}
  return rc;
}

全局的参数定义是这句,居然只是个指针。

static const stParaConfig* ParaConfigPtr = NULL;

从Flash中把参数读出来,调用的是ParaConfigInit函数,在这个函数中,定义了全局参数数据类型的指针,然后把这个指针的地址传递给ParaConfigInit。

而在ParaConfigInit函数中,操作也是比较特别。首先定义了一个stFlash类型的结构体指针,然后就直接把参数保存在Flash中的地址强转成这个stFlash类型的指针赋值给它。然后就直接取值,看标志位,如果标志位对的话。注意了,直接把data的地址赋值给传入的指针。

回到ParaConfigInit函数,读到参数后,跳转到FlashNomal分支。直接把刚刚获得的指针赋值给全局参数指针,参数读取就完成了。需要用到全局参数的时候就直接解引用,像这样ParaConfigPtr ->id;

这是什么操作呢,相当于是把参数的FLASH地址赋值给全局参数指针,然后每次要用到全局参数的时候,MCU就自动去flash中读参数。这波操作也是6了。上面我一般的做法,是把FLash中的参数拷贝到RAM,用的时候都是用RAM中的那个参数。而下面这段代码,则没有拷贝这一步,直接是读参数的时候让MCU去FLASH中读。

好处显而易见,这个参数一直在FLASH中,没拷贝出来,不会占用RAM的空间。坏处则是,无法对参数做直接的修改,因为全局参数指向的地址是FLASH,对FLASH的读,可以像对RAM的读那样操作,但是写一般都是要遵循一定的步骤,可不像RAM那样直接写的。

关于这段程序,有一个地方我是难以苟同的,那就是stFlash这个结构体了,这个结构体的大小看得出来设计者是把它定位一个扇区的大小。在存储参数的函数FlashParaSet中,直接定义了一个stFlash类型的结构体变量,然后把参数拷贝到这个变量的data字段,把这个变量的地址传递给Flash_WriteData把整个变量写入Flash。为什么说这里不好,因为一个stFlash结构体变量有可能很大,FLASH一个扇区的大小呢,假如MCU的RAM不够大,这么一定义,很有可能会把栈里的其他有用数据给清掉,程序意外跑飞是很有可能的事。

猜你喜欢

转载自blog.csdn.net/xbzlxtz/article/details/84648587