打印
[STM32F7]

STM32 的 Flash 存储操作与数据保存

[复制链接]
137|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
t60yz|  楼主 | 2024-12-9 14:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
STM32 的内部 Flash 是一种非易失性存储器,可以用来存储程序代码、配置数据和日志记录。以下内容涵盖 Flash 存储的基本操作(擦除、写入、读取)和在 Flash 中保存数据的实现方法。

1. Flash 存储概述
1.1 Flash 特性
分块存储:STM32 的 Flash 被划分为多个页(Page)或扇区(Sector)。
擦除机制:Flash 必须按页或扇区擦除,不能逐字节擦除。
写入限制:在擦除之前不能直接覆盖写入,只能将 1 改为 0。
1.2 操作流程
解锁 Flash:解除写保护。
擦除页/扇区:准备写入数据。
写入数据:按字(32 位)或双字(64 位)写入。
读取数据:按地址读取。
2. Flash 操作实现
2.1 Flash 擦除
擦除规则
擦除按页或扇区进行,具体大小根据芯片系列不同而变化。
STM32 HAL 提供统一的擦除接口。
代码示例:擦除扇区
使用 HAL 库擦除 Flash:

c
复制代码
FLASH_EraseInitTypeDef EraseInitStruct;
uint32_t PageError;

// 配置擦除参数
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; // 页擦除
EraseInitStruct.PageAddress = FLASH_USER_START_ADDR; // 用户区起始地址
EraseInitStruct.NbPages = 1; // 擦除 1 页

// 解锁 Flash
HAL_FLASH_Unlock();

// 擦除 Flash
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK) {
    // 擦除失败,处理错误
}

// 锁定 Flash
HAL_FLASH_Lock();

使用特权

评论回复
沙发
t60yz|  楼主 | 2024-12-9 14:34 | 只看该作者
Flash 写入
写入规则
Flash 数据写入按字(32 位)或双字(64 位)操作。
写入前需先擦除目标页或扇区。
代码示例:写入数据
c
复制代码
uint32_t address = FLASH_USER_START_ADDR; // 用户区起始地址
uint32_t data = 0x12345678; // 要写入的数据

// 解锁 Flash
HAL_FLASH_Unlock();

// 写入数据
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data) != HAL_OK) {
    // 写入失败,处理错误
}

// 锁定 Flash
HAL_FLASH_Lock();

使用特权

评论回复
板凳
t60yz|  楼主 | 2024-12-9 14:34 | 只看该作者
Flash 读取
读取规则
Flash 是可直接寻址的存储器,读取数据无需特殊配置。
使用指针按地址访问 Flash 数据。
代码示例:读取数据
c
复制代码
uint32_t address = FLASH_USER_START_ADDR;
uint32_t readData;

// 读取 Flash 数据
readData = *(__IO uint32_t *)address;

使用特权

评论回复
地板
t60yz|  楼主 | 2024-12-9 14:34 | 只看该作者
在 Flash 中保存配置数据
Flash 常用于存储设备的配置信息(如校准参数)。以下是保存与读取配置数据的步骤:

3.1 配置 Flash 地址
定义 Flash 用户区地址:

c
复制代码
#define FLASH_USER_START_ADDR  ((uint32_t)0x0801F800) // 自定义区域(末尾页)
#define FLASH_USER_END_ADDR    ((uint32_t)0x0801FFFF) // 自定义区域结束

使用特权

评论回复
5
t60yz|  楼主 | 2024-12-9 14:35 | 只看该作者
数据保存示例
将一组配置数据保存到 Flash:

c
复制代码
typedef struct {
    uint32_t parameter1;
    uint32_t parameter2;
    float parameter3;
} ConfigData;

ConfigData config = {0xAABBCCDD, 0x11223344, 3.14159};

