stm32的产品都有内置Flash,而且不同系列的产品其内置Flash的大小不尽相同,结构上也有差异,本文将对stm32f07x,stm32f10x,stm32f40x的内置Flash结构,以及如何进行读写操作做一个介绍。
一、特性与构成
1.stm32f07x系列


2、stm32f10x系列





3、stm32f40x系列


二、对内部Flash的读写操作
2.1 stm32不同系列的产品,内部Flash的特性与构成确实存在一定的差异,但是对于读写操作,步骤一致,比如要往内部flash写入数据,需要几个步骤:
①、解锁flash
②、擦除
③、写入数据
④、锁住flash
读取Flash的内容,只需要直接读取内存地址的数据即可。
注意:①如果要写入的地址内保存了一些重要的数据,不想丢失的话,应该在擦除之前先把数据读取到缓冲区里,再进行擦除和写入新的数据。在擦除页之后,如果写入的数据不够一页,可以连续写入。
②写入数据之前,应该计算好MCU程序占用的内存,因为MCU的程序也是保存在Flash里,如果写入的数据占用了MCU程序的内存,就会导致程序死掉。而计算MCU占用内存大小的方法可以将它生成为bin文件,从它的属性里可以知道它的大小。具体可以参考:https://www.cnblogs.com/xingboy/p/10818813.html;这里提供简单的操作介绍:
1、首先打开keil工程,点击
,出现以下界面,选择User

2、最下方的Run #1打上勾,后面文本框里输入:fromelf --bin --output .'SL@L.bin' '#L',然后点击OK

3、编译
4、到工程文件夹里找到这个bin文件,右击该文件,点击属性就可以查到这个文件的大小


