You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32H7使用HAL驱动写Flash的HardFault及数据读取异常问题

STM32H7 Flash 结构体读写问题排查与修复

问题概述

需将约680字节的Persistent_data结构体写入STM32H757XIH6的Bank 2 Sector 1 Flash(起始地址0x08100000),基于HAL驱动开发时遇到两个问题:

  • 初始代码调用HAL_FLASH_Program触发HardFault,追踪到stm32h7xx_hal_flash.c的*dest_addr = *src_addr;处,栈信息显示非法访问0xffffffec地址。
  • 按256位/32字节对齐要求修改代码后,写入无异常,但读取回的数据中data_header不匹配,指针或内存遍历逻辑存在问题。

初始代码片段

typedef struct {
    uint32_t data_header;
    Parameter parameter[NUMBER_PARAMETERS];
    int screen_paras[MAX_SCREENS][MAX_WIDGETS];
} Persistent_data;

void save_data() {
    Persistent_data save_data;

    uint32_t data_header = DATA_HEADER;

    save_data.data_header = data_header;
    memcpy(save_data.parameter, parameter, sizeof(save_data.parameter));
    memcpy(save_data.screen_paras, screen_paras, sizeof(save_data.screen_paras));

    size_t size = sizeof(Persistent_data);
    uint32_t word_count = (size + 3) / 4; //32bit对齐
    Flash_Write(FLASH_START_ADDRESS, (uint32_t*)&save_data, word_count);
}

HAL_StatusTypeDef Flash_Write(uint32_t address, uint32_t* data, uint32_t length) {
    HAL_StatusTypeDef status = HAL_OK;

    // 检查地址是否在Flash范围内
    if (address < FLASH_USER_START_ADDR || (address + length) > FLASH_USER_END_ADDR) {
        return HAL_ERROR;
    }

    // 解锁Flash
    HAL_FLASH_Unlock();

    // 擦除Flash扇区
    FLASH_EraseInitTypeDef eraseInitStruct = {
        .TypeErase = FLASH_TYPEERASE_SECTORS,
        .Banks = FLASH_BANK_2,
        .Sector = FLASH_SECTOR_1,
        .NbSectors = 1,
        .VoltageRange = FLASH_VOLTAGE_RANGE_3,
    };
    uint32_t sectorError = 0;

    status = HAL_FLASHEx_Erase(&eraseInitStruct, &sectorError);
    if (status != HAL_OK) {
        HAL_FLASH_Lock();
        return status;
    }

    // 写入数据
    for (int i = 0; i < length / 4; i++) { // 写入32位字
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, address, data[i]);
        if (status != HAL_OK) {
            break;
        }
        address += 4; // 地址增加4字节(32位)
    }

    // 锁定Flash
    HAL_FLASH_Lock();
    return status;
}

更新后代码片段

typedef struct {
    uint32_t data_header;
    Parameter parameter[NUMBER_PARAMETERS];
    int screen_paras[MAX_SCREENS][MAX_WIDGETS];
} Persistent_data;

void save_data() {
    Persistent_data save_data;

    uint32_t data_header</think_never_used_51bce0c785ca2f68081bfa7d91973934>= DATA_HEADER;

    save_data.data_header = data_header;
    memcpy(save_data.parameter, parameter, sizeof(save_data.parameter));
    memcpy(save_data.screen_paras, screen_paras, sizeof(save_data.screen_paras));

    size_t size = sizeof(Persistent_data);
    uint32_t word_count = (size + 31) / 32; //32字节对齐
    Flash_Write(FLASH_START_ADDRESS, (uint32_t*)&save_data, word_count);
}

void load_data() {
    Persistent_data load_data;
    size_t size = sizeof(Persistent_data);
    uint32_t word_count = (size + 31) / 32; //对齐
    Flash_Read(FLASH_START_ADDRESS, (uint32_t*)&load_data, word_count);

    if (load_data.data_header == DATA_HEADER) {
        memcpy(parameter, load_data.parameter, sizeof(parameter));
        memcpy(screen_paras, load_data.screen_paras, sizeof(screen_paras));
    }
}

