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;

}

相关推荐
NEWEVA__zzera222 小时前
利用光耦来隔离485芯片与串口引脚,实现自动收发485电路
单片机·嵌入式硬件
m0_748240542 小时前
STM32第十一课:STM32-基于标准库的42步进电机的简单IO控制(附电机教程,看到即赚到)
stm32·单片机·嵌入式硬件
温柔的男孩像海洋丶2 小时前
vscode的keil assistant 中搜索不到全局变量
ide·vscode·单片机
沐欣工作室_lvyiyi3 小时前
基于单片机的多功能智能小车(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·单片机毕业设计
鸿喵小仙女3 小时前
C# WPF读写STM32/GD32单片机Flash数据
stm32·单片机·c#·wpf
lucy153027510794 小时前
MCU 功耗基准测试
科技·单片机·嵌入式硬件·智能家居·信号处理·工控主板
m0_748240914 小时前
OpenMV与STM32通信全面指南
stm32·单片机·嵌入式硬件
Cchengzu7 小时前
阿里巴巴2017实习生笔试题(二)
stm32·单片机·嵌入式硬件
重生之我是数学王子10 小时前
单片机 STM32入门
stm32·单片机·嵌入式硬件
qq_4597300314 小时前
4-3 MCU中ARM存储器的作用
arm开发·单片机·嵌入式硬件