比如我这个bin文件是16KB+,那么我们从0x0800 0000开始算起,17KB的内存不要动,从17KB的位置开始写入数据就不会影响MCU的程序。需要注意的是,不同的MCU,其每一页的大小都不一样,如开篇提供的三种MCU的Flash构成表格,stm32f07x每一页是2KB,stm32f10x的每一页是1KB,stm32f40x没有页的概念,只有扇区的概念,且扇区的大小也不一定相同,有的是16KB,有的是64KB,有的是128KB。所以虽然读操作的步骤一样,但是编程的时候有些区别。这里以HAL为例:
stm32f07x系列:
/************************************************************************
*函数名称:void Flash_Write(uint32_t address,uint32_t *data,uint8_t size)
*函数功能:往内部Flash里写入数据
*函数形参:address:写入数据的起始地址,data:要写入的数据的源地址,size:大小
*函数返回值:无
*************************************************************************/
void Flash_Write(uint32_t address,uint32_t data)
{
uint32_t PageError = 0;
if(HAL_FLASH_Unlock() != HAL_OK) /*解锁Flash*/
{
printf('Unlock Flash Fail!rn');
}
else
{
printf('Unlock Flash succeed!rn');
}
FLASH_EraseInitTypeDef FLASH_EraseInit;
FLASH_EraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
FLASH_EraseInit.PageAddress = address;
FLASH_EraseInit.NbPages = 1;
if(HAL_FLASHEx_Erase(&FLASH_EraseInit,&PageError) != HAL_OK)
{
printf('Erase Flash Fail!rn');
}
else
{
printf('Erase Flash Succeed!rn');
}
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,address,data);
HAL_FLASH_Lock();
}
其中:
//擦除结构体:
typedef struct
{
uint32_t TypeErase; /*!< TypeErase: Mass erase or page erase.
This parameter can be a value of @ref FLASHEx_Type_Erase */
uint32_t PageAddress; /*!< PageAdress: Initial FLASH page address to erase when mass erase is disabled
This parameter must be a number between Min_Data = FLASH_BASE and Max_Data = FLASH_BANK1_END */
uint32_t NbPages; /*!< NbPages: Number of pagess to be erased.
This parameter must be a value between Min_Data = 1 and Max_Data = (max number of pages - value of initial page)*/
} FLASH_EraseInitTypeDef;
//擦除函数:
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
//Flash编程函数
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);
//锁住Flash
HAL_StatusTypeDef HAL_FLASH_Lock(void);
复制代码
TypeErase是擦除的类型,可以是FLASH_TYPEERASE_PAGES(页擦出)也可以是FLASH_TYPEERASE_MASSERASE(块擦除,可以把Main Flash主存储块全部擦除);
PageAddress是页地址,就是数据要写入的地址,因为我的MCU程序占用16KB内存,那么我从17KB开始写入数据就是0x0800 0000 + (0x400 * 17) = 0x0800 4400;
NbPages擦除多少页;
擦除函数的最后一个形参是一个指针,指向一个变量,这个变量包含了擦除错误的情况下的配置信息(0xFFFFFFFF表示所有的页都被正确擦除)
Flash编程函数:第一个形参写入的方式:FLASH_TYPEPROGRAM_HALFWORD(半字,一次写入16bit),FLASH_TYPEPROGRAM_WORD(字,一次写入32bit),FLASH_TYPEPROGRAM_DOUBLEWORD(双字,一次写入64bit).
第二个形参,写入数据的起始地址,第三个形参是要写入的数据。
stm32f40x系列
uint32_t GetSector(uint32_t Address)
{
uint32_t sector = 0;
if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
{
sector = FLASH_SECTOR_0;
}
else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
{
sector = FLASH_SECTOR_1;
}
else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
{
sector = FLASH_SECTOR_2;
}
else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
{
sector = FLASH_SECTOR_3;
}
else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
{
sector = FLASH_SECTOR_4;
}
else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
{
sector = FLASH_SECTOR_5;
}
else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
{
sector = FLASH_SECTOR_6;
}
else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))
{
sector = FLASH_SECTOR_7;
}
else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))
{
sector = FLASH_SECTOR_8;
}
else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))
{
sector = FLASH_SECTOR_9;
}
else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))
{
sector = FLASH_SECTOR_10;
}
else
{
sector = FLASH_SECTOR_11;
}
return sector;
}
uint32_t start_Add = 0x080E0000;
uint32_t end_Add = 0x080E0000;
//FLASH写入数据测试
uint32_t FlashWriteData(uint8_t *data,uint8_t len)
{
uint32_t UserStartSector;
uint32_t SectorError;
uint32_t i = 0;
FLASH_EraseInitTypeDef pEraseInit;
/*解锁Flash*/
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);
UserStartSector = GetSector(start_Add);
pEraseInit.TypeErase = TYPEERASE_SECTORS;
pEraseInit.Sector = UserStartSector;
pEraseInit.NbSectors = GetSector(end_Add)-UserStartSector+1 ;
pEraseInit.VoltageRange = VOLTAGE_RANGE_3;
if (HAL_FLASHEx_Erase(&pEraseInit, &SectorError) != HAL_OK)
{
/* Error occurred while page erase */
printf('Flash Erase failed!rn');
return (1);
}
printf('Flash Erase succeed!rn');
for(i = 0; i < len; i+= 4 )
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (uint32_t)(start_Add+i), *(uint32_t*)(data+i)) == HAL_OK)
{
/* Check the written value */
if(*(uint32_t *)(data + i) != *(uint32_t*)(start_Add+i))
{
/* Flash content doesn't match SRAM content */
return 2;
}
}
else
{
printf('src data != flash datarn');
return 3;
}
}
HAL_FLASH_Lock(); //锁住Flash
return HAL_OK;
}
2.2 读操作
/***********************************************
*函数名称:uint32_t Flash_Read(uint32_t address)
*函数功能:从内部Flash里读出数据
*函数形参:address:要读出的数据的起始地址
*函数返回值:读取到的数据
************************************************/
uint32_t Flash_Read(uint32_t address)
{
uint32_t retval;
retval = *(uint16_t *)(address);
printf('retval = %xrn',retval);
return retval;
}
总结:如果需要存储的数据并不是很多的时候,没必要外挂一个Flash去存储数据,完全可以充分利用内部Flash的。但是内部Flash对写操作的次数有限制,网上找到的数据是1万次,而读操作的次数就没有限制了。所以当数据量不大,不会频繁修改的时候,完全可以写到内部Flash。
上一篇:stm32相关笔记——ADC部分
下一篇:stm32与BQ4050通讯
推荐阅读最新更新时间:2026-03-25 11:54
- 用于 7VIN 至 16VIN、1.5V 和 1.2V 输出的 LTM4628EV DC/DC 模块稳压器的典型应用电路
- 使用 Analog Devices 的 LTC3728LIGN 的参考设计
- DER-406 - 适用于 A19 灯的 5.76 W 高 PF 非隔离降压-升压型 TRIAC 调光 LED 驱动器
- ADR5045B 5V 输出精密微功率并联模式电压基准的典型应用
- LT3970EDDB-3.42 2.5V 降压转换器的典型应用
- MC78M08BDTG 8V 电流调节器的典型应用
- LT1021DCN8-5 精密电压基准的典型应用
- DER-282 - 100W, 扁平(11 mm), LLC DC-DC转换器
- REF193 低压差开尔文连接电压基准的典型应用电路
- LT3088EM 线性稳压器用于添加软启动的典型应用

stm32驱动屏IC rm68042
STM32 PMSM FOC 简介
基于STM32G031的测试测量训练平台完成虚拟仪器实现
非常经典的关于LLC的杨波博士论文
1CIS223-04TG3M






京公网安备 11010802033920号