【HAL库】STM32CubeMX开发----STM32F407----目录
一、STM32存储器介绍
STM32存储器分为以下两种:
1. 随机存储器---RAM
- RAM是与CPU直接交换数据的内部存储器,也叫主存(内存)。
- 它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。
- 当电源关闭时RAM不能保留数据(掉电数据消失哦)如果需要保存数据,就必须把它们写入一个长期的存储设备中(例如硬盘)。
2. 只读存储器---ROM
- ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地、方便地加以改写。
- ROM所存数据稳定,断电后所存数据也不会改变。
二、STM32F407系列存储器介绍
按照内存,具体分类如下:
型号 | ROM容量(字节) | RAM容量(字节) |
---|---|---|
stm32f407xE | 512K | 192K |
stm32f407xG | 1024K | 192K |
本文使用的是STM32F407VET6芯片,keil5环境下默认的内存配置见下图:
- ROM区域 是0x8000000开始,大小是512K字节。
- RAM区域 是0x20000000开始,大小是192K字节。
内部Flash基本知识
内部Flash就是STM32存储器的ROM区域,掉电数据不丢失,但是Flash在STM32中比较重要,程序也是保存在这个地方,所以轻易不让用户进行随意的读写,以避免不必要的问题。
内部Flash模块构成如下:
内部Flash的擦除功能是一个扇区一个扇区的擦除。
三、内部Flash的读取
对于内部Flash的读取操作比较简单,可以直接指针寻址读取数据。
具体代码如下:
c
/**
*@功能:从内部Flash读取指定字节数据
*@参数1:ReadAddress:数据起始地址
*@参数2:*data: 读取到的数据缓存首地址
*@参数3:length: 读取字节个数
*/
void ReadFlashData(uint32_t ReadAddress, uint8_t *data, uint32_t length)
{
for(uint32_t i=0;i<length;i++)
{
data[i]=*(uint8_t*)(ReadAddress+i); //读取数据
}
}
四、内部Flash的写入
内部Flash 写入数据之前需要判断以前的数据是否为0xFF,如果不是0xFF,需要擦除,才可以正确写入新的数据。
例如:
上一次存数据为0x52,下次直接存储数据0x01。
如果不擦除,就写入数据,结果读出来就是0x51。
由于STM32F407的擦除操作,最少是扇区擦除 。所以我们写入数据也要一整扇区的写入,不可以分多次对一扇区写入数据,因为下一次的写入擦除会把上一次的数据擦除。
1. 具体操作步骤
- 解锁Flash写保护。
- 擦除Flash数据。
- 写入Flash数据。
- 重新锁定Flash写保护。
2. HAL库函数
(1) Flash解锁和锁定函数
c
HAL_FLASH_Unlock(); //解锁
HAL_FLASH_Lock(); //上锁
(2) Flash擦除函数
擦除函数
c
/*
功能:Flash擦除
参数1:擦除操作的配置信息
参数2:错误扇区的配置信息
*/
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError);
用于擦除操作的配置信息
c
typedef struct
{
uint32_t TypeErase; /*擦除类型:FLASH_TYPEERASE_SECTORS 扇区擦除和 FLASH_TYPEERASE_MASSERASE 批量擦除*/
uint32_t Banks; /*当启用批量擦除时,选择要擦除的存储体。*/
uint32_t Sector; /*当启用扇区擦除时,要擦除的初始FLASH扇区 */
uint32_t NbSectors; /*要擦除的扇区数。此参数的值必须介于1和(最大扇区数-初始扇区的值)之间*/
uint32_t VoltageRange;/*定义擦除并行性的设备电压范围。*/
} FLASH_EraseInitTypeDef;
(3) Flash写操作函数
c
/*
功能:在指定地址写入数据
参数1:写入数据类型: FLASH_TYPEPROGRAM_BYTE - 8bit,
FLASH_TYPEPROGRAM_HALFWORD - 16bit
FLASH_TYPEPROGRAM_WORD - 32bit
FLASH_TYPEPROGRAM_DOUBLEWORD - 64bit
参数2:指定要写入的地址
参数3:指定要写入的数据
*/
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);
(4) Flash等待函数
c
//等待FLASH操作完成
HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout);
3. 具体程序
c
#define FMC_FLASH_BASE 0x08000000 // FLASH的起始地址
#define FMC_FLASH_END 0x08080000 // FLASH的结束地址
#define FLASH_WAITETIME 50000 //FLASH等待超时时间
//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
//读取指定地址的字(32位数据)
//faddr:读地址
//返回值:对应数据.
static uint32_t STMFLASH_ReadWord(uint32_t faddr)
{
return *(uint32_t*)faddr;
}
//获取某个地址所在的flash扇区
//addr:flash地址
//返回值:0~11,即addr所在的扇区
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;
}
/**
*@功能:向内部Flash写入数据
*@参数1:WriteAddress:数据要写入的目标地址(偏移地址)
*@参数2:*data: 写入的数据首地址
*@参数3:length:写入数据的个数
*/
void WriteFlashData(uint32_t WriteAddress, uint8_t *data, uint32_t length)
{
FLASH_EraseInitTypeDef FlashEraseInit;
HAL_StatusTypeDef FlashStatus=HAL_OK;
uint32_t SectorError=0;
uint32_t addrx=0;
uint32_t endaddr=0;
if( (WriteAddress < FMC_FLASH_BASE) || ( WriteAddress + length >= FMC_FLASH_END) || (length <= 0) )
return;
HAL_FLASH_Unlock(); //解锁
addrx = WriteAddress; //写入的起始地址
endaddr = WriteAddress+length; //写入的结束地址
while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
{
if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
{
FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型,扇区擦除
FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx); //要擦除的扇区
FlashEraseInit.NbSectors=1; //一次只擦除一个扇区
FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围,VCC=2.7~3.6V之间!!
if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
{
break;//发生错误了
}
}else addrx+=1;
FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
}
FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
if(FlashStatus==HAL_OK)
{
while(WriteAddress<endaddr)//写数据
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE,WriteAddress,*data)!=HAL_OK)//写入数据
{
break; //写入异常
}
WriteAddress+=1;
data++;
}
}
HAL_FLASH_Lock(); //上锁
}