STM32F407IGT6内部Flash使用指南:HAL库操作方法详解
目录
一、STM32F4存储器介绍
二、STM32F407IGT6内部Flash简介
三、FLASH操作流程
四、Flash读写操作代码
1. 写入先前擦除Flash数据
2. 向指定地址写入数据
3. 读取指定地址的数据
4. Flash读写操作代码头文件
五、Debug调试示例
一、STM32F4存储器介绍
STM32存储器分为以下两种:
1. 随机存储器—RAM
RAM是与CPU直接交换数据的内部存储器,也叫主存(内存)。它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。当电源关闭时RAM数据消失,如果需要保存数据,就必须把写入一个长期的存储设备中(如硬盘等)。
2. 只读存储器—ROM
ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地、方便地加以改写。ROM存储数据稳定,断电后所存数据不丢失。
型号 | ROM(字节) | RAM(字节) |
---|---|---|
STM32F407xE | 512Kb | 192Kb |
STM32F407xG | 1024Kb | 192Kb |
本文使用的是STM32F407IGT6芯片,keil5环境下默认的内存配置如下图所示:
由图可知:STM32F407IGT6的ROM区域 是0x8000000开始,大小是512K字节。RAM区域 是0x20000000开始,大小是192K字节。
二、STM32F407IGT6内部Flash简介
STM32内部Flash就是STM32存储器的ROM区域,掉电数据不丢失,但是Flash在STM32中比较重要,程序也是保存在这个地方,所以轻易不让用户进行随意的读写,以避免不必要的问题。
由上图所示的Flash架构图可知,STM32F407IGT6分为主存储器、系统存储器、OTP区域和选项字节四部分。其中:
Flash主存储器,用于存放代码和数据常数(如const 类型的数据)。从上图可以看出主存储器的起始地址就是 0X08000000 。当 B0 、 B1 都接 GND 的时候,就是从 0X08000000 开始运行代码的。
系统存储器,用来存放STM32的 bootloaderr代码,此代码是出厂时就固化在 STM32F4 里面了,专门来给主存储器下载代码。当 B0 接 V3.3 B1 接 GND 的时候,从该存储器启动(即进入串口下载模式)。
OTP 区域,即一次性可编程区域,共 528 字节,被分成两个部分,前面 512 字节( 32 字节为 1 块,分成 16 块),可以用来存储一些用户数据(一次性,写完一次,永远不可以擦除!),后面 16 字节,用于锁定对应块。这里的一次性是指写入一次后,再次写入的话是前后相与的值,两次不相同则会置零 。
字节选项:用于配置读保护、BOR 级别、看门狗以及器件处于待机或停止模式下的复位。
STM32会根据自身Flash容量的大小,将Flash分为每页1K字节或每页2K字节。超过256K容量的每页为2K字节。对于本次使用的SMT32F407IGT6,其容量为1024K,则内部分为每页2K字节。
三、FLASH操作流程
STM32的Flash操作属于嵌入式设备底层操作,可直接对地址进行存取。其操作流程如下:
1. 确定要写入Flash的首地址
2. 解锁Flash写保护
3. 对Flash进行操作(写入数据)
4. 对Flash重新上锁
首地址确定: SMT32F407IGT6的Flash起始地址为0X0800 0000,由于STM32运行代码从地址0X0800 0000开始,因此,使用内部Flash时开始地址应该往后偏移,否则会将程序部分覆盖掉,导致系统死机。本文使用的FLASH范围是0X08010000-0X0801FFFF。
解锁Flash写保护:HAL_FLASH_Unlock( );
对Flash进行写操作:Flash的写操作,需要擦除一整页后再重新写入,不能对特定处进行修改,写的时候可以分多次写入。注意:擦写次数较多数据的不建议使用内部Flash进行存储,手册中给的数据是擦写1W次。
对Flash重新上锁:HAL_FLASH_Lock( );
四、Flash读写操作代码
1. 写入先前擦除Flash数据
擦除Flash内容示例代码:
#include "flash.h"
//清除扇区
//StartAddress:取值范围(0x08000000~0x0807FFFF)
//F407IGT6 1024k 11个扇区
uint32_t sectStartAddr[12]=
{
0x08000000,//0
0x08004000,//1
0x08008000,//2
0x0800c000,//3
0x08010000,//4
0x08020000,//5
0x08040000,//6
0x08060000,//7
0x08080000,//8
0x080A0000,//9
0x080C0000,//10
0x080E0000,//11
};
//获取Sector的编号
int GetSectorFromAddress(uint32_t address)
{
int sect;
if( address < 0x08000000 || address > 0x080FFFFF )
return -1;
for( int i=0; i<11; i++ )
{
if( address >= sectStartAddr[i] && address < sectStartAddr[i+1] )
{
sect = i;
break;
}
}
return sect;
}
void FlashErase(uint32_t StartAddress)
{
int sect = 0;
HAL_FLASH_Unlock();//解锁
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);//清除一些错误标志
sect = GetSectorFromAddress(StartAddress);//获取地址所在的扇区
FLASH_Erase_Sector(4,FLASH_VOLTAGE_RANGE_3);//擦除指定的闪存扇区(0~11)
HAL_FLASH_Lock();//上锁
}
2. 向指定地址写入数据
向Flash写入数据示例代码:
//返回当前要操作的扇区
uint8_t STMFLASH_GetFlashSector(uint32_t addr)
{
if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;
else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;
else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;
else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;
else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;
else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;
else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;
else if(addr<ADDR_FLASH_SECTOR_8)return FLASH_SECTOR_7;
else if(addr<ADDR_FLASH_SECTOR_9)return FLASH_SECTOR_8;
else if(addr<ADDR_FLASH_SECTOR_10)return FLASH_SECTOR_9;
else if(addr<ADDR_FLASH_SECTOR_11)return FLASH_SECTOR_10;
return FLASH_SECTOR_11;
}
//写入Data_flash
void STMFLASH_Write(uint32_t Addr,uint32_t *pBuffer,uint32_t Num)
{
FLASH_EraseInitTypeDef FlashEraseInit;
HAL_StatusTypeDef FlashStatus=HAL_OK;
uint32_t SectorError=0;
uint32_t addrx=0;
uint32_t endaddr=0;
WriteAddr=Addr;
if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //错误地址
HAL_FLASH_Unlock(); //解锁flash
addrx=WriteAddr; //写入起始地址
endaddr=WriteAddr+Num*4; //写入结束地址
if(addrx < 0x08010000)
{
while(addrx<endaddr)
{
if(STM32_FLASH_ReadWord(addrx)!=0XFFFFFFFF)
{
FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //扇区擦除
FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx); //擦除的扇区
FlashEraseInit.NbSectors=1; //擦除1个扇区
FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围2.7-3.6
if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
{
break;//写入错误
}
}
else addrx+=4;
FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待操作完成
}
}
FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待操作完成
if(FlashStatus==HAL_OK)
{
while(WriteAddr<endaddr)//写数据
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)
{
break;
}
WriteAddr+=4;
pBuffer++;
}
}
HAL_FLASH_Lock(); //flash上锁
3. 读取指定地址的数据
读取Flash指定地址的数据示例代码:
//读取指定地址的字(32位数据)
//faddr:读地址
//返回值:对应数据.
uint32_t WriteAddr = 0x08010000;
uint32_t STM32_FLASH_ReadWord(uint32_t faddr)
{
return *(__IO uint32_t*)faddr;
}
//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToRead:字(32位)数
void STM32_FLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead)
{
uint32_t i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=STM32_FLASH_ReadWord(ReadAddr);//读取4个字节.
ReadAddr+=4;//偏移4个`字节.
}
}
4. Flash读写操作代码头文件
Flash读写操作代码头文件
#ifndef __FLASH_H
#define __FLASH_H
#include "main.h"
#define STM32_FLASH_BASE 0x08000000 //flash起始地址
#define FLASH_WAITETIME 50000 //FLASH等待超时时间
void FlashErase(uint32_t StartAddress);
int GetSectorFromAddress(uint32_t address);
void FlashWrite(uint32_t StartAddress,uint32_t data);
uint32_t FlashRead(uint32_t StartAddress);
//FLASH 扇区的起始地址
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) //扇区0起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) //扇区1起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) //扇区2起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) //扇区3起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) //扇区4起始地址, 64 Kbytes
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) //扇区5起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) //扇区6起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) //扇区7起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) //扇区8起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) //扇区9起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) //扇区10起始地址,128 Kbytes
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) //扇区11起始地址,128 Kbytes
//读取数值
uint32_t STM32_FLASH_ReadWord(uint32_t faddr);
//flash写入,参数一为写入的地址,参数二为写入的缓冲区,参数三为写入的数据长度
void STMFLASH_Write(uint32_t Addr,uint32_t *pBuffer,uint32_t Num);
//flash读取
void STM32_FLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead);
extern uint32_t WriteAddr;
#endif
完整代码链接:https://pan.baidu.com/s/1bovod11KH79YByg-lUAR3Q?pwd=1234 提取码: 1234
参考文章:http://t.csdnimg.cn/BfnkW
五、Debug调试示例
//将flash_write_pressure数组中的4个数据存储到WRITE_P_ADDRFlash内存地址中(0x0801 0000)
STMFLASH_Write(WRITE_P_ADDR,(uint32_t*)flash_write_pressure,4);
//从READ_P_ADDR地址读出4个数据存储到flash_read_pressure数组中(0x0801 0000)
STM32_FLASH_Read(READ_P_ADDR,(uint32_t*)flash_read_pressure,4);
作者:FPGA小白菜