在嵌入式系统中,实现数据的掉电存储通常是为了确保关键数据在系统断电或重启时不会丢失。
实现方式:数据量不大时将数据保存在片内flash中。(注意flash的读写寿命)
一、STM32内部flash简介
STM32 芯片内部有一个 FLASH 存储器,它主要用于存储代码。

除了使用外部的工具(如下载器)读写内部 FLASH 外, STM32 芯片在运行的时候,也能对自身 的内部 FLASH 进行读写,因此,若内部 FLASH 存储了应用程序后还有剩余的空间,我们可以把 它像外部 SPI-FLASH 那样利用起来,存储一些程序运行时产生的需要掉电保存的数据。
由于访问内部 FLASH 的速度要比外部的 SPI-FLASH 快得多,所以在紧急状态下常常会使用内部 FLASH 存储关键记录;为了防止应用程序被抄袭,有的应用会禁止读写内部 FLASH 中的内容或者在第一次运行时计算加密信息并记录到某些区域,然后删除自身的部分加密代码,这些应用都涉及到内部 FLASH 的操作。
一、内部flash的构成:
STM32 的内部 FLASH 包含主存储器、系统存储器以及选项字节区域,它们的地址分布及大小见 表 STM32 大容量产品内部 FLASH 的构成 。

flash的读写过程
1 解锁
由于内部 FLASH 空间主要存储的是应用程序,是非常关键的数据,为了防止误操作修改了这些内容,芯片复位后默认会给控制寄存器 FLASH_CR 上锁,这个时候不允许设置 FLASH 的控制寄存器,从而不能修改 FLASH 中的内容。
所以对 FLASH 写入数据前,需要先给它解锁。解锁的操作步骤如下:
(1) 往 FPEC 键寄存器 FLASH_KEYR 中写入 KEY1 = 0x45670123
(2) 再往 FPEC 键寄存器 FLASH_KEYR 中写入 KEY2 = 0xCDEF89AB
2 页擦除
在写入新的数据前,需要先擦除存储区域, STM32 提供了页擦除指令和整个 FLASH 擦除 的指令,批量擦除指令仅针对主存储区。页擦除的过程如下:
(1) 检查 FLASH_SR 寄存器中的“忙碌寄存器位 BSY ”,以确认当前未执行任何 Flash 操作;
(2) 在 FLASH_CR 寄存器中,将“激活页擦除寄存器位 PER ”置 1 。
(3) 用 FLASH_AR 寄存器选择要擦除的页;
(4) 将 FLASH_CR 寄存器中的“开始擦除寄存器位 STRT ”置 1 ,开始擦除;
(5) 等待 BSY 位被清零时,表示擦除完成。
3 写入数据
擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还还需要配置一系列的寄存器,步骤如下:
(1) 检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何其它的内部 Flash 操作;
(2) 将 FLASH_CR 寄存器中的“激活编程寄存器位 PG ”置 1 ;
(3) 向指定的 FLASH 存储器地址执行数据写入操作,每次只能以 16 位的方式写入;
(4) 等待 BSY 位被清零时,表示写入完成。
三、STM32内部flash的验证
实现功能:创建1个变量,将变量的值写入flash中,两个按键分别控制变量的增加和减小,对百年变量的值进行修改,然后断电观察打印的变量的值是不是最后设定的值。
写入流程:检查状态–解锁–擦除–写入–上锁。
读出流程:解锁–读取–上锁
1. flash写入函数:
voidflash_write(void){/* 解锁 */FLASH_Unlock();/* 清空所有标志位 */FLASH_ClearFlag;/* 擦除 */FLASHStatus = FLASH_ErasePage(Address);/* 向内部FLASH写入数据 */while//判断是否擦除成功{ FLASHStatus = FLASH_ProgramWord;//写入数据}/* 上锁 */FLASH_Lock(); }
2. flash读出函数:使用指针寻址读出地址的内容
//读取flash的值FLASH_Unlock(); data = (*(__IOuint32_t*) Address); FLASH_Lock();
3. 整体代码:
#include"led.h"#include"delay.h"#include"sys.h"#include"usart.h"#include"key.h"uint32_tdata =1000;//定义一个变量,4个字节uint32_tAddress =0x08008000;//记录写入的地址typedefenum{ FAILED =0, PASSED = !FAILED} TestStatus;FLASH_Status FLASHStatus = FLASH_COMPLETE;//记录每次擦除的结果TestStatus MemoryProgramStatus = PASSED;//记录整个测试结果voidflash_write(void){/* 解锁 */FLASH_Unlock();/* 清空所有标志位 */FLASH_ClearFlag;/* 擦除 */FLASHStatus = FLASH_ErasePage(Address);/* 向内部FLASH写入数据 */while//判断是否擦除成功{ FLASHStatus = FLASH_ProgramWord;//写入数据}/* 上锁 */FLASH_Lock(); }//按下key1,data减小10voidkey1_handler(void){ data +=10; flash_write();}//按下key2,data增加10voidkey2_handler(void){ data -=10; flash_write();}u16 count=0;intmain(void){ delay_init();//延时函数初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2uart_init(115200);//串口初始化为115200LED_Init();//初始化与LED连接的硬件接口KEY_Init();printf(" STM32 flash 读写测试 ");//读取flash的值FLASH_Unlock(); data = (*(__IOuint32_t*) Address); FLASH_Lock();while(1) { u8 t=0;if(0==count%1) { t=KEY_Scan(0);//得到键值switch(t) {case1: { key1_handler(); }break;case2: { key2_handler(); }break;default:break; } }if(0==count%1000) {printf("The duchu number is: %d", data); }if(count ==10000) { delay_ms(1); count =1; } delay_ms(1); count +=1; } }
4. 实现效果:
通过按键修改数值为:6160

重新断电重启,观察数值:

可以看到数值就是最后保存的数值。