STM32 HAL库内部 Flash 读写实现

一、STM32F407 内部 Flash 概述

1.1 Flash 存储器的基本概念

Flash 存储器是一种非易失性存储器,它可以在掉电的情况下保持数据。STM32F407 系列微控制器内部集成了一定容量的 Flash 存储器,用于存储程序代码和数据。Flash 存储器具有擦除和编程次数的限制,通常在几万到几十万次不等,因此在使用时需要合理规划擦写操作,以延长其使用寿命。

1.2 STM32F407 内部 Flash 的结构

STM32F407 的内部 Flash 被划分为多个扇区,不同的扇区大小可能不同。每个扇区都有一个唯一的地址范围,擦除操作是以扇区为单位进行的。在进行读写操作时,需要了解这些扇区的地址和大小信息,以便正确地定位和操作数据。

1.3 Flash 操作的基本流程

在对 STM32F407 的内部 Flash 进行读写操作时,通常需要遵循以下基本流程:

  • 解锁 Flash:由于 Flash 处于写保护状态,在进行写操作之前需要先解锁。
  • 擦除扇区 :如果要写入新的数据,需要先擦除目标扇区,将扇区内的所有存储单元置为0xFF
  • 编程写入:在擦除完成后,可以将数据写入指定的地址。
  • 锁定 Flash:操作完成后,需要锁定 Flash 以恢复写保护。

二、硬件连接

由于是对内部 Flash 进行读写操作,因此不需要额外的硬件连接。只需要确保 STM32F407 开发板的电源、时钟等基本电路正常工作即可。

三、开发环境搭建

3.1 安装开发工具

  • STM32CubeMX:用于生成 STM32F407 的初始化代码和配置文件。
  • Keil MDK:用于编写、编译和调试 STM32 的代码。

3.2 配置 STM32CubeMX

  • 打开 STM32CubeMX,选择对应的芯片型号(STM32F407)。
  • 配置系统时钟和调试接口等基本参数。
  • 生成代码并导出到 Keil MDK 中。

3.3 创建工程

在 Keil MDK 中创建一个新的工程,将 STM32CubeMX 生成的代码导入到工程中。

四、STM32F407 HAL 库简介

4.1 HAL 库的概念

HAL(Hardware Abstraction Layer)库是 ST 公司为 STM32 微控制器提供的一种硬件抽象层库,它封装了底层的硬件操作,提供了一套统一的 API 接口,使得开发者可以更方便地进行硬件开发。

4.2 与 Flash 操作相关的 HAL 库函数

  • HAL_FLASH_Unlock():解锁 Flash,允许进行写操作。
  • HAL_FLASH_Program():向 Flash 中写入数据。
  • HAL_FLASHEx_Erase():擦除指定的 Flash 扇区。
  • HAL_FLASH_Lock():锁定 Flash,恢复写保护。

五、Flash 读写操作的代码实现

5.1 解锁 Flash

复制代码
#include "stm32f4xx_hal.h"

void unlock_flash() {
    HAL_FLASH_Unlock();
}

这段代码调用了HAL_FLASH_Unlock()函数,用于解锁 Flash,使得后续的写操作能够正常进行。

5.2 擦除扇区

复制代码
#define FLASH_SECTOR_TO_ERASE   FLASH_SECTOR_5

void erase_flash_sector() {
    FLASH_EraseInitTypeDef erase_init;
    uint32_t sector_error = 0;

    erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase_init.Sector = FLASH_SECTOR_TO_ERASE;
    erase_init.NbSectors = 1;
    erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3;

    HAL_FLASHEx_Erase(&erase_init, &sector_error);
}

此代码定义了要擦除的扇区为FLASH_SECTOR_5,并使用HAL_FLASHEx_Erase()函数进行扇区擦除操作。

5.3 写入数据到 Flash

复制代码
#define FLASH_START_ADDRESS   ((uint32_t)0x08020000)

void write_data_to_flash(uint32_t data) {
    uint32_t address = FLASH_START_ADDRESS;
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data);
}

该代码将数据写入到指定的 Flash 地址FLASH_START_ADDRESS处,使用HAL_FLASH_Program()函数进行编程操作。

5.4 从 Flash 读取数据

复制代码
uint32_t read_data_from_flash() {
    uint32_t address = FLASH_START_ADDRESS;
    return *(uint32_t*)address;
}

此函数用于从指定的 Flash 地址读取数据,通过指针解引用的方式获取存储在该地址的数据。

5.5 锁定 Flash

复制代码
void lock_flash() {
    HAL_FLASH_Lock();
}

调用HAL_FLASH_Lock()函数将 Flash 锁定,恢复写保护。

六、完整的 Flash 读写示例代码

复制代码
#include "stm32f4xx_hal.h"

// 定义要擦除的Flash扇区,这里选择扇区5
#define FLASH_SECTOR_TO_ERASE   FLASH_SECTOR_5
// 定义Flash写入数据的起始地址
#define FLASH_START_ADDRESS   ((uint32_t)0x08020000)

// 解锁Flash,允许对其进行写操作
void unlock_flash() {
    HAL_FLASH_Unlock();
}

// 擦除指定的Flash扇区
void erase_flash_sector() {
    // 定义Flash擦除初始化结构体
    FLASH_EraseInitTypeDef erase_init;
    // 用于存储擦除过程中可能出现的扇区错误信息
    uint32_t sector_error = 0;

    // 设置擦除类型为扇区擦除
    erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
    // 指定要擦除的扇区
    erase_init.Sector = FLASH_SECTOR_TO_ERASE;
    // 要擦除的扇区数量,这里为1个
    erase_init.NbSectors = 1;
    // 设置Flash的电压范围
    erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3;

    // 调用HAL库函数进行扇区擦除操作
    HAL_FLASHEx_Erase(&erase_init, &sector_error);
}

