运用 stm32f3xx,需求存储一些掉电不丢掉的校准信息,查阅手册得知:1、stm32 写 flash 的长度是固定的 16bit;2、擦除时有必要整块(2Kbytes)擦除,给出某 flash 块内的地址,履行擦除指令就能够了;3、参考手册给出了最小擦写次数为 10K。
以上三点关于实际运用时的影响,首要,写数据有必要以 16bit 为单位,许多 32bit 长度的值就不能直接运用类似 A = B 的赋值句子的办法去操作了,能够一致转化为指向 16bit 无符号整形值的指针来处理。举例,有一个 32bit 长度的 float 变量 v_float,要存入地址为 (FLASH_ADDRESS)的 flash中:
#define FLASH_ADDRESS (0x0803F800)
if(FLASH->CR &= FLASH_CR_LOCK) //解锁flash
{
FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY1);
FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY2);
while(FLASH->CR & FLASH_CR_LOCK);
}
while(FLASH->SR &= FLASH_SR_BSY);
FLASH->CR |= FLASH_CR_PG; //功用挑选,写flash
{
*((unsigned short *)(FLASH_ADDRESS) + 0 ) = *((unsigned short *)(&v_float) + 0 );
*((unsigned short *)(FLASH_ADDRESS) + 1 ) = *((unsigned short *)(&v_float) + 1 );
}
FLASH->CR &= ~FLASH_CR_PG;
FLASH->CR |= FLASH_CR_LOCK; //锁flash
假如需求读取写入到 flash 中的浮点数到 ram 中的变量 a_float,运用如下句子:
a_float = *((float *) FLASH_ADDRESS);
这样,写入和读取就完成了,下面是擦除,依照参考手册的流程来就能够了:
if(FLASH->CR &= FLASH_CR_LOCK) //解锁flash
{
FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY1);
FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY2);
while(FLASH->CR & FLASH_CR_LOCK);
}
while(FLASH->SR &= FLASH_SR_BSY); //Wait until flash not busy
FLASH->CR |= FLASH_CR_PER; //功用挑选,擦除页
FLASH->AR = FLASH_ADDRESS; //写入flash地址
FLASH->CR |= FLASH_CR_STRT; //开端擦除
while(FLASH->SR &= FLASH_SR_BSY); //等候
if(FLASH->SR &= FLASH_SR_EOP)
{
FLASH->SR |= FLASH_SR_EOP; //重置EOP
}
FLASH->CR &= ~FLASH_CR_PER;
FLASH->CR |= FLASH_CR_LOCK; //锁flash
以上代码会擦除 FLASH_ADDRESS 地点的整个 flash 页。
完成少数数据的读写和擦除操作今后,下一步要开端安排 flash 中存储的数据,这样今后阅览和修正都更为便利,我运用类似 stm32 官方寄存器配置文件的办法,用结构体来安排。假定我要将某个人的个人信息贮存在 flash 中地址为(FLASH_ADDRESS)的方位,包含:名字、性别、身高、体重,代码如下:
typedef struct //结构结构体
{
unsigned char name[16];
unsigned char male;
float height;
float weight;
}Personal_Information_TypeDef;
#define FLASH_ADDRESS (0x0803F800) //flash地址
#define Personal_Data ((Personal_Information_TypeDef *) FLASH_ADDRESS )
以上代码在开始地址为(FLASH_ADDRESS)的 flash 中,界说了用于贮存个人信息的结构体指针 Personal_Data,也便是说 Personal_Data 这个指针的值便是 FLASH_ADDRESS,只不过除了这个指针的值以外,咱们还界说了这个指针所指向的数据的结构。要读出这些贮存于 flash 中的值,运用读取结构体指针的办法:
Temp_variable = Personal_Data -> male;
写 flash 的时分,由于每次只能写 16bit,所以除了 short 类型以外,类似 int 和 float 这种 32bit 的数据,都要取地址强制转化为 16bit 类型后再取值最终写入,办法类似一开端的 float 类型数据写入 flash 的操作。我为了操作便利,在 ram 中建立了一个和 flash 内结构相同的结构体,每次需求写入 flash 的时分,就将 ram 中结构体的一切值悉数写入 flash:
typedef struct //结构结构体
{
unsigned char name[16];
unsigned char male;
float height;
float weight;
}Personal_Information_TypeDef;
#define Length (5) //结构体总长(16bit单位)
#define FLASH_ADDRESS (0x0803F800) //flash地址
#define Personal_Data ((Personal_Information_TypeDef *) FLASH_ADDRESS )
Personal_Information_TypeDef Personal_Data_Mirror; //ram中的结构体
/* 写入进程 */
(unsigned short *)WriteAddress = (unsigned short *)Personal_Data;
(unsigned short *)ReadAddress = (unsigned short *)(&Personal_Data_Mirror);
if(FLASH->CR &= FLASH_CR_LOCK) //解锁flash
{
FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY1);
FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY2);
while(FLASH->CR & FLASH_CR_LOCK);
}
FLASH->CR |= FLASH_CR_PG; //功用挑选,写入
while(Length > 0)
{
*WriteAddress = *ReadAddress; //Ram to Flash program, 16bit each
while(FLASH->SR &= FLASH_SR_BSY);
WriteAddress += 1;
ReadAddress += 1;
Length -= 1;
}
FLASH->CR &= ~FLASH_CR_PG; //Clear PG bit
FLASH->CR |= FLASH_CR_LOCK; //Lock flash
以上的写入进程之前,有必要确保要写入的 flash 方位首要擦除过,或者说要确保要写入数据的当地的值为0xFFFFFFFF,不然无法写入,硬件会有标志位来报错。
这样以结构体为单位擦写 flash 的优点是,假如需求修正要贮存的数据数量或类型的话,只需求修正结构体界说就能够了,并且用结构体来办理变量,程序的可读性较好。
最终便是 flash 的擦写次数问题了,最少10k次的擦写寿数,关于某些需求频频更新的内容仍是太少了,比EEPROM 一般的 100k 少了一个数量级,并且即使是改动一个变量,也有必要首要擦除整个 flash 块,更加快了 flash 的耗费。可是 stm32 的 flash 容量仍是不错的,动辄 256Kbytes,所以咱们能够用容量换寿数,
详细思路便是不要在同一个地址重复擦写,写的时分不断的改换地址,写满今后再擦除。比方,需求贮存的结构体长度为 20x16bit,那么一个 2Kbytes 的 flash 页就能够贮存 50 个相同的结构体,那么履行完 50 次写操作今后才需求履行一次擦除操作,flash 的运用寿数随之大为延伸。
仍是以贮存一个结构体为例阐明怎么完成这种贮存办法,首要界说结构体,除了你需求贮存的数据以外,还要额定添加一个变量,用于辨认你当时读写的 flash 地址:
typedef struct //结构结构体
{
unsigned char flag; //用于辨认当时地址的符号
unsigned char name[16];
unsigned char male;
float height;
float weight;
}Personal_Information_TypeDef;
#define Length (6) //结构体总长(16bit单位)
#define FLASH_ADDRESS (0x0803F800) //flash地址
#define Personal_Data ((Personal_Information_TypeDef *) FLASH_ADDRESS )
Personal_Information_TypeDef Personal_Data_Mirror; //ram中的结构体
这部分除了结构体中添加了一个符号(flag)变量以外,其它部分相同,可是思想上,咱们其实是在 flash 中界说了一个结构体数组,只不过没有运用一般的[]来遍历数组变量,取而代之的是直接运用指针来操作。每次写入时,将 flag 变量固定写为 0x00。需求读取 flash 数据时,就能够依据符号变量 flag 的值找到最新的 flash 数据地址:
#define FLASH_ADDRESS_MAX; //最大偏移量,避免跨区块操作
unsigned short FlashAddress_Offset = 0; //用于贮存flash地址偏移量的暂时变量
while( (Personal_Data + FlashAddress_Offset) -> flag == 0x00)
{
FlashAddress_Offset += 1;
if( (FlashAddress_Offset + FlashAddress_Offset) > MAX_OFFSET)
{
break;
}
}
找到写有数据的 flash 地址今后,后继的写操作和读操作和单个结构体的操作类似,写的地址变为:(Personal_Data + FlashAddress_Offset)
读的地址是:
(Personal_Data + FlashAddress_Offset - 1)
详细完成时要注意,结构体的长度要算好,不能呈现两个结构体穿插写入;擦除 flash 需求时刻,此间最好不进行需求读取 flash 的操作。