在嵌入式系统中,实现数据的掉电存储通常是为了确保关键数据在系统断电或重启时不会丢失。
实现方式:数据量不大时将数据保存在片内flash中。(注意flash的读写寿命)
一、STM32内部flash简介
STM32 芯片内部有一个
FLASH
存储器,它主要用于存储代码。
除了使用外部的工具(如下载器)读写内部 FLASH
外,
STM32
芯片在运行的时候,也能对自身 的内部 FLASH
进行读写,因此,若内部
FLASH
存储了应用程序后还有剩余的空间,我们可以把 它像外部 SPI-FLASH
那样利用起来,存储一些程序运行时产生的需要掉电保存的数据。
由于访问内部 FLASH
的速度要比外部的
SPI-FLASH
快得多,所以在紧急状态下常常会使用内部 FLASH 存储关键记录;为了防止应用程序被抄袭,有的应用会禁止读写内部
FLASH
中的内容或者在第一次运行时计算加密信息并记录到某些区域,然后删除自身的部分加密代码,这些应用
都涉及到内部
FLASH
的操作。
内部flash的构成:
STM32 的内部
FLASH
包含主存储器、系统存储器以及选项字节区域,它们的地址分布及大小见 表 STM32
大容量产品内部
FLASH
的构成
(在《
STM32
参考手册》中没有关于其内部
FLASH
的 说明,需要了解这些内容时,要查阅《STM32F10x
闪存编程参考手册》)。
二、STM32内部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写入函数:
void flash_write(void)
{
/* 解锁 */
FLASH_Unlock();
/* 清空所有标志位 */
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
/* 擦除 */
FLASHStatus = FLASH_ErasePage(Address);
/* 向内部FLASH写入数据 */
while(FLASHStatus == FLASH_COMPLETE) //判断是否擦除成功
{
FLASHStatus = FLASH_ProgramWord(Address, data); //写入数据
}
/* 上锁 */
FLASH_Lock();
}
2. flash读出函数:使用指针寻址读出地址的内容
//读取flash的值
FLASH_Unlock();
data = (*(__IO uint32_t*) Address);
FLASH_Lock();
3. 整体显示代码:
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "key.h"
uint32_t data = 1000; //定义一个变量,4个字节
uint32_t Address = 0x08008000; //记录写入的地址
typedef enum
{
FAILED = 0,
PASSED = !FAILED
} TestStatus;
FLASH_Status FLASHStatus = FLASH_COMPLETE; //记录每次擦除的结果
TestStatus MemoryProgramStatus = PASSED; //记录整个测试结果
void flash_write(void)
{
/* 解锁 */
FLASH_Unlock();
/* 清空所有标志位 */
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
/* 擦除 */
FLASHStatus = FLASH_ErasePage(Address);
/* 向内部FLASH写入数据 */
while(FLASHStatus == FLASH_COMPLETE) //判断是否擦除成功
{
FLASHStatus = FLASH_ProgramWord(Address, data); //写入数据
}
/* 上锁 */
FLASH_Lock();
}
//按下key1,data减小10
void key1_handler(void)
{
data += 10;
flash_write();
}
//按下key2,data增加10
void key2_handler(void)
{
data -= 10;
flash_write();
}
u16 count=0;
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init();
printf("\r\n STM32 flash 读写测试 \r\n");
//读取flash的值
FLASH_Unlock();
data = (*(__IO uint32_t*) Address);
FLASH_Lock();
while(1)
{
u8 t=0;
if(0==count%1)
{
t=KEY_Scan(0); //得到键值
switch(t)
{
case 1:
{
key1_handler();
}
break;
case 2:
{
key2_handler();
}
break;
default:
break;
}
}
if(0==count%1000)
{
printf("The duchu
4. 实现效果:
通过按键修改数值为:6160
重新断电重启,观察数值:
可以看到数值就是最后保存的数值。
完整代码:https://download.csdn.net/download/qq_39742246/88582982
相关文章
发表评论