如何存取STM32内置flash

目录

描述

代码

HAL函数及结构体介绍

结构FLASH_EraseInitTypeDef

结构体定义

字段说明

擦除函数

编程(写入)函数

解锁函数

上锁函数

取得FLASH错误标志

FLASH的空闲区间在哪里?


描述

        复位后,闪存写入/擦除控制器处于锁定状态。要解锁它,请使用 FLASH_Unlock 函数。
在对所需地址进行写入之前,使用闪存擦除扇区功能执行擦除操作。是否有一个疑问,为什么要擦除才能写入,复杂的物理结构不提,简单的说,就是写入操作时可以把0->1,但无法从1->0.而且擦除的最小单元是扇区.通过填充擦除初始化结构来编程擦除过程(从第一个扇区到计算出的扇区数量进行扇区擦除)
然后通过调用 HAL_FLASHEx_Erase 函数逐个擦除所有这些扇区。
注意:如果某个扇区出现问题,擦除操作将停止,并将故障扇区返回给用户(通过变量“SectorError”)。
一旦此操作完成,将使用 HAL_FLASH_Program 函数执行写入操作。随后会检查写入的数据,并将编程操作的结果存储到 MemoryProgramStatus 变量中。

代码


/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_2   /* 开始写入的区域 */
#define FLASH_USER_END_ADDR     ADDR_FLASH_SECTOR_5  +  GetSectorSize(ADDR_FLASH_SECTOR_5) -1 /* 用户闪存区域的结束位置 : sector start address + sector size -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);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{

  HAL_Init();


  /* Configure the system clock to 84 MHz */
  SystemClock_Config();

  /* 解锁FLASH可以访问控制器寄存器 *************/ 
  HAL_FLASH_Unlock();

  /* 取得擦除的第一个扇区 */
  FirstSector = GetSector(FLASH_USER_START_ADDR);
  /* 从第一个扇区开始擦除的扇区数量*/
  NbOfSectors = GetSector(FLASH_USER_END_ADDR) - FirstSector + 1;

  /* 填写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)
  { 
    /* 
        在执行扇区擦除操作时发生错误。
        用户可以在此处添加一些代码来处理此错误。
        “扇区错误”将包含故障扇区的信息,从而能够了解该扇区上的代码错误,
        用户可以调用函数“HAL_FLASH_GetError()”来获取相关信息。
    */
    /*
      FLASH_ErrorTypeDef errorcode = HAL_FLASH_GetError();
    */
    Error_Handler();
  }

  /* 注意:如果闪存中的擦除操作也涉及到数据或指令缓存中的数据,
则必须确保在代码执行期间访问这些数据之前,先对其进行重写。如果无法安全地做到这一点,建议通过在 FLASH_CR 寄存器中设置 DCRST 和 ICRST 位来刷新缓存。*/
  __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();

  /* 一个字一个字的写到用户区
    (用户定义的 FLASH_USER_START_ADDR 和 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
    { 
      /*
        FLASH_ErrorTypeDef errorcode = HAL_FLASH_GetError();
      */
      Error_Handler();
    }
  }

  /* 锁定闪存以禁用闪存控制寄存器的访问(此操作推荐用于保护闪存免受可能的不当操作影响) *********/
  HAL_FLASH_Lock(); 

  /* 检查所编写的数据是否正确
        MemoryProgramStatus = 0:数据编入正确
        MemoryProgramStatus != 0:有部分字节编入错误 ******/
  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;
  }  

  
  /* Infinite loop */
  while (1)
  {
  }
}

/**
  * @brief  Gets the sector of a given address
  * @param  None
  * @retval The sector of a given address
  */
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/*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_5))*/
  {
    sector = FLASH_SECTOR_5;
  }

  return sector;
}

/**
  * @brief  Gets sector Size
  * @param  None
  * @retval The size of a given sector
  */
static uint32_t GetSectorSize(uint32_t Sector)
{
  uint32_t sectorsize = 0x00;

  if((Sector == FLASH_SECTOR_0) || (Sector == FLASH_SECTOR_1) || (Sector == FLASH_SECTOR_2) || (Sector == FLASH_SECTOR_3))
  {
    sectorsize = 16 * 1024;
  }
  else if(Sector == FLASH_SECTOR_4)
  {
    sectorsize = 64 * 1024;
  }
  else
  {
    sectorsize = 128 * 1024;
  }  
  return sectorsize;
}

HAL函数及结构体介绍

结构FLASH_EraseInitTypeDef

结构体定义

