STM32 HAL库之配置数据FLASH存储

设计需求

        在项目开发中,经常需要保存配置数据至非易失性存储介质中(Flash 或EEPROM中),以便上电重启后获取配置信息。常见的STM32开发中,一般将数据存储在FLASH、备份区域寄存器、外挂EEPROM。由于内部Flash不能像EEPROM一样直接按字节进行读写操作,地址数据只能由1变为0,对固定地址重复写入,必须擦除整页使得地址数据为全FF,才可重新写入数据,通常为了提高内部FLASH的擦除寿命,一般采用内部flash模拟EEPROM方法。

FLASH概述

        本文以STM32F103VET6为例,介绍其内部512KByte的flash存储器。该处理器flash单元主要包括三个部分:存储单元、信息块部分、存储器相关控制寄存器。存储单元以page的形式组织,每页2Kbyte,共256页。信息块部分包括system memory(2Kbyte)和option byte(16Byte),system memory单元主要用于存储ST公司固化的bootloader,实现USART1串口更新用户程序。STM32F103VET6内部flash地址分配如下表所示。

需要注意的几点问题:

(1)、擦除和编程FLASH所需的高压是由STM32内部电路产生,且在此过程中内部晶振HSI需开启;

(2)、页写保护和读保护;

(3)、在写操作过程中,读操作将被挂起,待写操作完成后进行读操作。

2、Flash控制器FPEC —flash programming and erase controller

      默认情况下,内部的flash的FPEC和控制寄存器是处于写保护状态,要实现对FLASH的写操作,则首先unlock 存储器,即向寄存器FLASH_KEYR顺序写入两个key值。

    KEY1 = 0x45670123

    KEY2 = 0xCDEF89AB

FPEC控制器解锁后,通过查询FLASH_SR_BUSY位检查当前工作状态,配合FLASH_CR_PG、FLASH_CR_PER、FLASH_CR_MER分别实现对存储器的编程、页擦除、块擦除。

对于option byte的编程和擦除操作,首先对option byte部分解锁,即向FLASH_OPTKEY写入key值,配合FLASH_CR_OPTPG、FLASH_CR_OPTER实现对option byte进行编程和擦除操作。

3、option byte

选项字节主要用于配置flash的读写保护及看门狗,其分配地址如下:

RDP和WRPx分别用于内部flash的读保护、页写保护设置。系统复位后,the option byte loader—OBL自动加载option byte,并保存option byte 的部分数据至寄存器FLASH_OBR和FLASH_WRPR。RDP默认值为RDPRT key = 0x00A5,即处于非读保护状态。WRPx每位管理着2页的写保护状态。

上图可以看出:WRP0(对应寄存器FLASH_WRPR[7:0])的每位控制2页的写保护状态,共计16页;

WRP1(对应寄存器FLASH_WRPR[15:0])的每位控制2页的写保护状态,共计16页;

WRP2(对应寄存器FLASH_WRPR[23:16])的每位控制2页的写保护状态,共计16页;

WRP3(对应寄存器FLASH_WRPR[30:24])的每位控制2页的写保护状态,共计14页;

WRP3的最高位(对应寄存器FLASH_WRPR[31])控制页62至255页。

默认情况下,寄存器FLASH_WRPR的值均为0,即存储器处于写保护状态。若解除某一页存储器的写保护状态,需要对option byte进行擦除,并进行编程操作,完成后复位系统。

#define OB_BASE               ((uint32_t)0x1FFFF800)    /*!< Flash Option Bytes base address */

typedef struct
{
  __IO uint16_t RDP;
  __IO uint16_t USER;
  __IO uint16_t Data0;
  __IO uint16_t Data1;
  __IO uint16_t WRP0;
  __IO uint16_t WRP1;
  __IO uint16_t WRP2;
  __IO uint16_t WRP3;
} OB_TypeDef;

#define OB                  ((OB_TypeDef *) OB_BASE) 

4、访问存储器时间latency的设置

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }

应用1—操作FLASH

        使用__attribute__后,编译器自动将将所需的配置信息固化Flash地址中。

const uint16_t Version __attribute__((at(0x08010000)))= 0x1234;
const uint32_t Device_SN __attribute__((at(0x08010004))) = 0x55678911;

        重新变更FLASH中的SN序列号。

  /* USER CODE BEGIN 2 */
	FlashWrite(FLASH_TYPEPROGRAM_WORD, 0x08010004, 0x88992266);
  /* USER CODE END 2 */
void FlashWrite(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
	FLASH_EraseInitTypeDef EraseInitstruct =
	{
			.TypeErase = FLASH_TYPEERASE_PAGES,
			.NbPages = 1,
			.PageAddress = Address
	};
	uint32_t PageError = 0;
	HAL_FLASH_Unlock();
	HAL_FLASHEx_Erase(&EraseInitstruct, &PageError);
	HAL_FLASH_Program(TypeProgram, Address, Data);
	HAL_FLASH_Lock();
}

        从上面的结果可以看到,对SN序列号的更改导致了version也变成FF了,这是因为对flash地址数据变更时,必须对整页进行擦除。

