3. Flash编程与校验

Flash编程与校验 学习卡片

任务目标

实现AUTOSAR Bootloader中的Flash驱动层编程接口:支持按页擦除、按页写入,并在编程完成后执行回读校验,确保写入数据与源数据一致。本案例聚焦Flash驱动的最小可复现实现,不涉及上层UDS协议交互。

环境与依赖

  • 硬件:Infineon AURIX TC275(或同类TriCore架构MCU)
  • 编译器:HighTec GCC / Tasking
  • 基础驱动:IfxFlash(AURIX SDK提供底层寄存器操作)

关键完整代码

/* flash_drv.h */
#ifndef FLASH_DRV_H
#define FLASH_DRV_H

#include "IfxFlash.h"
#include <stdint.h>
#include <string.h>

#define FLASH_PAGE_SIZE    (8u)      /* 单页大小:8KB(TC275 UCB/PMI区域) */
#define FLASH_BASE_ADDR    (0x80000000u)  /* PFLASH起始地址 */

typedef enum {
    FLASH_OK = 0u,
    FLASH_ERASE_FAIL,
    FLASH_WRITE_FAIL,
    FLASH_VERIFY_FAIL,
    FLASH_ADDR_INVALID
} Flash_ResultType;

/* 按页擦除(地址需页对齐) */
Flash_ResultType Flash_ErasePage(uint32_t address);

/* 按页写入(数据长度不超过FLASH_PAGE_SIZE) */
Flash_ResultType Flash_WritePage(uint32_t address, const uint8_t *data, uint32_t length);

/* 回读校验:比较Flash内容与源数据 */
Flash_ResultType Flash_Verify(uint32_t address, const uint8_t *expected, uint32_t length);

#endif /* FLASH_DRV_H */
/* flash_drv.c */
#include "flash_drv.h"

static Flash_ResultType Flash_CheckAddr(uint32_t addr, uint32_t len)
{
    if ((addr & (FLASH_PAGE_SIZE - 1u)) != 0u) {
        return FLASH_ADDR_INVALID;   /* 非页对齐 */
    }
    if ((addr + len) > (FLASH_BASE_ADDR + 0x100000u)) {  /* 限制在1MB范围内 */
        return FLASH_ADDR_INVALID;
    }
    return FLASH_OK;
}

Flash_ResultType Flash_ErasePage(uint32_t address)
{
    if (Flash_CheckAddr(address, 0u) != FLASH_OK) {
        return FLASH_ADDR_INVALID;
    }
    IfxFlash_eraseSector(address);   /* AURIX SDK底层擦除接口 */
    /* 等待操作完成 */
    while (IfxFlash_waitOpDone() == FALSE) { }
    return FLASH_OK;
}

Flash_ResultType Flash_WritePage(uint32_t address, const uint8_t *data, uint32_t length)
{
    Flash_ResultType ret;
    uint32_t i;

    if (data == NULL_PTR || length == 0u || length > FLASH_PAGE_SIZE) {
        return FLASH_ADDR_INVALID;
    }
    ret = Flash_CheckAddr(address, length);
    if (ret != FLASH_OK) {
        return ret;
    }

    /* 先擦除目标页 */
    ret = Flash_ErasePage(address);
    if (ret != FLASH_OK) {
        return ret;
    }

    /* 按字(32位)写入,不足部分补0xFF */
    for (i = 0u; i < length; i += 4u) {
        uint32_t word = 0xFFFFFFFFu;
        uint32_t j;
        for (j = 0u; j < 4u; j++) {
            if ((i + j) < length) {
                word &= ((uint32_t)data[i + j]) << (8u * j);
            }
        }
        IfxFlash_writeWord(address + i, word);
        while (IfxFlash_waitOpDone() == FALSE) { }
    }
    return FLASH_OK;
}

Flash_ResultType Flash_Verify(uint32_t address, const uint8_t *expected, uint32_t length)
{
    uint32_t i;
    uint8_t read_byte;

    if (expected == NULL_PTR || length == 0u) {
        return FLASH_ADDR_INVALID;
    }
    if (Flash_CheckAddr(address, length) != FLASH_OK) {
        return FLASH_ADDR_INVALID;
    }

    for (i = 0u; i < length; i++) {
        read_byte = *(volatile uint8_t *)(address + i);
        if (read_byte != expected[i]) {
            return FLASH_VERIFY_FAIL;
        }
    }
    return FLASH_OK;
}
/* 最小调用示例(main.c片段) */
#include "flash_drv.h"

#define TEST_ADDR   (FLASH_BASE_ADDR + 0x10000u)  /* 避开Bootloader自身所在区域 */

void Flash_Test(void)
{
    uint8_t src_data[FLASH_PAGE_SIZE];
    Flash_ResultType result;
    uint32_t i;

    /* 构造测试数据:0x00~0x07 */
    for (i = 0u; i < FLASH_PAGE_SIZE; i++) {
        src_data[i] = (uint8_t)i;
    }

    /* 写入并校验 */
    result = Flash_WritePage(TEST_ADDR, src_data, FLASH_PAGE_SIZE);
    if (result != FLASH_OK) {
        /* 错误处理:可在此处设置错误码或进入错误状态 */
        return;
    }

    result = Flash_Verify(TEST_ADDR, src_data, FLASH_PAGE_SIZE);
    if (result == FLASH_OK) {
        /* 编程成功且校验通过 */
    } else {
        /* 校验失败处理 */
    }
}

运行与验证

  1. 地址选择:TEST_ADDR必须指向应用区(非Bootloader保护区),且页对齐(8KB边界)。
  2. 编译烧录:将上述文件加入工程,编译后通过调试器烧录至MCU内部Flash。
  3. 执行路径:调用Flash_Test(),观察返回值。
  4. 通过条件:Flash_WritePage返回FLASH_OK,且Flash_Verify返回FLASH_OK,说明写入数据与源数据完全一致。
  5. 附加检查:可在调试器中直接查看TEST_ADDR起始的8字节内存,应依次为00 01 02 03 04 05 06 07。

常见问题

  • 擦除失败:确认目标地址不在UCB(用户配置块)保护区,且未锁定。
  • 写入后校验失败:检查写入数据长度是否超过页大小,或地址是否页对齐。
  • 等待超时:若IfxFlash_waitOpDone()长时间不返回,检查Flash模块时钟配置。