FLASH的简单应用

FLASH的简单应用(后续)

1、简介

  • Flash存储器(Flash Memory)是一种非易失性内存器件,断电后可长期保存数据。其主要分为NOR和NAND两类技术:NOR Flash由Intel于1988年推出,支持直接执行代码,读取速度快,适用于嵌入式系统等小容量代码存储场景;NAND Flash由东芝于1989年提出,存储密度高、成本低,擦写速度快,适用于固态硬盘(SSD)、U盘等大容量数据存储领域。
    两类闪存均基于浮栅晶体管结构,通过隧道效应实现数据擦写。NOR采用并行结构和热电子注入方式,支持字节级随机访问,但单元尺寸较大;NAND采用串行结构,以页/块为单位操作,单元尺寸更小且集成度高,但需配合ECC纠错和坏块管理机制。NOR适用于对代码可靠性要求高的场景,NAND则在消费电子和移动存储领域占据主导地位,其技术演进涵盖从2D到3D堆叠架构,显著提升了存储密度与性能。

2、操作方法(HAL库)

(1)初始化

  • 使能串口
    img
  • Inc中新建myflash.h并定义以下函数
void MyFLASHProgramInit(uint64_t data);
uint32_t MyFLASH_Read(uint32_t Address);
  • 在主函数外部声明该函数
//用于写入初始化的函数,参数为要写入的字符
void MyFLASHProgramInit(uint64_t data){
    HAL_FLASH_Unlock();//解锁FLASH

    // 擦除 Sector 5,这里得具体看芯片哪块扇区允许操作
    /*----------初始化用于删除的结构体---------------*/
    FLASH_EraseInitTypeDef erase = {0};
    uint32_t SectorError;//定义报错类型
    erase.TypeErase = FLASH_TYPEERASE_SECTORS;//选择"删除种类为'扇区删除'"
    erase.Sector = FLASH_SECTOR_5;//选择删除的为"第五扇区"
    erase.NbSectors = 1;//选择删除的扇区数量
    erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;//选择删除的电压级别
    erase.Banks = FLASH_BANK_1;//选择存储区为Bank_1
    //如果擦除成功,就进行写入操作
    if (HAL_FLASHEx_Erase(&erase, &SectorError) == HAL_OK) {
        // 写入数据(8 位)
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, ADDR_FLASH_SECTOR_5, data);
    }

    HAL_FLASH_Lock();//锁住FLASH
}
//用于读取该位置是否存在数据的函数,会返回该地址的数值
uint32_t MyFLASH_Read(uint32_t Address){
	return *((__IO uint32_t *)(Address));
}
/* USER CODE END 0 */

为了安全起见,操作FLASH时应当按照以下步骤:
解锁FLASH→操作→锁上FLASH

(2)写入代码

  • 在主函数内部声明该字符串:
  /* USER CODE BEGIN 1 */
	char message[] = "Notempty";
  /* USER CODE END 1 */
  • 在主循环外部写入以下内容:
