问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

STM32F407单片机编程入门:内部FLASH详解及读写实战

创作时间:
作者:
@小白创作中心

STM32F407单片机编程入门:内部FLASH详解及读写实战

引用
CSDN
1.
https://blog.csdn.net/zy2232652/article/details/142356007

STM32F407VET6是一款强大而灵活的微控制器,它的片内Flash存储器可以用来存储有关代码和数据,在实际应用中,我们也需要对这个存储器进行读写操作。本文将详细介绍STM32F407VET6的FLASH存储空间,FLASH数据的读写,以及读写保护等。

一.概要

STM32F407VET6是一款强大而灵活的微控制器,它的片内Flash存储器可以用来存储有关代码和数据,在实际应用中,我们也需要对这个存储器进行读写操作。STM32的FLASH主存储块按页组织,有的产品每页1KB,有的能到2KB,页面典型的用途就是用于按页擦除FLASH,STM32F407的FLASH页大一点,能到16K,我们也叫做扇区。

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

二.内部FLASH地址空间排布

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

  • 主存储块:用于存储程序,我们写的程序一般存储在这里,用户还可以存储数据。
  • 信息块又分成两部分:系统存储器、OTP、选项字节。
  • 系统存储器存储用于存放在系统存储器自举模式下的启动程序(BootLoader),当使用ISP方式加载程序时,就是由这个程序执行。这个区域由芯片厂写入BootLoader,然后锁死,用户是无法改变这个区域的。
  • OTP(One Time Program)区域,指的是只能写入一次的存储区域,容量为528字节,写入后数据就无法再更改,OTP常用于存储应用程序的加密密钥。
  • 选项字节存储芯片的配置信息及对主存储块的保护信息,主要有写保护字节,读保护字节等。

STM32F407VET6产品主存储块512KB, 每个扇区16KB~128K大小不等,一共有7个扇区,系统存储器30KB(启动代码),存储空间如下图:

选项字节内容如下:

三.内部FLASH主要特色

  • 容量大的芯片可以高达 1 MB 容量
  • 128 位宽数据读取
  • 字节、半字、字和双字数据写入
  • 扇区擦除与批量擦除
  • 存储器构成

Flash 结构如下:

  • 主存储器块,含 4 个 16 KB 扇区、1 个 64 KB 扇区 和 7 个 128 KB 扇区。
  • 系统存储器,器件在系统存储器自举模式下从该存储器自举。此区域为意法半导体预留,其中包含自举程序,用以通过以下接口之一对 Flash 进行重新编程:USART1、USART3、CAN2、USB OTG FS 设备模式(DFU:设备固件升级)。自举程序由 ST 在器件制造期间编写,用于防止误写/误擦除操作。
  • 512 OTP(一次性可编程)字节,用于存储用户数据。OTP 区域包含 16 个附加字节,用于锁定相应的 OTP 数据块。
  • 选项字节:读写保护、BOR 级别、软件/硬件看门狗以及器件在待机或停机模式下的复位。

四.内部FLASH读写操作

1.FLASH数据读取

内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。以下是一个示例代码,展示了如何在C语言中读取STM32F407的内部Flash数据

// 假设我们要读取的数据在Flash地址0x8000000的位置
#define FLASH_ADDRESS 0x8000000  
uint32_t data =(uint32_t)FLASH_ADDRESS;  

2.FLASH数据擦除

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

按扇区擦除操作流程:

3.FLASH数据写入

对主存储器数据写入每次可以写入16位。当FLASH_CR寄存器的PG位为’1’时,在一个闪存地址写入一个半字将启动一次编程。

FLASH数据写入操作流程:

五.内部FLASH的各种保护

1.写保护

STM32F407的FLASH写保护可以通过编程特定的选项字节来启用或禁用,以便保护特定的内存区域免受未经授权的写入,用于避免误写/误擦除操作。

