FLASH简单的读、写操作
主存储器是以页为单位划分的。stm32根据FLASH主存储块容量、页面的不同,系统存储器的不同,分为小容量、中容量、大容量、互联型,共四类产品。
- 主存储块用于保存具体的程序代码和用户数据。起始地址0x08000000,b0和b1接GND从这里开始执行程序。
- 信息块用于负责由stm32出厂时放置2KB的启动程序(BootLoader)和512B的用户配置信息区。启动程序代码,是用来存储 ST 自带的启动程序,b0接3.3v,b1接GND运行的就是这部分代码 ,用作串口下载代码。
- 闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构 。在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理 。
操作流程(详见STM32F1 开发指南(精英板-库函数版) )
- 检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁
- 检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作
- 设置 FLASH_CR 寄存器的 PER 位为’ 1’
- 用 FLASH_AR 寄存器选择要擦除的页
- 设置 FLASH_CR 寄存器的 STRT 位为’ 1’
- 等待 BSY 位变为’ 0’
- 读出被擦除的页并做验证
库函数级的操作流程
- 确定要写入Flash的首地址(稍后介绍确定地址的方法)
- 解锁Flash
- 对Flash进行操作(写入数据)
- 对Flash重新上锁
要想选定安全的Flash地址进行读写,可以根据自己的STM32 MCU型号,查找数据手册,确定FLASH的地址区段,因为起始段会存储代码,所以一定要避开起始段,以避免数据错误。(我一般是根据Flash大小计算Flash的最末尾地址,往前推一段地址空间,在这里一般不会对代码中的数据产生覆盖等影响)这里采用的是STM32F103(大容量产品)写入的首地址为0x0807F800;
- 添加STM32 flash底层固件库文件stm32f10x_flash.c
编写自己的Flash驱动库文件,如下
Flash.h
#ifndef __FLASH_H
#define __FLASH_H
#include "sys.h"
void FLASH_W(u32 add,u16 dat);
u16 FLASH_R(u32 add);
#endif
Flash.c
#include "flash.h"
//FLASH写入数据
void FLASH_W(u32 add,u16 dat){ //参数1:32位FLASH地址。参数2:16位数据
// RCC_HSICmd(ENABLE); //打开HSI时钟
FLASH_Unlock(); //解锁FLASH编程擦除控制器
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);//清除标志位
FLASH_ErasePage(add); //擦除指定地址页
FLASH_ProgramHalfWord(add,dat); //从指定页的addr地址开始写
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);//清除标志位
FLASH_Lock(); //锁定FLASH编程擦除控制器
}
//FLASH读出数据
u16 FLASH_R(u32 add){ //参数1:32位读出FLASH地址。返回值:16位数据
u16 a;
a = *(u16*)(add);//从指定页的addr地址开始读
return a;
}
main.c
#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "flash.h"
#define FLASH_START_ADDR 0x0807F800 //写入的起始地址
int main (void){//主程序
u8 key;
u16 a; //定义变量
//初始化程序
RCC_Configuration(); //时钟设置
LED_Init();//LED初始化
KEY_Init();//按键初始化
a = FLASH_R(FLASH_START_ADDR);//从指定页的地址读FLASH
//GPIO_Write(LEDPORT,a); //直接数值操作将变量值写入LED(LED在GPIOB组的PB0和PB1上)
//主循环
while(1){
key = KEY_Scan(0);
if(key == WK_UP_PRES){
a++;
if(a>1)a=0;
FLASH_W(FLASH_START_ADDR,a);
}else{
delay_ms(10);
}
LED0=a;
}
}
Flash操作注意事项:
- 操作一定要先擦后写
- 每页是1024个地址,起始地址0x08000000
- 擦除操作以页为单位,写操作必须以16位宽度为单位,允许跨页写入。
- STM32内置Flash擦或写时,必须打开外部/内部高速振荡器。
- Flash可最多擦写10万次,不可死循环擦写。
- 擦写时要避开用户程序存储的区域,否则会擦掉用户程序导致错误。
- 擦除一页要10ms(对于1k大小的一页),比较慢,而且不能单个字节的擦写。