// 向Flash中写入一个32位的数据
void write_data_to_flash(uint32_t data) {
    // 获取写入数据的起始地址
    uint32_t address = FLASH_START_ADDRESS;
    // 调用HAL库函数将数据写入指定地址
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data);
}

// 从Flash中读取一个32位的数据
uint32_t read_data_from_flash() {
    // 获取读取数据的起始地址
    uint32_t address = FLASH_START_ADDRESS;
    // 通过指针解引用的方式读取该地址处的数据
    return *(uint32_t*)address;
}

// 锁定Flash,禁止对其进行写操作
void lock_flash() {
    HAL_FLASH_Lock();
}

int main(void) {
    // 初始化HAL库
    HAL_Init();

    // 解锁Flash,为后续的擦除和写入操作做准备
    unlock_flash();
    // 擦除指定的Flash扇区
    erase_flash_sector();

    // 定义要写入Flash的数据
    uint32_t data_to_write = 0x12345678;
    // 调用函数将数据写入Flash
    write_data_to_flash(data_to_write);

    // 调用函数从Flash中读取数据
    uint32_t read_data = read_data_from_flash();

    // 锁定Flash,防止误操作
    lock_flash();

    while (1) {
        // 可以在这里添加更多的逻辑
    }
}    

代码解释

  • main函数中,首先调用unlock_flash()解锁 Flash。
  • 然后调用erase_flash_sector()擦除指定的扇区。
  • 接着定义要写入的数据data_to_write,并调用write_data_to_flash()将数据写入 Flash。
  • 调用read_data_from_flash()从 Flash 中读取数据。
  • 最后调用lock_flash()锁定 Flash。

七、错误处理

在进行 Flash 操作时,可能会出现各种错误,例如擦除失败、编程错误等。以下是一个简单的错误处理示例:

复制代码
void erase_flash_sector() {
    FLASH_EraseInitTypeDef erase_init;
    uint32_t sector_error = 0;

    erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase_init.Sector = FLASH_SECTOR_TO_ERASE;
    erase_init.NbSectors = 1;
    erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3;

    if (HAL_FLASHEx_Erase(&erase_init, &sector_error) != HAL_OK) {
        // 处理擦除错误
        while (1);
    }
}

在擦除扇区时,如果HAL_FLASHEx_Erase()函数返回值不等于HAL_OK,表示擦除操作失败,可以在这里添加相应的错误处理逻辑。

八、注意事项

8.1 擦除和编程次数限制

Flash 存储器有擦除和编程次数的限制,频繁的擦写操作会缩短其使用寿命。因此,在设计系统时,应尽量减少不必要的擦写操作,合理规划数据存储。

8.2 数据一致性

在进行 Flash 写操作时,需要确保数据的一致性。如果在写操作过程中发生异常(如断电),可能会导致数据丢失或损坏。可以采用一些数据校验和备份的方法来提高数据的可靠性。

8.3 地址对齐

在进行 Flash 编程时,需要注意地址对齐的问题。例如,使用HAL_FLASH_Program()函数写入数据时,地址必须是 4 字节对齐的。

九、性能优化

9.1 批量擦除和写入

如果需要写入大量的数据,可以考虑批量擦除和写入,减少擦除和写入操作的次数,提高效率。

9.2 合理选择扇区

在选择要擦除和写入的扇区时,应根据数据的大小和使用频率进行合理规划,避免频繁擦除同一个扇区。

十、应用场景

10.1 数据存储

可以将一些重要的数据(如配置参数、校准数据等)存储在内部 Flash 中,以便在掉电后仍然能够保留。

10.2 程序更新

通过对内部 Flash 的读写操作,可以实现程序的在线更新,方便系统的维护和升级。

十一、总结

通过使用 STM32F407 的 HAL 库,我们可以方便地实现对内部 Flash 的读写操作。在实际应用中,需要注意 Flash 的擦除和编程次数限制、数据一致性等问题,并根据具体需求进行性能优化。通过合理地使用内部 Flash,可以提高系统的数据存储和处理能力。

相关推荐
蓝斯49721 小时前
从.到实现一个Windows上的虚拟hid键盘设备
windows·单片机·计算机外设
熙芯XiChip21 小时前
硬实时与软实时RTOS核心区别
嵌入式硬件
伟大的前程1 天前
unsigned int 定义的变量 在 电脑的 64 位系统中 和 32 位 cortex-m0 和 8 位的 51 单片机
单片机·嵌入式硬件
小渔村的拉线工1 天前
1.HPM6E80 解析芯片整体工作原理和存储架构
单片机·嵌入式硬件·mcu·架构·hpm6e80
熙芯XiChip1 天前
CAN与CANopen的本质区别
单片机
lilihewo1 天前
Altium Designer 封装库的创建(分享pcb 3D封装库)
嵌入式硬件·51单片机
神罚天下ET1 天前
典型arm位单片机启动流程(从上电到main.c)
c语言·arm开发·单片机
碧海银沙音频科技研究院1 天前
蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法
嵌入式硬件·算法
Zevalin爱灰灰1 天前
模拟信号链电路——基于TL431的电压基准电路
单片机·嵌入式硬件
csdn杰哥1 天前
瑞萨单片机开发教程[二十七] DTC 外部中断触发传输实验
单片机·嵌入式硬件·mongodb·瑞萨·ra6m5