应用2 — FLASH模拟EEPROM

        先聊聊本质。从应用1来看,要像读写EEPROM一样对FLASH固定地址的数据进行写操作,必须擦除整页的数据,而flash的擦除寿命是有限的,频繁的擦除势必会对MCU折损。flash模拟EEPROM策略就是对外提供虚拟物理地址,表面上看,可对同一地址进行重复写入,实质是在一页中按地址写入,当一页写满时,拷贝此页有效数据至另一页,并对此页进行整页擦除。以2K/页来说,4096/4 = 1024,即写入1024次后才会擦除一页,这样大大降低了FLASH擦除次数。

        Flash模拟EEPROM接口(官网示例)

uint16_t EE_Init(void);
uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data);
uint16_t EE_WriteVariable(uint16_t VirtAddress, uint16_t Data);

        使用时需要重新定义如下几个条目。NumOfVar是写入uint16型的变量数目,VirtAddVarTab数组是定义的虚拟地址。

#define PAGE_SIZE  (uint16_t)0x800  /* Page size = 2KByte */

/* EEPROM start address in Flash */
#define EEPROM_START_ADDRESS    ((uint32_t)0x08010000) 

#define NumbOfVar               ((uint8_t)0x03)
/* Virtual address defined by the user: 0xFFFF value is prohibited */
uint16_t VirtAddVarTab[NumbOfVar] = {0x01, 0x02, 0x03};

          由于官方例子不是使用HAL库函数,需要在EEPROM源文件做一下封装兼容。

#define FLASH_COMPLETE  HAL_OK
#define FLASH_Status    HAL_StatusTypeDef

FLASH_Status FLASH_ErasePage(uint32_t PageAddr)
{
    uint32_t PageError = 0;
    FLASH_EraseInitTypeDef EraseInitstruct =
    {
        .TypeErase = FLASH_TYPEERASE_PAGES,
        .NbPages = 1,
        .PageAddress = PAGE0_BASE_ADDRESS
    };

    EraseInitstruct.PageAddress = PageAddr;
    return HAL_FLASHEx_Erase(&EraseInitstruct, &PageError);
}

#define FLASH_ProgramHalfWord(PageAddr, Data) \
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, (PageAddr), (Data))

        读写示例

	HAL_FLASH_Unlock();
	EE_Init();

	EE_WriteVariable(0x01, 0x3344);
	EE_WriteVariable(0x02, 0x5671);
	EE_WriteVariable(0x03, 0x8899);
	HAL_FLASH_Lock();

    

EE_ReadVariable(0x03, &ReadDat);

应用3 —— 程序加密

        待发.....

### STM32 使用 CubeMX 配置数据保存至 Flash 的方法 #### 工具准备 为了实现STM32数据保存到Flash的功能,需要先准备好必要的工具和资源。这包括安装好STM32CubeMX以及对应的IDE(如Keil uVision或TrueSTUDIO),并确保已经下载了最新的HAL文件。 #### 创建项目与配置芯片参数 启动STM32CubeMX后创建一个新的工程,在选择目标器件时依据实际使用的MCU型号进行挑选。之后进入“Pinout & Configuration”页面设置系统的时钟树和其他外设选项[^2]。 #### FLASH存储配置 在主菜单栏找到“Configuration”,点击左侧的“RCC”标签页调整系统频率;接着切换到“Peripherals”,勾选“FLASH”。此时可以在右侧看到关于内部闪存的各项设定项,比如擦除次数限制、等待状态控制等。根据具体需求修改这些默认值即可满足大多数应用场景下的性能优化要求。 #### 初始化代码生成 完成上述硬件层面的基础搭建工作以后,转而关注软件部分——即编写业务逻辑之前自动生成框架结构的过程。返回首页按下“Project->Generate Code”按钮让STM32CubeMX为我们产出一套完整的模板文件夹体系,其中就包含了针对所选平台定制化的驱动接口定义和服务函数声明等内容。 #### 编程实例:向指定地址写入特定数值 下面给出一段简单的C语言片段作为示范用途,展示了怎样利用官方提供的API把一个整数型变量的内容持久化地记录下来: ```c #include "stm32f4xx_hal.h" // 假定已成功初始化flash操作句柄 extern FLASH_HandleTypeDef hflash; void WriteDataToFlash(uint32_t address, uint32_t data){ HAL_FLASH_Unlock(); // 解锁flash编程/擦除权限 __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR); // 清除所有错误标志位 if (HAL_FLASH_Program(&hflash, FLASH_TYPEPROGRAM_WORD, address, data) != HAL_OK){ Error_Handler(); } HAL_FLASH_Lock(); // 锁住flash防止意外改动 } ``` 这段代码实现了解锁Flash区域以便后续执行写命令的操作,并通过`HAL_FLASH_Program()`函数指定了待处理的目标位置及其对应要填充进去的新值。最后记得再次加锁保护起来以免误触造成破坏性后果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值