3. 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 {
/* 校验失败处理 */
}
}
运行与验证
- 地址选择:
TEST_ADDR必须指向应用区(非Bootloader保护区),且页对齐(8KB边界)。 - 编译烧录:将上述文件加入工程,编译后通过调试器烧录至MCU内部Flash。
- 执行路径:调用
Flash_Test(),观察返回值。 - 通过条件:
Flash_WritePage返回FLASH_OK,且Flash_Verify返回FLASH_OK,说明写入数据与源数据完全一致。 - 附加检查:可在调试器中直接查看
TEST_ADDR起始的8字节内存,应依次为00 01 02 03 04 05 06 07。
常见问题
- 擦除失败:确认目标地址不在UCB(用户配置块)保护区,且未锁定。
- 写入后校验失败:检查写入数据长度是否超过页大小,或地址是否页对齐。
- 等待超时:若
IfxFlash_waitOpDone()长时间不返回,检查Flash模块时钟配置。