2. Flash驱动抽象层设计

任务目标

本案例设计并实现一个Flash驱动抽象层,为上层Bootloader提供统一的擦除、写入和读取接口,屏蔽不同Flash芯片的底层差异。完成本案例后,上层应用无需感知具体芯片型号,仅通过抽象接口即可操作Flash。

接口定义

抽象层定义三个核心操作,覆盖Flash操作的最小必要集合:

  • 擦除:按扇区擦除,传入扇区编号
  • 写入:按地址写入数据缓冲区
  • 读取:按地址读取数据到缓冲区

关键完整代码

/* flash_abstraction.h */
#ifndef FLASH_ABSTRACTION_H
#define FLASH_ABSTRACTION_H

#include <stdint.h>
#include <stddef.h>

/* 抽象层统一错误码 */
typedef enum {
    FLASH_OK = 0,
    FLASH_ERR_PARAM,
    FLASH_ERR_ERASE,
    FLASH_ERR_WRITE,
    FLASH_ERR_READ
} FlashStatus;

/* 抽象接口:由具体芯片驱动实现 */
typedef struct {
    FlashStatus (*eraseSector)(uint32_t sectorAddr);
    FlashStatus (*write)(uint32_t addr, const uint8_t *data, uint32_t len);
    FlashStatus (*read)(uint32_t addr, uint8_t *buf, uint32_t len);
} FlashOps;

/* 全局操作句柄,Bootloader通过它访问Flash */
extern const FlashOps g_flashOps;

#endif /* FLASH_ABSTRACTION_H */
/* flash_abstraction.c */
#include "flash_abstraction.h"
#include "flash_chip_a.h"   /* 芯片A驱动头文件 */
#include "flash_chip_b.h"   /* 芯片B驱动头文件 */

/* 当前目标芯片选择:通过编译宏切换 */
#ifdef FLASH_CHIP_A
static FlashStatus eraseA(uint32_t addr) {
    return chipA_eraseSector(addr);
}
static FlashStatus writeA(uint32_t addr, const uint8_t *data, uint32_t len) {
    return chipA_program(addr, data, len);
}
static FlashStatus readA(uint32_t addr, uint8_t *buf, uint32_t len) {
    return chipA_read(addr, buf, len);
}

const FlashOps g_flashOps = {
    eraseA,
    writeA,
    readA
};
#elif defined(FLASH_CHIP_B)
static FlashStatus eraseB(uint32_t addr) {
    return chipB_erase(addr);
}
static FlashStatus writeB(uint32_t addr, const uint8_t *data, uint32_t len) {
    return chipB_write(addr, data, len);
}
static FlashStatus readB(uint32_t addr, uint8_t *buf, uint32_t len) {
    return chipB_read(addr, buf, len);
}

const FlashOps g_flashOps = {
    eraseB,
    writeB,
    readB
};
#else
#error "No flash chip selected. Define FLASH_CHIP_A or FLASH_CHIP_B."
#endif
/* 芯片A驱动接口声明(flash_chip_a.h)——实际工程中由芯片厂商驱动提供 */
#ifndef FLASH_CHIP_A_H
#define FLASH_CHIP_A_H

#include <stdint.h>

FlashStatus chipA_eraseSector(uint32_t addr);
FlashStatus chipA_program(uint32_t addr, const uint8_t *data, uint32_t len);
FlashStatus chipA_read(uint32_t addr, uint8_t *buf, uint32_t len);

#endif
/* 芯片B驱动接口声明(flash_chip_b.h) */
#ifndef FLASH_CHIP_B_H
#define FLASH_CHIP_B_H

#include <stdint.h>

FlashStatus chipB_erase(uint32_t addr);
FlashStatus chipB_write(uint32_t addr, const uint8_t *data, uint32_t len);
FlashStatus chipB_read(uint32_t addr, uint8_t *buf, uint32_t len);

#endif

编译与集成方式

将上述文件加入Bootloader工程源码目录。编译时通过宏选择目标芯片:

# 选择芯片A
gcc -DFLASH_CHIP_A -c flash_abstraction.c

# 选择芯片B
gcc -DFLASH_CHIP_B -c flash_abstraction.c

芯片A和芯片B的具体驱动实现(如chipA_eraseSector等)由芯片厂商提供或自行编写,本抽象层只负责接口适配,不包含具体芯片时序逻辑。

验证方法

编写如下测试代码验证抽象层接口行为:

/* test_flash_abs.c */
#include <assert.h>
#include <string.h>
#include "flash_abstraction.h"

/* 模拟芯片A驱动实现(仅用于验证抽象层) */
static uint8_t fakeFlash[4096];
FlashStatus chipA_eraseSector(uint32_t addr) {
    if (addr >= sizeof(fakeFlash)) return FLASH_ERR_PARAM;
    memset(&fakeFlash[addr], 0xFF, 512);  /* 假设扇区512字节 */
    return FLASH_OK;
}
FlashStatus chipA_program(uint32_t addr, const uint8_t *data, uint32_t len) {
    if (addr + len > sizeof(fakeFlash)) return FLASH_ERR_PARAM;
    memcpy(&fakeFlash[addr], data, len);
    return FLASH_OK;
}
FlashStatus chipA_read(uint32_t addr, uint8_t *buf, uint32_t len) {
    if (addr + len > sizeof(fakeFlash)) return FLASH_ERR_PARAM;
    memcpy(buf, &fakeFlash[addr], len);
    return FLASH_OK;
}

int main(void) {
    uint8_t wbuf[] = {0x11, 0x22, 0x33, 0x44};
    uint8_t rbuf[4] = {0};

    /* 通过抽象接口写入并读回 */
    assert(g_flashOps.eraseSector(0x100) == FLASH_OK);
    assert(g_flashOps.write(0x100, wbuf, sizeof(wbuf)) == FLASH_OK);
    assert(g_flashOps.read(0x100, rbuf, sizeof(rbuf)) == FLASH_OK);
    assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);

    /* 验证擦除后数据为0xFF */
    memset(rbuf, 0, sizeof(rbuf));
    assert(g_flashOps.eraseSector(0x100) == FLASH_OK);
    assert(g_flashOps.read(0x100, rbuf, sizeof(rbuf)) == FLASH_OK);
    for (int i = 0; i < 4; i++) {
        assert(rbuf[i] == 0xFF);
    }

    return 0;
}

编译运行:

gcc -DFLASH_CHIP_A -o test_flash test_flash_abs.c flash_abstraction.c
./test_flash

通过条件:程序正常退出且无assert失败,说明抽象层正确完成了擦除、写入、读取的接口转发,且数据一致性与擦除语义符合预期。若更换为芯片B,只需将编译宏改为-DFLASH_CHIP_B并提供对应驱动实现,测试代码无需修改。