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