//判断指定区域是否为空
  if(MyFLASH_Read(ADDR_FLASH_SECTOR_4) != 0){
    //如果不为空,则输出"Notempty"
	  HAL_UART_Transmit(&huart2, message, strlen(message), HAL_MAX_DELAY);
  }
  //写入字符'a'
    MyFLASHEraseInit('a');
    char chr = MyFLASH_Read(ADDR_FLASH_SECTOR_4);//读取该FLASH地址的值
  • 在主循环写入以下内容
  while (1)
  {
    //同过串口询问已写入地址处的值
	HAL_UART_Transmit(&huart2, &chr, 1, HAL_MAX_DELAY);
	HAL_Delay(1000);    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

(3)烧录效果

(i)烧录程序后,会先查询指定扇区是否为空,若为空则输出Notempty

(ii) 之后会在指定扇区写入字符

(iii)完成写入操作后每隔一秒会由串口传输指定地址的数据

img

3、代码清单

/*-----------------用于FLASH删除初始化的结构体-------------------*/
typedef struct
{
  uint32_t TypeErase;   /*选择"大量删除"(mass erase),还是"块删除"(sector erase)*/

  uint32_t Banks;       /*选择要删除的存储区*/

  uint32_t Sector;      /*当"大量删除"被禁用后,用于初始化指定删除的扇区*/

  uint32_t NbSectors;   /*指定要删除的扇区数量*/

  uint32_t VoltageRange;/*指定要选择的删除电压*/

} FLASH_EraseInitTypeDef;
#define FLASH_TYPEERASE_SECTORS         0x00000000U  /*仅删除扇区*/
#define FLASH_TYPEERASE_MASSERASE       0x00000001U  /*把整个芯片的FLASH都删除了*/
#define FLASH_VOLTAGE_RANGE_1        0x00000000U  /*操作电压等级: 1.8V 到 2.1V                */
#define FLASH_VOLTAGE_RANGE_2        0x00000001U  /*操作电压等级: 2.1V 到 2.7V                */
#define FLASH_VOLTAGE_RANGE_3        0x00000002U  /*操作电压等级: 2.7V 到 3.6V                */
#define FLASH_VOLTAGE_RANGE_4        0x00000003U  /*操作电压等级” 2.7V  到 3.6V + 外部电源 */
#define ADDR_FLASH_SECTOR_5    ((uint32_t)0x08020000) //指定的扇区
//解锁FLASH寄存器的函数
HAL_StatusTypeDef HAL_FLASH_Unlock(void)
{
  HAL_StatusTypeDef status = HAL_OK;

  if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != RESET)
  {
    /* Authorize the FLASH Registers access */
    WRITE_REG(FLASH->KEYR, FLASH_KEY1);
    WRITE_REG(FLASH->KEYR, FLASH_KEY2);

    /* Verify Flash is unlocked */
    if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != RESET)
    {
      status = HAL_ERROR;
    }
  }

  return status;
}
//锁定FLASH寄存器的函数
HAL_StatusTypeDef HAL_FLASH_Lock(void)
{
  /* Set the LOCK Bit to lock the FLASH Registers access */
  FLASH->CR |= FLASH_CR_LOCK;

  return HAL_OK;
}
//写入FLASH的函数,需要写入类型"TyprProgram",指定地址"Address",指定数据"Data"
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
  HAL_StatusTypeDef status;

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    if (TypeProgram == FLASH_TYPEPROGRAM_BYTE)
    {
      /*Program byte (8-bit) at a specified address.*/
      FLASH_Program_Byte(Address, (uint8_t) Data);
    }
    else if (TypeProgram == FLASH_TYPEPROGRAM_HALFWORD)
    {
      /*Program halfword (16-bit) at a specified address.*/
      FLASH_Program_HalfWord(Address, (uint16_t) Data);
    }
    else if (TypeProgram == FLASH_TYPEPROGRAM_WORD)
    {
      /*Program word (32-bit) at a specified address.*/
      FLASH_Program_Word(Address, (uint32_t) Data);
    }
    else
    {
      /*Program double word (64-bit) at a specified address.*/
      FLASH_Program_DoubleWord(Address, Data);
    }

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

    /* If the program operation is completed, disable the PG Bit */
    FLASH->CR &= (~FLASH_CR_PG);
  }
}

注意:烧录该程序后再次从CubeIDE中烧录其他程序会导致报错,需要用KEIL烧录程序后才行。疑似因为CubeIDE烧录时并未全部擦除FLASH数据,等我研究一下再发后续
后续来了,把同样程序用Keil编译完执行到HAL_UART_Transmit(&huart2, ADDR_FLASH_SECTOR_5, 1, HAL_MAX_DELAY);后,单片机就卡死了
我后面看来一下,传输的地址要求为const uint8_t *,FLASH的地址是uint32_t,不符合要求
疑似是GCC编译器在编译代码时优化了这个问题,而KEIL的ARMCC编译器没有优化,导致执行到该区域后单片机卡死
所以需要中间变量char c来读取FLASH处数据,然后再使用串口传输的函数传输该中间变量的值

posted @ 2025-08-29 13:48  奶龙大王  阅读(44)  评论(1)    收藏  举报