STM32G474单片机开发入门(八)内部FLASH详解及读写实战

文章目录

一.概要

STM32G474RET6是一款强大而灵活的微控制器,它的片内Flash存储器可以用来存储有关代码和数据,在实际应用中,我们也需要对这个存储器进行读写操作。

STM32的FLASH主存储块按页组织,有的产品每页1KB,有的能到2KB,页面典型的用途就是用于按页擦除FLASH,STM32G474RET6总共有512 KB空间,每个FLASH页是16K。

本文介绍了STM32G474RET6的FLASH存储空间,FLASH数据的读写,以及读写保护等。

二.内部FLASH地址空间排布

根据用途,STM32片内的FLASH分成两部分:主存储块、信息块。

主存储块:用于存储程序,我们写的程序一般存储在这里,用户还可以存储数据。

信息块又分成两部分:系统存储器、OTP、选项字节。

系统存储器存储用于存放在系统存储器自举模式下的启动程序(BootLoader),当使用ISP方式加载程序时,就是由这个程序执行。这个区域由芯片厂写入BootLoader,然后锁死,用户是无法改变这个区域的。

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

选项字节存储芯片的配置信息及对主存储块的保护信息,主要有写保护字节,读保护字节等。

STM32G474RET6产品主存储块512KB,分成两个Bank, 每个Bank为256K,每个Bank有128页,每页大小为2K,一共有7个扇区。系统存储器56KB(ISP的启动代码),存储空间如下图:

三.内部FLASH主要特色

  1. ‌双BANK架构与灵活映射‌
    采用独立的BANK1和BANK2设计,各256KB(共512KB),通过SYSCFG_MEMRMP寄存器的FB_MODE位可动态映射地址空间(0x08000000或0x08040000)‌。
    默认BANK1映射至0x08000000,用户通常无需修改此配置‌。
  2. ‌高性能与可靠性‌
    每页2KB,每个BANK含256页,支持至少1万次擦写,且在85℃环境下可保证数据保存30年,优于同类产品。集成FPEC(Flash编程与擦除控制器),支持ISP、ICP、IAP等多种编程方式。
  3. ‌安全与保护机制‌
    选项字节存储芯片配置及主FLASH保护信息,系统存储器固化BootLoader(不可修改)‌。
    支持存储块保护功能,通过小信息模块(8字节)定义复位特性和保护策略。
  4. ‌扩展功能支持‌
    支持存储器重映射(如SRAM、FSMC、QUADSPI等),通过SYSCFG_MEMRMP寄存器的MEM_MODE位配置,优化外设访问性能‌。

该芯片的FLASH设计兼顾灵活性、耐久性与安全性,适合需要复杂算法或实时数据处理的场景‌。

四.内部FLASH读写操作

1.FLASH数据读取

内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。

以下是一个示例代码,展示了如何在C语言中读取STM32G474RET6的内部Flash数据

// 假设我们要读取的数据在Flash地址0x8000000的位置

#define FLASH_ADDRESS 0x8000000

uint32_t data = (uint32_t)FLASH_ADDRESS;

2.FLASH数据擦除

STM32G474RET6的FLASH在写入数据前确实需要先进行擦除操作。‌这是因为FLASH存储器的特性决定的,‌与其它FLASH存储器一样,‌STM32G474RET6的FLASH也是按页进行管理的,FLASH可以按扇区擦除,也可以整片擦除。

‌1.解锁Flash控制寄存器‌

通过调用HAL_FLASH_Unlock()函数解除写保护,允许后续擦除操作。

2‌.清除挂起标志‌

使用__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS)清除可能存在的错误标志,避免干扰擦除过程。

3‌.配置擦除参数‌

初始化FLASH_EraseInitTypeDef结构体,设置擦除类型为页擦除(FLASH_TYPEERASE_PAGES),并指定目标页地址(Page)和擦除页数(NbPages)。STM32G474的Flash每页大小为2KB,需根据数据手册确认具体页号范围。

4‌.执行擦除操作‌

调用HAL_FLASHEx_Erase(&eraseConfig, &SectorError)函数,传入配置参数和错误状态变量。需检查返回值和SectorError以确认操作是否成功。

