【读写内部Flsah】


前言

STM32的内部Flash不仅掉电数据不丢失,还具有快速存储的特性。在内部Flash中,不仅可以存储程序,还可用于紧急情况下保留有限数据的作用。之前在项目中曾遇到过向STM32的内部Flash写入1800个字节数据的需求,下面我们就一起来看一下内部Flash的相关知识。


一、内部Flash组成详解

STM32 的内部 FLASH 由主存储器、系统存储器以及选项字节区域组成。下面来看一下各组成部分的功能。
在这里插入图片描述
(1)一般我们说 STM32 内部 FLASH 的时候,都是指主存储器区域,主存储器是存储用户应用程序的空间,它被划分为若干页(也称为扇区)。如STM32F103VET6的主存储器被划分为 256 页,每页大小为 2KB,共 512KB。在向FLASH 写入数据前,要先擦除准备写入数据的地址所在页(扇区)的整页(扇区)数据。
(2)用户无法访问系统存储器,它是放置启动代码的地方,此区域与用户无关。
(3)选项字节用于配置 FLASH 的读写保护、待机/停机复位等功能,共 16 字节。可以通过修改 FLASH 的选项控制寄存器来修改选项字节中的内容。

二、存储数据

1.向内部FLASH 写入数据的步骤

向内部FLASH 写入数据的三个步骤:
(1)解锁。由于内部FLASH 中存储的是应用程序,十分重要。为避免用户对FLASH 的误操作,因此在芯片复位后,会对FLASH 控制寄存器上锁。一旦对其上锁,便无法操纵FLASH了,进而无法修改内部FLASH 中的内容。要想修改内部FLASH 中的内容,需要先解锁。
(2)擦除。在向内部FLASH 写入数据之前,需要先将要写入部分所在的页(也称为扇区)进行整页数据的擦除。
(3)写入数据。在擦除指定页(扇区)的数据后,便可向其写入数据。

2.确定写入数据的位置

由于我们要写入数据的位置处于主存储器区域,由上述可知,主存储器中主要是存储应用程序,应用程序不一定占满主存储区域,剩下的未使用的部分才是用户可写入数据的地方。内部FLASH 中并无明显界限以区分到底哪一部分已存放代码,哪一部分未使用,可由用户存储数据。这就对我们存储数据带来要求,存储数据时,应避免随意选取存储位置,否则将可能造成程序数据的破坏。可通过查询应用程序编译时产生的“*.map”后缀文件,了解程序存储到了哪些区域,哪些区域未存储程序,以此确定可写入数据的区域。
在这里插入图片描述

3.与读写内部Flash相关的库函数

为帮助用户编程, STM32 标准库提供了一些库函数,用户可使用库函数实现对内部Flash的操作。

FLASH 解锁函数

#define FLASH_KEY1 ((uint32_t)0x45670123)
#define FLASH_KEY2 ((uint32_t)0xCDEF89AB)
/**
 * @brief 对 FLASH 控制寄存器解锁,使能访问
 * @param None
 * @retval None
*/
void FLASH_Unlock(void)
{
	if ((FLASH->CR & FLASH_CR_LOCK) != RESET) {
	/* 写入确认验证码 */
	FLASH->KEYR = FLASH_KEY1;
	FLASH->KEYR = FLASH_KEY2;
	}
}

FLASH 上锁函数

/**
* @brief 对 FLASH 控制寄存器上锁,禁止访问
* @param None
* @retval None
*/
void FLASH_Lock(void)
{
	/* 设置 FLASH 寄存器的 LOCK 位 */
	 FLASH->CR |= FLASH_CR_LOCK;
}

擦除扇区函数

