ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

STM32G0系列将内部FLASH作为EEPROM使用,巧妙编程,可延长Flash擦写寿命上百倍,已用于量产产品。

2022-01-07 12:04:49  阅读:785  来源: 互联网

标签:0x5A FLASH u8 擦写 Flash user FLash


STM32内部flash可以用作EEPROM,用于保存用户数据。

1、一般来说,stm32的flash擦写寿命只有10万次,如果在同一位置擦写过于频繁,在产品质保期内FLASH就会达到寿命极限,保存数据出现异常。

2、stm32G0系列,一页flash的容量是2KB,往flash写数据(写0)的时候可以在任意位置写入任意长度的字节(当然不超过2KB),但是擦除(写1)的时候必须整页擦除,只有十万次的擦除寿命、

3、用户保存的字节一般只有几十个字节,可以在同一页flash里的逐个区域保存数据(写0),直到使用完一整页,才擦除整页,这样就可以延长擦写寿命。

4、代码原理不细说,代码原理都要了解了才用,那生命效率太低了,直接告诉你们怎么使用吧,十分钟上手。

5、我是使用的是LL库。

/**用户在此定义需要保存的数据**/
u8      user_data1;
u16     user_data2;
u32     user_data3;
float   user_data4;
double  user_data5;
/**************************/


#define		d_FLASH_Page_Size		2048//一页flash2KB
#define		d_FLASH_Data_maxbyte	32  //用户的数据长度,32 byte,必须是8的倍数,多余的字节填0x5A
#define		d_FLASH_Data_maxDWord	4   //用户的数据长度,4 double word=4*64bit=32 byte,直接用上面32/8 就是这个数字了

typedef union
{
	volatile	uc8		R_Flash8[2048];
	volatile	uc16	R_Flash16[1024];
	volatile	uc32	R_Flash32[512];
	volatile	uc64	R_Flash64[256];
}FLASH_DATA_typedef;//用户不用管
	
const FLASH_DATA_typedef		FLASH_DATA_SAVE	__attribute__((at(d_FLASH_DATA_ADDRESS)));//用户不用管		

u16		R_Flash_i;//用户不用管	

u8	R_0xA5=0xA5;//保存的数据帧头,以此来确认上次保存的数据在哪个位置
u8	R_0x5A=0x5A;//多余的字节填充0x5A


/**用户上面定义的数据拆分放入列表**/
u8 *const FLASH_Data[]={
	&R_0xA5,

    &user_data1,                //unsigned char=1 byte

	(u8 *)&user_data2,
	(u8 *)((u8 *)&user_data2+1),//unsigned short int =2 byte
	
	(u8 *)&user_data3,
	(u8 *)((u8 *)&user_data3+1),//unsigned int =4 byte
    (u8 *)((u8 *)&user_data3+2),
    (u8 *)((u8 *)&user_data3+3),
	
	(u8 *)&user_data4,
	(u8 *)((u8 *)&user_data4+1),//float=4 byte
    (u8 *)((u8 *)&user_data4+2),
    (u8 *)((u8 *)&user_data4+3),
	
	(u8 *)&user_data5,
	(u8 *)((u8 *)&user_data5+1),//double=8 byte
    (u8 *)((u8 *)&user_data5+2),
    (u8 *)((u8 *)&user_data5+3),
    (u8 *)((u8 *)&user_data5+4),
    (u8 *)((u8 *)&user_data5+5),
    (u8 *)((u8 *)&user_data5+6),
    (u8 *)((u8 *)&user_data5+7),

	&R_0x5A,
	&R_0x5A,
	&R_0x5A,
	&R_0x5A,
	&R_0x5A,
	&R_0x5A,
	&R_0x5A,
	&R_0x5A,
	&R_0x5A,
    &R_0x5A,
    &R_0x5A,
    &R_0x5A
};//上面定义保存长度32 byte,如果用户的参数不够32 byte,则用0x5A填充。

void EEPROMData_Check(void)//上电读取flash后,检查数据是否在合理范围内,如不合理,立刻纠正。
{
	if((user_data1<50)||(user_data1>100))
        user_data1=80;

}