‌5.重新锁定Flash‌

操作完成后通过HAL_FLASH_Lock()重新启用写保护,防止意外修改。

3.FLASH数据写入

1.Flash初始化与解锁‌

调用HAL_FLASH_Unlock()解除写保护,使能编程操作。需注意操作前需关闭全局中断以避免干扰。

通过__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS)清除可能的错误标志。

2.写入参数配置‌

数据需按64位(8字节)对齐写入,最小写入长度为8字节,不足部分需填充。

目标地址需位于有效Flash区域(如BANK1的0x08000000~ 0x0803FFFF或BANK2的0x08040000~0x0807FFFF)。

4.数据写入执行‌

使用HAL_FLASH_Program()函数,指定编程类型(如FLASH_TYPEPROGRAM_DOUBLEWORD)和地址数据。示例代码片段:

c 复制代码
uint64_t data = 0x123456789ABCDEF0; // 64位数据
HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, targetAddress, data);

5.写入后处理‌

校验数据是否成功写入,可通过读取目标地址并与原始数据对比。调用HAL_FLASH_Lock()重新启用写保护,确保Flash安全性。

五.内部FLASH的各种保护

1.写保护

STM32G474RET6单片机的FLASH写保护机制主要通过选项字节(Option Bytes)和硬件寄存器实现,具体可分为以下核心要点:

1.写保护原理‌

‌保护单位‌:以4KB页为最小单位进行写保护,通过WRP0~WRP3共4个选项字节控制‌。每个选项字节的每一位对应一个4KB区域的保护状态(0为保护,1为解除)‌。保护效果‌:被保护的页禁止任何擦除和写入操作,但读操作不受影响(读权限由读保护单独控制)‌。

2.选项字节配置‌

‌关键寄存器‌:通过FLASH_CR寄存器操作选项字节,需先解锁(写入特定序列0x45670123和0xCDEF89AB)。

2.读保护

将Flash设置为读保护的目的,是为了防止其他人通过STLINK等仿真器,将Flash中的程序读取出来(设想一下,你辛辛苦苦研发的产品,别人通过仿真器将程序读取出来,再copy一下产品的硬件,就可以生产),所以可以通过将Flash设置为读保护来保护自己的程序。

可对 Flash 中的用户区域实施读保护,以防不受信任的代码读取其中的数据。读保护分三个级别,具体定义如下:

级别 0:无读保护,将 0xAA 写入读保护选项字节 (RDP) 时,读保护级别即设为 0。

级别 1:存储器读保护,将任意值(分别用于设置级别 0 和级别 2 的 0xAA

和 0xCC 除外)写入 RDP 选项字节时,即激活读保护级别 1。

级别 2:禁止调试/芯片读保护,将 0xCC 写入 RDP 选项字节时,即激活读保护级别 2。

六.FLASH读写例程

1.硬件准备

STLINK接STM32G474RET6开发板,STLINK接电脑USB口。

2.创建工程

如下图所示,打开STM32CubeMX软件,新建工程。

如下图所示,Part Number处输入STM32G474RE,再双击就创建新的工程。

如下图所示,配置下载口引脚,PA13为SWD的SWDIO脚,PA14为SWD的SWCLK脚。

如下图所示,配置系统主频170Mhz,使用内部16MHZ晶振。

如下图所示,配置工程文件名,保存路径,KEIL5工程输出方式。

如下图所示,生成工程。

如下图所示,用Keil5打开工程。

添加代码,FLASH解锁,FLASH写入,FLASH数据读取。

主要代码:

c 复制代码
#define FLASH_USER_START_ADDR   ADDR_FLASH_PAGE_4   /* 从0x08002000地址开始存数据#define ADDR_FLASH_PAGE_4     ((uint32_t)0x08002000)*/
#define FLASH_USER_END_ADDR     (ADDR_FLASH_PAGE_5  - 1)   /*地址存放结束地址为0x080027FF */