/**
* @brief 擦除指定的页
* @param Page_Address: 要擦除的页地址.
* @retval FLASH Status:
可能的返回值: FLASH_BUSY, FLASH_ERROR_PG,
* FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status FLASH_ErasePage(uint32_t Page_Address)
{
	FLASH_Status status = FLASH_COMPLETE;
	/* 检查参数 */
	assert_param(IS_FLASH_ADDRESS(Page_Address));
	/*...此处省略 XL 超大容量芯片的控制部分*/
	/* 等待上一次操作完成 */
	status = FLASH_WaitForLastOperation(EraseTimeout);
	if (status == FLASH_COMPLETE) {
	 /* 若上次操作完成,则开始页擦除 */
	FLASH->CR|= CR_PER_Set;
	FLASH->AR = Page_Address;
	FLASH->CR|= CR_STRT_Set;
  /* 等待操作完成 */
  status = FLASH_WaitForLastOperation(EraseTimeout);
	/* 复位 PER 位 */
	FLASH->CR &= CR_PER_Reset;
	 }
	 /* 返回擦除结果 */
	return status;
 }

写入数据函数

/**
* @brief 向指定的地址写入一个字的数据(32 位)
* @param Address: 要写入的地址
* @param Data: 要写入的数据
* @retval FLASH Status:
可能的返回值: FLASH_ERROR_PG,
* FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
	FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)
 {
	FLASH_Status status = FLASH_COMPLETE;
	__IO uint32_t tmp = 0;
	/* 检查参数 */
	assert_param(IS_FLASH_ADDRESS(Address));
	/*...此处省略 XL 超大容量芯片的控制部分*/
	/* Wait for last operation to be completed */
	status = FLASH_WaitForLastOperation(ProgramTimeout);
	if (status == FLASH_COMPLETE) {
	/* 若上次操作完成,则开始页入低 16 位的数据(输入参数的第 1 部分) */
	FLASH->CR |= CR_PG_Set;
	*(__IO uint16_t*)Address = (uint16_t)Data;
	/* 等待上一次操作完成 */
	status = FLASH_WaitForLastOperation(ProgramTimeout);
	if (status == FLASH_COMPLETE) {
	/* 若上次操作完成,则开始页入高 16 位的数据(输入参数的第 2 部分) */
	tmp = Address + 2;
	*(__IO uint16_t*) tmp = Data >> 16;
	/* 等待操作完成 */
	status = FLASH_WaitForLastOperation(ProgramTimeout);
	/* 复位 PG 位 */
	FLASH->CR &= CR_PG_Reset;
	} else {
	/* 复位 PG 位 */
	FLASH->CR &= CR_PG_Reset;
}

标 准 库 里 还 提 供 了FLASH_ProgramHalfWord 函数用于每次写入半个字,即 16 位。

/**
  * @brief  向指定的地址写入半个字的数据(16 位).
  * @param Address: 要写入的地址
  * @param Data: 要写入的数据
  * @retval FLASH Status:
  可能的返回值: FLASH_ERROR_PG,
  * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
  FLASH_Status status = FLASH_COMPLETE;
  /* Check the parameters */
  assert_param(IS_FLASH_ADDRESS(Address));

#ifdef STM32F10X_XL
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(ProgramTimeout);
  
  if(Address < FLASH_BANK1_END_ADDRESS)
  {
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new data */
      FLASH->CR |= CR_PG_Set;
  
      *(__IO uint16_t*)Address = Data;
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastBank1Operation(ProgramTimeout);

      /* Disable the PG Bit */
      FLASH->CR &= CR_PG_Reset;
    }
  }
  else
  {
    if(status == FLASH_COMPLETE)
    {
      /* if the previous operation is completed, proceed to program the new data */
      FLASH->CR2 |= CR_PG_Set;
  
      *(__IO uint16_t*)Address = Data;
      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastBank2Operation(ProgramTimeout);

      /* Disable the PG Bit */
      FLASH->CR2 &= CR_PG_Reset;
    }
  }
#else
  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(ProgramTimeout);
  
  if(status == FLASH_COMPLETE)
  {
    /* if the previous operation is completed, proceed to program the new data */
    FLASH->CR |= CR_PG_Set;
  
    *(__IO uint16_t*)Address = Data;
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(ProgramTimeout);
    
    /* Disable the PG Bit */
    FLASH->CR &= CR_PG_Reset;
  } 
#endif  /* STM32F10X_XL */
  
  /* Return the Program Status */
  return status;
}

总结

相信通过对内部Flash结构、相关注意事项及库函数的讲解,大家对内部Flash有了更深刻的了解,大家动起手来,实践一下吧。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值