2. Flash驱动抽象层设计

任务目标

在Bootloader差分OTA方案中,Flash驱动层需要屏蔽不同厂商、不同型号Flash芯片的差异,向上层提供统一的读写擦除接口。本案例设计一个最小可用的Flash抽象层,支持两种典型芯片(NOR Flash与内部Flash)的模拟实现,并验证接口一致性。

接口定义

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

#include <stdint.h>

typedef enum {
    FLASH_OK = 0,
    FLASH_ERR_PARAM,
    FLASH_ERR_ADDR,
    FLASH_ERR_BUSY,
    FLASH_ERR_UNSUPPORTED
} flash_status_t;

typedef struct {
    uint32_t base_addr;
    uint32_t sector_size;
    uint32_t total_size;
    uint32_t page_size;      /* 编程最小单位 */
    uint8_t  is_memory_mapped; /* 1:可直接寻址, 0:需命令访问 */
} flash_info_t;

typedef struct flash_dev {
    flash_info_t info;
    flash_status_t (*init)(struct flash_dev *dev);
    flash_status_t (*read)(struct flash_dev *dev, uint32_t offset,
                           uint8_t *buf, uint32_t len);
    flash_status_t (*write)(struct flash_dev *dev, uint32_t offset,
                            const uint8_t *buf, uint32_t len);
    flash_status_t (*erase)(struct flash_dev *dev, uint32_t offset,
                            uint32_t len);
} flash_dev_t;

/* 注册具体芯片驱动 */
flash_status_t flash_register(flash_dev_t *dev);

#endif

关键完整实现

/* flash_drv.c */
#include "flash_drv.h"
#include <string.h>

#define MAX_FLASH_DEVICES 4

static flash_dev_t *g_devices[MAX_FLASH_DEVICES];
static uint8_t g_dev_count = 0;

/* 模拟NOR Flash存储区 */
static uint8_t nor_storage[256 * 1024];
/* 模拟内部Flash存储区 */
static uint8_t int_storage[64 * 1024];

flash_status_t flash_register(flash_dev_t *dev) {
    if (!dev || g_dev_count >= MAX_FLASH_DEVICES)
        return FLASH_ERR_PARAM;
    if (dev->init(dev) != FLASH_OK)
        return FLASH_ERR_BUSY;
    g_devices[g_dev_count++] = dev;
    return FLASH_OK;
}

/* ---------- NOR Flash 驱动 (模拟W25Q系列) ---------- */
static flash_status_t nor_init(flash_dev_t *dev) {
    dev->info.base_addr = 0x90000000;
    dev->info.sector_size = 4096;
    dev->info.total_size = sizeof(nor_storage);
    dev->info.page_size = 256;
    dev->info.is_memory_mapped = 1;
    memset(nor_storage, 0xFF, sizeof(nor_storage));
    return FLASH_OK;
}

static flash_status_t nor_read(flash_dev_t *dev, uint32_t offset,
                               uint8_t *buf, uint32_t len) {
    if (!dev || !buf || offset + len > dev->info.total_size)
        return FLASH_ERR_PARAM;
    memcpy(buf, &nor_storage[offset], len);
    return FLASH_OK;
}

static flash_status_t nor_write(flash_dev_t *dev, uint32_t offset,
                                const uint8_t *buf, uint32_t len) {
    if (!dev || !buf || offset + len > dev->info.total_size)
        return FLASH_ERR_PARAM;
    /* NOR Flash只能将1写为0,模拟时直接覆盖 */
    memcpy(&nor_storage[offset], buf, len);
    return FLASH_OK;
}

static flash_status_t nor_erase(flash_dev_t *dev, uint32_t offset,
                                uint32_t len) {
    if (!dev || offset % dev->info.sector_size != 0 ||
        len % dev->info.sector_size != 0 ||
        offset + len > dev->info.total_size)
        return FLASH_ERR_PARAM;
    memset(&nor_storage[offset], 0xFF, len);
    return FLASH_OK;
}

/* ---------- 内部Flash驱动 (模拟STM32内部Flash) ---------- */
static flash_status_t intf_init(flash_dev_t *dev) {
    dev->info.base_addr = 0x08000000;
    dev->info.sector_size = 2048;
    dev->info.total_size = sizeof(int_storage);
    dev->info.page_size = 8;   /* 内部Flash按字节编程 */
    dev->info.is_memory_mapped = 1;
    memset(int_storage, 0xFF, sizeof(int_storage));
    return FLASH_OK;
}