typedef struct {
   uint32_t TypeErase; // 擦除类型:扇区擦除或全片擦除
   uint32_t Banks; // 目标存储块(仅适用于双存储块设备)
   uint32_t Sector; // 起始扇区编号
   uint32_t NbSectors; // 要擦除的扇区数量
   uint32_t VoltageRange; // 电压范围,用于设置操作位宽
} FLASH_EraseInitTypeDef;

字段说明

TypeErase 指定擦除类型,可选值: FLASH_TYPEERASE_SECTORS:仅扇区擦除。 FLASH_TYPEERASE_MASSERASE:闪存批量擦除激活。
Banks 指定目标存储块,仅适用于支持双存储块的设备(如 STM32F42x/43x 系列)。对于单存储块设备,此字段可忽略。仅适用于TypeErase为FLASH_TYPEERASE_MASSERASE
Sector 指定起始扇区编号,范围为 FLASH_SECTOR_0 至 FLASH_SECTOR_11(具体范围取决于芯片型号)。仅适用于TypeErase为FLASH_TYPEERASE_SECTORS
NbSectors 指定要擦除的扇区数量。需确保起始扇区和数量的组合在有效范围.
VoltageRange 指定操作电压范围,用于设置擦除和写入的位宽: 
    FLASH_VOLTAGE_RANGE_1:1.8V 至 2.1V,操作位宽为 8 位。 
    FLASH_VOLTAGE_RANGE_2:2.1V 至 2.7V,操作位宽为 16 位。 
    FLASH_VOLTAGE_RANGE_3:2.7V 至 3.6V,操作位宽为 32 位。 
    FLASH_VOLTAGE_RANGE_4:外部 Vpp,操作位宽为 64位.                                                             最大操作位数会影响擦除和写入的速度,其中64位宽度的操作除了配置寄存器位外,还需要在Vpp引脚外加一个8-9V的电压源,且其供电时间不得超过一小时,否则FLASH可能损坏,所以64位宽度的操作一般是在量产时对FLASH写入应用程序时才使用,大部分应用场合都是用32位的宽度。

擦除函数

函数名称

        HAL_StatusTypeDef HAL_FLASHEx_Erase (FLASH_EraseInitTypeDef * pEraseInit, uint32_t * SectorError)

函数描述

        执行闪存批量或特定扇区擦除

参数

        pEraseInit:指向一个“FLASH_EraseInitTypeDef”结构的指针,该结构包含了擦除的配置信息。就是上面解释的结构体.
        SectorError:指向一个变量的指针,该变量在出现错误时包含故障扇区的配置信息(0xFFFFFFFFU 表示所有扇区均已正确擦除)如果想知道具体错误信息,调用函数HAL_FLASH_GetError()

编程(写入)函数

函数名称

        HAL_StatusTypeDef HAL_FLASH_Program (uint32_t TypeProgram, uint32_t Address, uint64_t Data)

描述

        在指定地址处读取字节、半字、字或双字的数据。

参数

TypeProgram:程序设置:指示在指定地址处进行程序设置的方式。此参数可以是“闪存程序设置”这一值。

        FLASH_TYPEPROGRAM_BYTE        字节(8位)

        FLASH_TYPEPROGRAM_HALFWORD        半字(16位)

        FLASH_TYPEPROGRAM_WORD                字(32位)

        FLASH_TYPEPROGRAM_DOUBLEWORD        双字(64位)
Address:指定要进行程序设置的地址。
Data:指定要进行程序设置的数据。

解锁函数

 函数名称

        HAL_StatusTypeDef HAL_FLASH_Unlock (void )

描述

      解锁 FLASH 控制寄存器访问。

上锁函数

 函数名称

        HAL_StatusTypeDef HAL_FLASH_Lock (void )

描述

        锁 FLASH 控制寄存器访问。

取得FLASH错误标志

函数名称

        uint32_t HAL_FLASH_GetError (void )

  返回值

        闪存错误代码:返回的值可以是以下几种情况的组合:
                – HAL_FLASH_ERROR_RD:闪存读保护错误标志(PCROP)
                – HAL_FLASH_ERROR_PGS:闪存编程序列错误标志
                – HAL_FLASH_ERROR_PGP:闪存编程并行性错误标志
                – HAL_FLASH_ERROR_PGA:闪存编程对齐错误标志
                – HAL_FLASH_ERROR_WRP:闪存写保护错误标志
                – HAL_FLASH_ERROR_OPERATION:闪存操作错误标志

FLASH的空闲区间在哪里?

请看另一篇文章

STM32的程序存储在内置flash的地址区间 -优快云博客

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值