HAL_StatusTypeDef Flash_Write(uint32_t address, uint32_t* data, uint32_t length) {
    HAL_StatusTypeDef status = HAL_OK;

    // 检查地址是否在Flash范围内
    if (address < FLASH_USER_START_ADDR || (address + length) > FLASH_USER_END_ADDR) {
        return HAL_ERROR;
    }

    // 解锁Flash
    HAL_FLASH_Unlock();

    // 擦除Flash扇区
    FLASH_EraseInitTypeDef eraseInitStruct = {
        .TypeErase = FLASH_TYPEERASE_SECTORS,
        .Banks = FLASH_BANK_2,
        .Sector = FLASH_SECTOR_1,
        .NbSectors = 1,
        .VoltageRange = FLASH_VOLTAGE_RANGE_3,
    };
    uint32_t sectorError = 0;

    status = HAL_FLASHEx_Erase(&eraseInitStruct, &sectorError);
    if (status != HAL_OK) {
        HAL_FLASH_Lock();
        return status;
    }

    // 写入数据
    for (int i = 0; i < length / 32; i++) { // 写入32字节(256位)字
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, address, (uint32_t)&data[i * 8]);
        if (status != HAL_OK) {
            break;
        }
        address += 32; // 地址增加32字节(256位)
    }

    // 锁定Flash
    HAL_FLASH_Lock();
    return status;
}

/**
 * @brief 从Flash读取数据
 * @param address 起始读取地址(必须256位对齐)
 * @param data 存储读取数据的缓冲区指针
 * @param length 读取字节数(必须是32的倍数)
 */
void Flash_Read(uint32_t address, uint32_t* data, uint32_t length) {
    if (address < FLASH_USER_START_ADDR || (address + length) > FLASH_USER_END_ADDR) {
        return; // 地址越界
    }

    for (uint32_t i = 0; i < length / 32; i++) { // 读取32字节(256位)字
        // 从指定地址读取32字节(256位)数据
        for (uint32_t j = 0; j < 8; j++) {
            data[i * 8 + j] = *(__IO uint32_t*)(address + j * 4); // 每个uint32_t占4字节
        }
        address += 32; // 地址增加32字节(256位)
    }
}

问题分析与修复方案

1. 初始HardFault根源

FLASH_TYPEPROGRAM_FLASHWORD是STM32H7的256位编程模式,要求传入的data参数是指向32字节对齐缓冲区的指针。初始代码中直接传入data[i](单个32位数值),HAL内部会将该数值当作指针解析,导致访问非法地址0xffffffec,触发HardFault。

2. 更新后读取数据错误的核心问题

  • 参数定义不一致:save_data和load_data中计算的word_count是32字节块数,但Flash_Write和Flash_Read的length参数被当作字节数处理,导致循环次数计算错误,读写数据量不匹配。
  • 结构体对齐缺失:Persistent_data结构体未显式设置32字节对齐,内存布局可能存在填充,导致Flash中的数据偏移与结构体字段不对应,读取后data_header错位。
  • Flash_Write循环逻辑错误:循环条件i < length / 32错误,若length是块数,应直接使用i < length;另外(uint32_t)&data[i * 8]的指针转换未确保缓冲区对齐,存在风险。
  • Flash_Read调用参数错误:load_data传入的word_count是块数,但函数注释要求length是字节数,导致读取总字节数不足,结构体数据不完整。

3. 修复后的关键调整

(1)确保结构体对齐

修改结构体定义,添加32字节对齐属性:

typedef struct __attribute__((aligned(32))) {
    uint32_t data_header;
    Parameter parameter[NUMBER_PARAMETERS];
    int screen_paras[MAX_SCREENS][MAX_WIDGETS];
} Persistent_data;

(2)统一参数定义

将Flash_Write和Flash_Read的length参数改为总字节数,内部计算32字节块数:

// Flash_Write修改
uint32_t num_blocks = (length + 31) / 32;
for (int i = 0; i < num_blocks; i++) {
    status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, address, (uint32_t)(data + i * 8));
    if (status != HAL_OK) break;
    address += 32;
}

// Flash_Read修改
uint32_t num_blocks = (length + 31) / 32;
for (uint32_t i = 0; i < num_blocks; i++) {
    for (uint32_t j = 0; j < 8; j++) {
        if (address + j*4 > FLASH_USER_END_ADDR) break; // 边界检查
        data[i * 8 + j] = *(__IO uint32_t*)(address + j * 4);
    }
    address += 32;
}

(3)修正读写调用参数

// save_data中调用
Flash_Write(FLASH_START_ADDRESS, (uint32_t*)&save_data, sizeof(Persistent_data));

// load_data中调用
Flash_Read(FLASH_START_ADDRESS, (uint32_t*)&load_data, sizeof(Persistent_data));

(4)地址边界检查优化

在Flash_Write中修正地址合法性判断:

if (address < FLASH_USER_START_ADDR || (address + length) > FLASH_USER_END_ADDR) {
    return HAL_ERROR;
}

内容的提问来源于stack exchange,提问作者Graeme Coard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 02:50:56