static flash_status_t intf_read(flash_dev_t *dev, uint32_t offset,
                                uint8_t *buf, uint32_t len) {
    if (!dev || !buf || offset + len > dev->info.total_size)
        return FLASH_ERR_PARAM;
    memcpy(buf, &int_storage[offset], len);
    return FLASH_OK;
}

static flash_status_t intf_write(flash_dev_t *dev, uint32_t offset,
                                 const uint8_t *buf, uint32_t len) {
    if (!dev || !buf || offset + len > dev->info.total_size)
        return FLASH_ERR_PARAM;
    /* 内部Flash写入前必须已擦除(0xFF),此处模拟硬件行为 */
    for (uint32_t i = 0; i < len; i++) {
        if (int_storage[offset + i] != 0xFF)
            return FLASH_ERR_BUSY;
    }
    memcpy(&int_storage[offset], buf, len);
    return FLASH_OK;
}

static flash_status_t intf_erase(flash_dev_t *dev, uint32_t offset,
                                 uint32_t len) {
    if (!dev || offset % dev->info.sector_size != 0 ||
        len % dev->info.sector_size != 0 ||
        offset + len > dev->info.total_size)
        return FLASH_ERR_PARAM;
    memset(&int_storage[offset], 0xFF, len);
    return FLASH_OK;
}

/* ---------- 设备实例与注册 ---------- */
static flash_dev_t nor_dev = {
    .info = {0},
    .init = nor_init,
    .read = nor_read,
    .write = nor_write,
    .erase = nor_erase
};

static flash_dev_t intf_dev = {
    .info = {0},
    .init = intf_init,
    .read = intf_read,
    .write = intf_write,
    .erase = intf_erase
};

void flash_drv_init(void) {
    flash_register(&nor_dev);
    flash_register(&intf_dev);
}

/* 供上层查询设备 */
flash_dev_t *flash_get_device(uint8_t index) {
    if (index >= g_dev_count) return 0;
    return g_devices[index];
}

验证代码

/* test_flash_abstraction.c */
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "flash_drv.h"

int main(void) {
    flash_drv_init();
    uint8_t wbuf[64], rbuf[64];

    for (int dev_idx = 0; dev_idx < 2; dev_idx++) {
        flash_dev_t *dev = flash_get_device(dev_idx);
        assert(dev != 0);
        printf("Device %d: base=0x%08X size=%u sector=%u\n",
               dev_idx, dev->info.base_addr, dev->info.total_size,
               dev->info.sector_size);

        /* 擦除一个扇区 */
        assert(dev->erase(dev, 0, dev->info.sector_size) == FLASH_OK);

        /* 写入并回读验证 */
        for (int i = 0; i < 64; i++) wbuf[i] = (uint8_t)(i + dev_idx);
        assert(dev->write(dev, 0, wbuf, 64) == FLASH_OK);
        memset(rbuf, 0, 64);
        assert(dev->read(dev, 0, rbuf, 64) == FLASH_OK);
        assert(memcmp(wbuf, rbuf, 64) == 0);

        /* 越界访问应返回错误 */
        assert(dev->read(dev, dev->info.total_size - 1, rbuf, 2) == FLASH_ERR_PARAM);
        printf("  -> write/read/erase/param check PASSED\n");
    }
    printf("All flash abstraction tests passed.\n");
    return 0;
}

编译与运行

gcc -Wall -Wextra -o test_flash flash_drv.c test_flash_abstraction.c
./test_flash

验证判定标准

  • 程序输出两个设备的base地址、容量和扇区大小,且与代码中定义一致;
  • 每个设备均完成“擦除→写入→回读比对→越界检查”流程,打印PASSED;
  • 最终输出All flash abstraction tests passed.,退出码为0。

设计要点

  • 上层通过flash_dev_t函数指针调用,不感知具体芯片类型;新增芯片只需实现4个接口并注册;
  • 内部Flash驱动模拟了“写前必须擦除”的硬件约束,返回FLASH_ERR_BUSY;NOR Flash则允许直接覆盖;
  • 擦除操作强制按扇区对齐,非法参数返回FLASH_ERR_PARAM,保证上层OTA模块的调用安全。
OTA差分模块 Flash抽象层 flash_dev_t 统一接口 调用统一接口 NOR Flash驱动 W25Q系列模拟 扇区4KB / 页256B 内部Flash驱动 STM32内部Flash模拟 扇区2KB / 写前需擦除 SPI NOR Flash 芯片 MCU内部Flash 新增芯片驱动