STM32读写内部flash

一.简介

在 STM32 芯片内部有一个 FLASH 存储器,它主要用于存储代码,我们在电脑上编写好应用程序后,使用下载器把编译后的代码文件烧录到该内部 FLASH 中,由于 FLASH 存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部 FLASH 中加载代码并运行;除了使用外部的工具(如下载器)读写内部 FLASH 外,STM32 芯片在运行的时候,也能对自身的内部 FLASH 进行读写,因此,若内部 FLASH 存储了应用程序后还有剩余的空间,我们可以把它像外部 SPI-FLASH 那样利用起来,存储一些程序运行时产生的需要掉电保存的数据。

说明:内部flash为NOR Flash

二.内部FLASH的构成

STM32F429为例的内部 FLASH 包含主存储器、系统存储器、OTP 区域以及选项字节区域,它们的地址分布及大小见表:

主存储器:

一般我们说 STM32 内部 FLASH 的时候,都是指这个主存储器区域,它是存储用户应用程序的空间,芯片型号说明中的 1M FLASH、2M FLASH 都是指这个区域的大小。

系统存储区:

系统存储区是用户不能访问的区域,它在芯片出厂时已经固化了启动代码,它负责实现串口、USB 以及 CAN 等 ISP 烧录功能。

OTP 区域:

OTP(One Time Program),指的是只能写入一次的存储区域,容量为 512 字节,写入后数据就无法再更改,OTP 常用于存储应用程序的加密密钥

选项字节:

选项字节用于配置 FLASH 的读写保护、电源管理中的 BOR 级别、软件/硬件看门狗等功能,这部分共 32 字节。可以通过修改 FLASH 的选项控制寄存器修改。

三.对内部 FLASH 的写入过程

1.解锁

由于内部 FLASH 空间主要存储的是应用程序,是非常关键的数据,为了防止误操作修改了这些内容,芯片复位后默认会结 FLASH 上锁,这个时候不允许设置 FLASH 的控制寄存器,并且不能对修改 FLASH 中的内容。

所以对 FLASH 写入数据前,需要先给它解锁。

2.擦除扇区

在写入新的数据前,需要先擦除存储区域,STM32 提供了扇区擦除指令和整个 FLASH 擦除 (批量擦除) 的指令,批量擦除指令仅针对主存储区。

页擦除的过程如下:

(1) 检查 FLASH_SR 寄存器中的"忙碌寄存器位 BSY",以确认当前未执行任何 Flash 操作;

(2) 在 FLASH_CR 寄存器中,将"激活扇区擦除寄存器位 SER "置 1,并设置"扇区编号寄存器位 SNB",选择要擦除的扇区;

(3) 将 FLASH_CR 寄存器中的"开始擦除寄存器位 STRT "置 1,开始擦除;

(4) 等待 BSY 位被清零时,表示擦除完成。

3.写入数据

擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还还需要配置一系列的寄存器,步骤如下:

(1) 检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何其它的内部 Flash 操作;

(2) 将 FLASH_CR 寄存器中的"激活编程寄存器位 PG"置 1;

(3) 针对所需存储器地址(主存储器块或 OTP 区域内)执行数据写入操作;

(4) 等待 BSY 位被清零时,表示写入完成

四.读写内部flash代码示例

1.internalFlash.h

#ifndef __INTERNAL_FLASH_H

#define __INTERNAL_FLASH_H

#include "stm32f4xx.h"

/* Base address of the Flash sectors */

#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base address of Sector 0, 16 Kbytes */

#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base address of Sector 1, 16 Kbytes */

#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base address of Sector 2, 16 Kbytes */

#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base address of Sector 3, 16 Kbytes */

#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base address of Sector 4, 64 Kbytes */

#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base address of Sector 5, 128 Kbytes */

#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base address of Sector 6, 128 Kbytes */

#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base address of Sector 7, 128 Kbytes */

int InternalFlash_Test(void);

#endif /* __INTERNAL_FLASH_H */

2.internalFlash.c

#include "internalFlash.h"

/*准备写入的测试数据*/

#define DATA_32 ((uint32_t)0x87654321)

/* Exported types ------------------------------------------------------------*/

/* Exported constants --------------------------------------------------------*/

/* 要擦除内部FLASH的起始地址 */