#define DATA_32                 ((uint32_t)0x12345678)//定义32bit数据内容
#define DATA_64                 ((uint64_t)0x1234567812345678)//定义64bit数据内容
uint32_t FirstPage = 0, NbOfPages = 0, BankNumber = 0;//FLASH读写操作变量
uint32_t Address = 0, PageError = 0;
__IO uint32_t MemoryProgramStatus = 0;
__IO uint32_t data32 = 0;
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();//SysTick配置成1ms中断

  /* USER CODE BEGIN Init */
  /* Configure the system clock to 170 MHz */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();//内部16MHZ晶振,170MHZ系统主频

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  HAL_FLASH_Unlock();//FLASH解锁
  __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);//清除FLASH所有错误标志
  FirstPage = GetPage(FLASH_USER_START_ADDR);//获取需要写FLASH地址的起始地址的页号,页号是4
  NbOfPages = GetPage(FLASH_USER_END_ADDR) - FirstPage + 1;//获取页数,这边是1页
  BankNumber = GetBank(FLASH_USER_START_ADDR);//获取Bank,这边是BANK1
  EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
  EraseInitStruct.Banks       = BankNumber;
  EraseInitStruct.Page        = FirstPage;
  EraseInitStruct.NbPages     = NbOfPages;
  if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK)//擦除根据定义的FLASH起始地址到结束地址的空间,空间的内容都变成0xff
  {
  }
  Address = FLASH_USER_START_ADDR;
  while (Address < FLASH_USER_END_ADDR)
  {
    if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, Address, DATA_64) == HAL_OK)//根据定义的FLASH起始地址到结束地址,双字写入,一次写入8字节,内容是0x1234567812345678
    {
      Address = Address + 8;//地址加8
    }
   else
    {
    }
  }
  HAL_FLASH_Lock();//FLASH上锁
  Address = FLASH_USER_START_ADDR;
  MemoryProgramStatus = 0x0;
  while (Address < FLASH_USER_END_ADDR)
  {
    data32 = *(__IO uint32_t *)Address;//4字节读取从定义的FLASH起始地址到结束地址的数据
    if (data32 != DATA_32)//读出的内容与写入的内容比较
    {
      MemoryProgramStatus++;//内容不一致,变量加1
    }
    Address = Address + 4;//地址加4
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

3.实验结果

在Keil5下进入调试模式,全速运行,查看Memory内容,输入0x08002000地址,可以看到内容已经变成我们写入的数值。

七.小结

在STM32的开发过程中,保存用户数据、‌实现程序的自我更新等应用场景都离不开对FLASH读写操作,用好内部FLASH,能降低整体产品成本。

相关推荐
学习路上_write4 小时前
STM32回调函数使用/定时器/GPIO/串口/
c语言·单片机·嵌入式硬件
稻草、6 小时前
合泰单片机之时基中断
单片机·嵌入式硬件
盈创力和20076 小时前
物联网 “神经” 之以太网:温湿度传感器的工业级 “高速干道”
运维·服务器·网络·嵌入式硬件·以太网温湿度传感器
GilgameshJSS6 小时前
STM32H743-ARM例程36-DNS
c语言·arm开发·stm32·单片机·嵌入式硬件
工具人55557 小时前
电脑插入USB一个端口都识别不出来
单片机·嵌入式硬件·电脑
KOAN凯擎小妹7 小时前
晶振信号质量:上升下降时间与占空比
单片机·嵌入式硬件·fpga开发·信息与通信
sheepwjl8 小时前
《嵌入式硬件(二十一):基于IMX6ULL的脉冲宽度调制(PWM)操作》
嵌入式硬件·pwm·脉冲宽度调制
小莞尔9 小时前
【51单片机】【protues仿真】基于51单片机简易电子琴系统(8键)
c语言·单片机·嵌入式硬件·物联网·51单片机
盈创力和200711 小时前
技术解析:CO与NO₂双气体监测如何构筑协同化安全防线
嵌入式硬件·安全·以太网温湿度传感器·多参量传感器·温湿度+气体智能传感器
平凡灵感码头11 小时前
STM32 串口中断接收原理与实战详解:从配置到中断服务函数全流程解析
单片机·嵌入式硬件