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是用户根据项目需求去定义的数据长度,数据越多,寿命越短。