void Erase_Flash_Page(u32 FLASH_DATA_ADDRESS)//整页擦除
{
	u32 Page;
	u32	F;
	Page=(FLASH_DATA_ADDRESS-FLASH_BASE)/FLASH_PAGE_SIZE;
	
	FLASH_EraseInitTypeDef	FLash;
	HAL_FLASH_Unlock();
	__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP|FLASH_FLAG_WRPERR|FLASH_FLAG_PGAERR);
	FLash.TypeErase=FLASH_TYPEERASE_PAGES;
	FLash.Banks=FLASH_BANK_1;
	FLash.Page=Page;
	FLash.NbPages=1;
	HAL_FLASHEx_Erase(&FLash, &F);
	HAL_FLASH_Lock();
}


void Read_Flash(void)//上电后读flash
{
	u16 i;
	R_Flash_i=0;
	for(i=0;i<d_FLASH_Page_Size;i+=d_FLASH_Data_maxbyte)
	{
		if(((FLASH_DATA_typedef*)d_FLASH_DATA_ADDRESS)->R_Flash8[i]==0xA5)
		{
			R_Flash_i=i;
			break;
		}
	}
	if(((FLASH_DATA_typedef*)d_FLASH_DATA_ADDRESS)->R_Flash8[0]!=0xA5 && R_Flash_i==0)
	{
		Erase_Flash_Page(d_FLASH_DATA_ADDRESS);
	}
	else
	{
		for(i=0;i<d_FLASH_Data_maxbyte;i++)
		{
			*FLASH_Data[i]=((FLASH_DATA_typedef*)d_FLASH_DATA_ADDRESS)->R_Flash8[R_Flash_i+i];
		}
	}
	EEPROMData_Check();//检查数据是否合理,不合理直接赋值
}


void Write_Flash(void)//需要保存时,调用这个函数
{
	u16 i,j;
	
	R_0xA5=0xA5;
	R_0xFF=0xFF;
	
	for(i=0;i<d_FLASH_Data_maxbyte;i++)
	{
		if(*FLASH_Data[i]!=((FLASH_DATA_typedef*)d_FLASH_DATA_ADDRESS)->R_Flash8[R_Flash_i+i])
		{
			HAL_FLASH_Unlock();
			__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP|FLASH_FLAG_WRPERR|FLASH_FLAG_PGAERR);
			
			HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,(uint32_t)(&(((FLASH_DATA_typedef*)d_FLASH_DATA_ADDRESS)->R_Flash64[R_Flash_i/8])),0);
			
			R_Flash_i+=d_FLASH_Data_maxbyte;
			
			if(R_Flash_i+d_FLASH_Data_maxbyte>=d_FLASH_Page_Size)
			{
				R_Flash_i=0;
				FLASH_EraseInitTypeDef	FLash;
				u32	F;
				FLash.TypeErase=FLASH_TYPEERASE_PAGES;
				FLash.Banks=FLASH_BANK_1;
				FLash.Page=(d_FLASH_DATA_ADDRESS-FLASH_BASE)/FLASH_PAGE_SIZE;
				FLash.NbPages=1;
				HAL_FLASHEx_Erase(&FLash, &F);
			}
			for(i=0;i<d_FLASH_Data_maxDWord;i++)
			{
				j=i*8;
				HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,(uint32_t)(&(((FLASH_DATA_typedef*)d_FLASH_DATA_ADDRESS)->R_Flash64[R_Flash_i/8+i])),\
				(uint64_t)*FLASH_Data[j+7]<<56 | (uint64_t)*FLASH_Data[j+6]<<48 | (uint64_t)*FLASH_Data[j+5]<<40 | (uint64_t)*FLASH_Data[j+4]<<32 | (uint64_t)*FLASH_Data[j+3]<<24 | (uint64_t)*FLASH_Data[j+2]<<16 | (uint64_t)*FLASH_Data[j+1]<<8 | (uint64_t)*FLASH_Data[j]);
			}
			HAL_FLASH_Lock();
			break;
		}
	}
}

代码使用指南:

1、用户定义需要保存的数据。

2、用户根据需求修改数据长度。

3、用户把保存的数据拆分,放入数组。

4、在程序运行中修改第1步的数据,调用Write_Flash()保存数据。

5、上电调用Read_Flash()读取上次保存的数据,并且判断数据是否合理。

6、例程使用此方法,理论上flash的擦写寿命可以达到(2048/32)*10W=640万次,其中32是用户根据项目需求去定义的数据长度,数据越多,寿命越短。

标签:0x5A,FLASH,u8,擦写,Flash,user,FLash
来源: https://blog.csdn.net/weixin_42401119/article/details/122352854

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有