void SaveConfigToFlash(ConfigData *config) {
    FLASH_EraseInitTypeDef EraseInitStruct;
    uint32_t PageError;

    // 解锁 Flash
    HAL_FLASH_Unlock();

    // 擦除目标页
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
    EraseInitStruct.PageAddress = FLASH_USER_START_ADDR;
    EraseInitStruct.NbPages = 1;
    HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);

    // 写入配置数据
    uint32_t address = FLASH_USER_START_ADDR;
    uint32_t *data = (uint32_t *)config;

    for (int i = 0; i < sizeof(ConfigData) / 4; i++) {
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data[i]);
        address += 4;
    }

    // 锁定 Flash
    HAL_FLASH_Lock();
}

使用特权

评论回复
6
t60yz|  楼主 | 2024-12-9 14:35 | 只看该作者
数据读取示例
从 Flash 中读取配置数据:

c
复制代码
void LoadConfigFromFlash(ConfigData *config) {
    uint32_t *flashData = (uint32_t *)FLASH_USER_START_ADDR;
    uint32_t *ramData = (uint32_t *)config;

    for (int i = 0; i < sizeof(ConfigData) / 4; i++) {
        ramData[i] = flashData[i];
    }
}

使用特权

评论回复
7
t60yz|  楼主 | 2024-12-9 14:35 | 只看该作者
在 Flash 中记录日志
Flash 也可用于保存设备运行日志(如错误码、状态信息)。由于日志是追加模式,需特别注意 Flash 的写入和擦除机制。

4.1 定义日志区域与结构
c
复制代码
#define FLASH_LOG_START_ADDR  ((uint32_t)0x0801E800) // 日志存储起始地址
#define FLASH_LOG_END_ADDR    ((uint32_t)0x0801F7FF) // 日志存储结束地址

typedef struct {
    uint32_t timestamp;
    uint32_t eventCode;
    uint32_t value;
} LogEntry;

使用特权

评论回复
8
t60yz|  楼主 | 2024-12-9 14:35 | 只看该作者
追加日志记录
找到下一个可写地址: Flash 不支持直接覆盖写入,需要找到未使用的地址。
代码实现
c
复制代码
uint32_t FindNextLogAddress() {
    uint32_t address = FLASH_LOG_START_ADDR;

    while (address < FLASH_LOG_END_ADDR) {
        if (*(__IO uint32_t *)address == 0xFFFFFFFF) { // 空白地址
            break;
        }
        address += sizeof(LogEntry);
    }

    return address;
}

使用特权

评论回复
9
t60yz|  楼主 | 2024-12-9 14:35 | 只看该作者
写入日志记录:
c
复制代码
void AddLogEntry(LogEntry *entry) {
    uint32_t address = FindNextLogAddress();

    if (address < FLASH_LOG_END_ADDR) {
        HAL_FLASH_Unlock();

        for (int i = 0; i < sizeof(LogEntry) / 4; i++) {
            HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, ((uint32_t *)entry)[i]);
            address += 4;
        }

        HAL_FLASH_Lock();
    }
}

使用特权

评论回复
10
t60yz|  楼主 | 2024-12-9 14:36 | 只看该作者
读取日志
逐条读取日志数据:

c
复制代码
void ReadLogs() {
    uint32_t address = FLASH_LOG_START_ADDR;

    while (address < FLASH_LOG_END_ADDR) {
        LogEntry *entry = (LogEntry *)address;

        if (entry->timestamp == 0xFFFFFFFF) { // 未使用地址
            break;
        }

        // 处理日志
        ProcessLogEntry(entry);

        address += sizeof(LogEntry);
    }
}

使用特权

评论回复
11
t60yz|  楼主 | 2024-12-9 14:36 | 只看该作者
Flash 操作注意事项
擦除操作耗时:

Flash 擦除需要一定时间,应避免频繁操作。
可以在低功耗模式或非关键任务中执行。
生命周期限制:

Flash 的写入/擦除次数有限(通常为 10,000~100,000 次)。
可通过分区轮换机制延长使用寿命。
断电保护:

在 Flash 写入过程中断电可能导致数据损坏,可通过校验机制检测并修复。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

162

主题

1001

帖子

0

粉丝