Flash 中的用户扇区(0 到 11)具备写保护功能,可防止因程序计数器 (PC) 跑飞而发生意‌外的写操作。当扇区 i 中的非写保护位 (nWRPi, 0 ≤ i ≤ 11) 为低电平时,无法对扇区 i 执行擦除或编程操作。因此,如果某个扇区处于写保护状态,则无法执行批量擦除。

2.读保护

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

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

  • 级别 0:无读保护,将 0xAA 写入读保护选项字节 (RDP) 时,读保护级别即设为 0。
  • 级别 1:存储器读保护,将任意值(分别用于设置级别 0 和级别 2 的 0xAA 和 0xCC 除外)写入 RDP 选项字节时,即激活读保护级别 1。
  • 级别 2:禁止调试/芯片读保护,将 0xCC 写入 RDP 选项字节时,即激活读保护级别 2。

六.FLASH读写例程

硬件准备:

  • STLINK接STM32F407VET6开发板,STLINK接电脑USB口。

打开STM32CubeMX软件,新建工程

  • Part Number处输入STM32F407VE,再双击就创建新的工程
  • 配置下载口引脚
  • 配置外部晶振引脚

  • 配置系统主频168Mhz,使用外部晶振
  • 配置工程文件名,保存路径,KEIL5工程输出方式
  • 生成工程
  • 用Keil5打开工程
  • 添加代码,FLASH解锁,FLASH写入,FLASH数据读取

主要代码

#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
#define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_1  	/*内容写入起始地址*/
#define FLASH_USER_END_ADDR     ADDR_FLASH_SECTOR_2  -1 /*内容写入结束地址*/
#define DATA_32                 ((uint32_t)0x12345678)
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint32_t FirstSector = 0, NbOfSectors = 0, Address = 0;
uint32_t SectorError = 0;
__IO uint32_t data32 = 0 , MemoryProgramStatus = 0;
/*Variable used for Erase procedure*/
static FLASH_EraseInitTypeDef EraseInitStruct;
   
/* Private function prototypes -----------------------------------------------*/
static void SystemClock_Config(void);
static void Error_Handler(void);
static uint32_t GetSector(uint32_t Address);
static uint32_t GetSectorSize(uint32_t Sector);
void SystemClock_Config(void);
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();
  /* USER CODE BEGIN Init */
  /* USER CODE END Init */
  /* Configure the system clock */
  SystemClock_Config();
  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
 /* Unlock the Flash to enable the flash control register access *************/ 
  HAL_FLASH_Unlock();//解锁
  /* Erase the user Flash area
    (area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR) ***********/
  /* Get the 1st sector to erase */
  FirstSector = GetSector(FLASH_USER_START_ADDR);
  /* Get the number of sector to erase from 1st sector*/
  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)//扇区删除
  { 
    Error_Handler();
  }
  __HAL_FLASH_DATA_CACHE_DISABLE();
  __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
  __HAL_FLASH_DATA_CACHE_RESET();
  __HAL_FLASH_INSTRUCTION_CACHE_RESET();
  __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
  __HAL_FLASH_DATA_CACHE_ENABLE();
  /* Program the user Flash area word by word
    (area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR) ***********/
  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
    { 
      Error_Handler();
    }
  }
  HAL_FLASH_Lock(); 
  Address = FLASH_USER_START_ADDR;
  MemoryProgramStatus = 0x0;
  
  while (Address < FLASH_USER_END_ADDR)
  {
    data32 = *(__IO uint32_t*)Address;//数据读取
    if (data32 != DATA_32)
    {
      MemoryProgramStatus++;  
    }
    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 */
}
  

七.CubeMX工程源代码下载

通过网盘分享的文件:9.内部FLASH读写.zip

链接: https://pan.baidu.com/s/1v8VKrk2FrxhvSvYG8-utOQ 提取码: zhdu

如果链接失效,可以联系博主给最新链接

程序下载下来之后解压就行

八.小结

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

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号