#define FLASH_USER_START_ADDR ADDR_FLASH_SECTOR_5

/* 要擦除内部FLASH的结束地址 */

#define FLASH_USER_END_ADDR ADDR_FLASH_SECTOR_7

static uint32_t GetSector(uint32_t Address);

/**

* @brief InternalFlash_Test,对内部FLASH进行读写测试

* @param None

* @retval None

*/

int InternalFlash_Test(void)

{

/*要擦除的起始扇区(包含)及结束扇区(不包含),如8-12,表示擦除8、9、10、11扇区*/

uint32_t FirstSector = 0;

uint32_t NbOfSectors = 0;

uint32_t SECTORError = 0;

uint32_t Address = 0;

__IO uint32_t Data32 = 0;

__IO uint32_t MemoryProgramStatus = 0;

static FLASH_EraseInitTypeDef EraseInitStruct;

/* FLASH 解锁 ********************************/

/* 使能访问FLASH控制寄存器 */

HAL_FLASH_Unlock();

FirstSector = GetSector(FLASH_USER_START_ADDR);

NbOfSectors = GetSector(FLASH_USER_END_ADDR)- FirstSector + 1;

/* 擦除用户区域 (用户区域指程序本身没有使用的空间,可以自定义)**/

/* Fill EraseInit structure*/

EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;

EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;/* 以"字"的大小进行操作 */

EraseInitStruct.Sector = FirstSector;

EraseInitStruct.NbSectors = NbOfSectors;

/* 开始擦除操作 */

if (HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) != HAL_OK)

{

/*擦除出错,返回,实际应用中可加入处理 */

return -1;

}

/* 以"字"的大小为单位写入数据 ********************************/

Address = FLASH_USER_START_ADDR;

while (Address < FLASH_USER_END_ADDR)

{

if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, DATA_32) == HAL_OK)

{

Address = Address + 4;

}

else

{

/*写入出错,返回,实际应用中可加入处理 */

return -1;

}

}

/* 给FLASH上锁,防止内容被篡改*/

HAL_FLASH_Lock();

/* 从FLASH中读取出数据进行校验***************************************/

/* MemoryProgramStatus = 0: 写入的数据正确

MemoryProgramStatus != 0: 写入的数据错误,其值为错误的个数 */

Address = FLASH_USER_START_ADDR;

MemoryProgramStatus = 0;

while (Address < FLASH_USER_END_ADDR)

{

Data32 = *(__IO uint32_t*)Address;

if (Data32 != DATA_32)

{

MemoryProgramStatus++;

}

Address = Address + 4;

}

/* 数据校验不正确 */

if(MemoryProgramStatus)

{

return -1;

}

else /*数据校验正确*/

{

return 0;

}

}

/**

* @brief 根据输入的地址给出它所在的sector

* 例如:

uwStartSector = GetSector(FLASH_USER_START_ADDR);

uwEndSector = GetSector(FLASH_USER_END_ADDR);

* @param Address:地址

* @retval 地址所在的sector

*/

static 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/*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_23))*/

{

sector = FLASH_SECTOR_7;

}

return sector;

}

相关推荐
嵌入式大圣1 小时前
STM32 单片机最小系统全解析
stm32·单片机·嵌入式硬件
LN花开富贵6 小时前
stm32g431rbt6芯片中VREF+是什么?在电路中怎么设计?
笔记·stm32·单片机·嵌入式硬件·学习
qq21084629536 小时前
【stm32笔记】使用rtt-studio与stm32CubeMx联合创建项目
笔记·stm32·嵌入式硬件
CV金科6 小时前
蓝桥杯—STM32G431RBT6按键的多方式使用(包含软件消抖方法精讲)从原理层面到实际应用(一)
stm32·单片机·嵌入式硬件·蓝桥杯
2021.096 小时前
五、CAN总线
嵌入式硬件
luckyluckypolar6 小时前
STM32——输入捕获
stm32·单片机·嵌入式硬件·物联网
hong1616886 小时前
嵌入式硬件基础知识
嵌入式硬件
hai405876 小时前
单片机(Microcontroller)原理及应用
单片机·嵌入式硬件
jun7788957 小时前
嵌入式硬件基础知识
嵌入式硬件
Projectsauron8 小时前
STM32 芯片启动过程
stm32·单片机·芯片启动过程