杰理 SDK 文档中心
首页
首页
  • 项目概览

    • SDK 简介与核心特性
    • 芯片平台与硬件资料
    • SDK 版本与发布信息
  • 快速开始

    • 环境搭建与工具链
    • 编译工程
    • 烧录与量产工具
  • 工程结构与构建系统

    • 工程目录布局
    • 构建与链接配置
  • 应用层开发

    • mbox_flash 应用框架
    • 板级支持包 (BSP)
    • 公共应用模块
    • UI 显示子系统
  • 蓝牙子系统

    • BLE 控制器、链路层与 HCI 传输
    • GATT 服务框架
    • BLE 应用示例:遥控器 / Dongle / 对讲机
    • 经典蓝牙支持
  • 音频子系统

    • 音频编解码器
    • 音频设备接口 (DAC / ADC / APA)
    • 音效处理与 EQ
    • 播放、录音与 MIO 工作流
  • 设备与文件系统

    • 存储设备驱动 (NorFlash / SDMMC / USB)
    • 文件系统 (FAT / nor_fs / SYDF)
    • 设备管理框架 (dev_mg)
  • 系统服务与电源管理

    • 消息机制 (msg / hot_msg)
    • 配置与参数存储 (app_config / VM)
    • 电源管理 (SOFT OFF / POWER DOWN)
  • 固件升级

    • 升级框架总览 (code_v1 / code_v2)
    • 双 Bank 升级机制
    • 升级通道:UART / 测试盒 / BLE OTA / USB / SD
  • 补丁包与版本维护

    • 版本升级补丁链 (v1.1.0 → v1.4.0)
    • 问题修复补丁
    • 固件裁剪与资源优化
  • 开发工具与支持

    • 辅助工具与脚本
    • 文档、配置说明与常见问题

存储设备驱动 (NorFlash / SDMMC / USB)

本页面向 AW30N BLE SDK 中的存储类设备驱动:NorFlash(SPI 接口外部 Flash)、SDMMC(SD 卡/TF 卡)与 USB 存储(U 盘 / OTG),涵盖设备抽象模型、平台数据配置、设备注册表、操作回调与缓存同步机制。

Purpose and Scope

本页覆盖 SDK 中存储设备驱动层的完整实现机制:

  • 统一设备抽象:struct device / struct device_operations 回调模型,以及 REGISTER_DEVICES 设备注册表。
  • NorFlash 驱动:SPI 挂载的外部 Flash,命令集、平台数据、分区与缓存同步。
  • SDMMC 驱动:SD0 平台数据、卡检测模式(CMD/CLK/IO)与读写回调。
  • USB 存储:U 盘 Mass Storage 设备(UDISK)与 OTG 主机/从机检测。

以下主题属于其他页面,不在本页展开:文件系统层(如 norfs、fatfs 挂载)、BT 配置工具中的 MAC 读写细节、启动流程中 flash_init.c 之外的引导逻辑。

概述

AW30N SDK 采用「设备-操作」分离的驱动架构:每个存储外设被描述为一个设备节点,挂载一组操作回调(init/open/read/write/bulk_read/ioctl/close 等),并通过编译期宏(如 TFG_EXT_FLASH_EN、TFG_SD_EN、TCFG_UDISK_ENABLE)按需注册进统一的设备表。上层(文件系统、蓝牙配置工具、量产工具)只依赖设备名(如 __EXT_FLASH_NANE、__SD0_NANE、__UDISK)即可透明访问底层存储介质。

这种设计的目的:

  1. 介质无关:上层无需关心 Flash/SD/USB 的时序差异,统一走 device_read/device_write 接口。
  2. 裁剪灵活:通过配置宏即可启用/禁用某类存储,SDK 构建时按需编译。
  3. 平台可移植:引脚、时钟、线宽等差异全部收敛到平台数据结构中(如 spix_p_data、sd0_data)。

NorFlash 驱动还内置了裸机互斥锁(NORFLASH_NO_SYS 模式下用 flash_mutex 保护 SPI 总线)、写保护(norflash_set_write_protect)与缓存同步定时器(_norflash_cache_sync_timer),保证掉电场景下数据的一致性。

架构

flowchart TD
    subgraph sg_Upper["上层调用方"]
        FS["文件系统 / norfs"]
        BTTool["bt_config_tool"]
        UDisk["U盘枚举(USB)"
        Loop["bsp_loop 缓存同步"]
    end

    subgraph sg_DevAPI["设备抽象层"]
        DevTable["REGISTER_DEVICES(device_table)"]
        DevOps["struct device_operations 回调表"]
        DevAPI["dev_open/read/write/ioctl API"]
    end

    subgraph sg_Nor["NorFlash 驱动 (SPI)"]
        NF_Data["norflash_data 平台数据"]
        NF_Ops["norflash_dev_ops / norfs_dev_ops"]
        NF_Mutex["flash_mutex 互斥"]
        NF_WP["norflash_set_write_protect"]
    end

    subgraph sg_SD["SDMMC 驱动"]
        SD_Data["sd0_data 平台数据"]
        SD_Ops["sd_dev_ops (sdx_dev_*)"]
        SD_Dect["CMD/CLK/IO 卡检测"]
    end

    subgraph sg_USB["USB 存储驱动"]
        USB_Ops["mass_storage_ops (usb_stor_*)"]
        OTG["otg_data / usb_dev_ops"]
    end

    subgraph sg_HW["硬件"]
        SPI1["SPI1/SPI2 总线"]
        FlashChip["外部 NorFlash 芯片"]
        SDChip["SD/TF 卡"]
        USBChip["USB 控制器"]
    end

    FS --> DevAPI
    BTTool --> DevAPI
    UDisk --> DevAPI
    Loop --> NF_Ops
    DevAPI --> DevTable
    DevTable --> DevOps
    DevOps --> NF_Ops
    DevOps --> SD_Ops
    DevOps --> USB_Ops
    NF_Ops --> NF_Mutex
    NF_Ops --> NF_WP
    NF_Ops --> NF_Data --> SPI1 --> FlashChip
    SD_Ops --> SD_Data --> SD_Dect
    SD_Data --> SDChip
    USB_Ops --> USBChip
    OTG --> USBChip

架构说明

  • 设备注册表(device_list.c)通过 REGISTER_DEVICES(device_table) 把所有存储设备登记为「名称 + 操作表 + 私有数据」三元组,并在启动时由 devices_init_api() 遍历初始化。
  • NorFlash 挂载在 SPI 总线上,平台数据 norflash_data 指定 SPI 通道、CS 引脚、读线宽与 SPI 参数;其操作表根据 TCFG_USB_EXFLASH_UDISK_ENABLE 在 norflash_dev_ops(原始 Flash 接口)与 norfs_dev_ops(文件系统接口)之间切换。
  • SDMMC 通过 sd0_data 配置 CMD/CLK/DAT0 引脚、速率、检测方式与供电回调;sd_dev_ops 提供字节读写与块读写(bulk_read/bulk_write)。
  • USB 存储 由 mass_storage_ops 提供 U 盘读写(usb_stor_*),otg_data 配置 OTG 在线/离线检测计数与检测模式,usb_dev_ops 承载 OTG 设备节点。

NorFlash 驱动详解

使能条件与编译宏

NorFlash 驱动的编译由宏 TCFG_NORFLASH_DEV_ENABLE 控制,该宏在 TFG_EXT_FLASH_EN 打开时自动定义,见 norflash.h:

#if defined(TFG_EXT_FLASH_EN) && TFG_EXT_FLASH_EN
#define TCFG_NORFLASH_DEV_ENABLE //启动 nor flash
#endif

Source: norflash.h

同时在 norflash.c 中,整个驱动体也被 #if defined(TCFG_NORFLASH_DEV_ENABLE) 包裹,未使能时驱动代码不参与编译,保证最小裁剪。

命令集(Winbond W25X 兼容)

驱动直接面向 SPI NOR Flash 的指令集编程,命令宏集中定义在 norflash.h:

#define WINBOND_WRITE_ENABLE		0x06
#define WINBOND_READ_SR1			0x05
#define WINBOND_READ_SR2			0x35
#define WINBOND_WRITE_SR1			0x01
#define WINBOND_WRITE_SR2			0x31
#define WINBOND_READ_DATA		    0x03
#define WINBOND_FAST_READ_DATA		0x0b
#define WINBOND_FAST_READ_DUAL_OUTPUT 0x3b
#define WINBOND_PAGE_PROGRAM	    0x02
#define WINBOND_PAGE_ERASE          0x81
#define WINBOND_SECTOR_ERASE		0x20
#define WINBOND_BLOCK_ERASE		    0xD8
#define WINBOND_CHIP_ERASE		    0xC7
#define WINBOND_JEDEC_ID            0x9F
#define WINBOND_POWER_DOWN          0xB9
#define WINBOND_RELEASE_POWER_DOWN  0xAB

Source: norflash.h

擦除粒度通过枚举区分页擦除(0x81)、扇区擦除(0x20,4KB)、块擦除(0xD8,64KB)与整片擦除(0xC7),见 norflash.h。驱动据此实现按分区的最小擦除单元选择,避免不必要的全片擦除,这是延长 Flash 寿命的关键设计。

平台数据结构

struct norflash_dev_platform_data 描述 Flash 的硬件挂接方式(norflash.h):

struct norflash_dev_platform_data {
    s8 spi_hw_num;         //只支持SPI1或SPI2
    u8 spi_cs_port;        //cs的引脚
    u8 spi_read_width;     //flash读数据的线宽
    const struct spi_platform_data *spi_pdata;
    u32 start_addr;         //分区起始地址
    u32 size;               //分区大小,若只有1个分区,则这个参数可以忽略
};

Source: norflash.h

配套的 NORFLASH_DEV_PLATFORM_DATA_BEGIN/END 宏用于初始化该结构体。实际的平台数据实例在 device_list.c 中定义:

NORFLASH_DEV_PLATFORM_DATA_BEGIN(norflash_data)
    .spi_hw_num = TFG_SPI_HW_NUM,
    .spi_cs_port = TFG_SPI_CS_PORT_SEL,
    .spi_read_width = TFG_SPI_READ_DATA_WIDTH,
    .spi_pdata = &spix_p_data[TFG_SPI_HW_NUM],
NORFLASH_DEV_PLATFORM_DATA_END()

Source: device_list.c

spix_p_data[] 数组为 SPI0/SPI1/SPI2 提供端口与时钟配置:SPI1 使用 TFG_SPI_CLK_PORT_SEL 等引脚、主模式、TFG_SPI_WORK_MODE、MSB 先行、CPOL/CPHA 均为 0(空闲低电平、首沿采样)、时钟 10MHz;SPI2 为备用通道(双向 1bit 模式、1MHz),见 device_list.c。

分区与互斥(norflash.c)

norflash.c 定义了分区上限 MAX_NORFLASH_PART_NUM 4 与 struct norflash_partition(含 name 字段),即一片外部 Flash 最多划分为 4 个逻辑分区,供文件系统、蓝牙配置保留区、量产数据等各取一段地址空间。

为保证 SPI 总线在无 RTOS(NORFLASH_NO_SYS)场景下的互斥访问,头文件用 volatile 变量模拟信号量(norflash.h):

typedef volatile u8 flash_mutex;
static inline void flash_mutex_create(flash_mutex *sem, u8 count) { *sem = count; }
static inline void flash_mutex_post(flash_mutex *sem) { (*sem) = 1; }
static inline s8 flash_mutex_pend(flash_mutex *sem, u32 timeout)
{
    u32 _jiffies = maskrom_get_jiffies();
    u32 _timeout = timeout + _jiffies;
    extern void wdt_clear();
    while (1) {
        if (*sem) { (*sem) = 0; break; }
        if ((timeout != 0) && (_timeout < _jiffies)) { return -1; }
        wdt_clear();
    }
    return 0;
}

Source: norflash.h

设计要点:flash_mutex_pend 在等待期间主动 wdt_clear() 喂狗,避免长时间等待 Flash 忙(擦除/编程)时触发看门狗复位;timeout == 0 表示死等,超时则返回 -1 表示获取失败。这一机制保证了掉电/擦写等长耗时操作期间系统不被误复位。

SDMMC 驱动详解

SD0 平台数据

SD 卡驱动通过 SD0_PLATFORM_DATA_BEGIN(sd0_data) 配置(device_list.c):

SD0_PLATFORM_DATA_BEGIN(sd0_data)
    .port = {
        SDMMC_CMD_IO,//CMD
        SDMMC_CLK_IO,//CLK
        SDMMC_DAT_IO,//DAT0
    },
    .speed                  = 12000000,
    .detect_mode            = SD_CLK_DECT,
    .detect_func            = sdmmc_0_clk_detect,
    .detect_io_level        = 0,//0:低电平检测到卡  1:高电平检测到卡
    .power                  = set_sd_power,   //TFG_SDPG_ENABLE 时生效
    .data_width             = 1,
    .priority               = 3,
SD0_PLATFORM_DATA_END()

Source: device_list.c

关键点:

  • 时钟 12MHz、1 位数据线(data_width = 1)、中断优先级 3。
  • 卡检测支持三种模式,代码中以编译期开关切换:SD_CMD_DECT(CMD 检测)、SD_CLK_DECT(CLK 检测,当前使能,检测到低电平表示有卡)、SD_IO_DECT(IO 引脚检测)。
  • power 回调在 TFG_SDPG_ENABLE 时指向 set_sd_power,用于上电时序控制;否则为 NULL。

SD 设备操作表

sd_dev_ops 将 SD 卡挂入设备抽象层(device_list.c):

const struct device_operations sd_dev_ops = {
    .init   = sdx_dev_init,
    .online = sdx_dev_online,
    .open   = sdx_dev_open,
    .read   = sdx_dev_byte_read,
    .write  = sdx_dev_byte_write,
    .bulk_read   = sdx_dev_read,
    .bulk_write  = sdx_dev_write,
    .ioctl  = sdx_dev_ioctl,
    .close  = sdx_dev_close,
};

Source: device_list.c

字节接口(sdx_dev_byte_read/write)与块接口(sdx_dev_read/write)并存:文件系统通常走块接口获得更高吞吐,调试/配置工具可走字节接口。online 回调负责卡插入检测后的初始化握手。

CDROM / 加密变体

当 SD_CDROM_EN 打开时,额外注册 sd_cdrom_dev_ops(只读块设备,write/bulk_write 为 NULL)与 sd_enc_dev_ops(加密读写),见 device_list.c。这为量产只读镜像与数据加密场景提供了同一硬件的不同逻辑视图。

USB 存储驱动详解

U 盘 Mass Storage

TCFG_UDISK_ENABLE 打开时,mass_storage_ops 提供 U 盘块设备访问(device_list.c):

const struct device_operations mass_storage_ops = {
    .init = NULL,
    .online = usb_stor_online,
    .open = usb_stor_open,
    .read = usb_stor_read,
    .write = usb_stor_write,
    .bulk_read = usb_stor_read,
    .bulk_write = usb_stor_write,
    .ioctl = usb_stor_ioctrl,
    .close = usb_stor_close,
};

Source: device_list.c

字节接口与块接口指向同一组 usb_stor_* 函数,即 USB Mass Storage 天然以 512 字节扇区为最小传输单元。

OTG 设备

otg_data 配置 OTG 在线/离线判定(device_list.c):

const struct otg_dev_data otg_data = {
    .usb_dev_en = 1,
    .slave_online_cnt = 2,
    .slave_offline_cnt = 2,
    .host_online_cnt = 2,
    .host_offline_cnt = 3,
    .detect_mode = TCFG_OTG_MODE,
    .detect_time_interval = 50,
    .usb_otg_sof_check_init = usb_otg_sof_check_init,
};

Source: device_list.c

连续 2 次检测到从机在线、3 次检测到主机离线才切换状态,50ms 检测间隔,这是典型的防抖计数设计:避免线缆插拔抖动、电压毛刺导致的频繁状态翻转。

设备注册表与初始化流程

REGISTER_DEVICES 设备表

所有存储设备在编译期统一注册进 device_table(device_list.c):

REGISTER_DEVICES(device_table) = {
#if TFG_EXT_FLASH_EN
#if TCFG_USB_EXFLASH_UDISK_ENABLE
    {.name = __EXT_FLASH_NANE, .ops = &norflash_dev_ops, .priv_data = (void *) &norflash_data},
#else
    {.name = __EXT_FLASH_NANE, .ops = &norfs_dev_ops, .priv_data = (void *) &norflash_data},
#endif
#endif
#if TFG_SD_EN
    {.name = __SD0_NANE, .ops = &sd_dev_ops, .priv_data = (void *) &sd0_data},
#endif
#if TCFG_UDISK_ENABLE
    {.name = __UDISK, .ops = &mass_storage_ops, .priv_data = (void *)NULL},
#endif
#if (TCFG_PC_ENABLE || TCFG_UDISK_ENABLE)
    {.name = __OTG, .ops = &usb_dev_ops, .priv_data = (void *)&otg_data},
#endif
#if SD_CDROM_EN
    {.name = "sd_cdrom", .ops = &sd_cdrom_dev_ops, .priv_data = (void *) NULL},
    {.name = "sd_enc", .ops = &sd_enc_dev_ops, .priv_data = (void *) NULL},
#endif
#ifdef VM_SFC_ENABLE
#if VM_SFC_ENABLE
    {.name = __SFC_NANE, .ops = &sfc_dev_ops, .priv_data = (void *)NULL},
#endif
#endif
};

Source: device_list.c

设计要点:

  • 同名多实现切换:__EXT_FLASH_NANE 节点根据 TCFG_USB_EXFLASH_UDISK_ENABLE 在「原始 Flash 接口」与「norfs 文件系统接口」间切换操作表,让上层代码无需改动即可支持「外部 Flash 作为 U 盘」与「外部 Flash 作为文件系统」两种产品形态。
  • 同一硬件多视图:SD 卡同时可注册 sd0(读写)、sd_cdrom(只读镜像)、sd_enc(加密)三个节点,由 SD_CDROM_EN 控制。
  • 链接段约束:文件头部的 #pragma bss_seg(".dev_lst.data.bss") 等指令把设备表放入专用段,便于链接器按需裁剪和地址固化。

核心控制流

sequenceDiagram
    participant APP as 应用/文件系统
    participant DEV as 设备抽象层
    participant NF as NorFlash驱动
    participant SD as SDMMC驱动
    participant USB as USB存储驱动
    participant HW as 硬件(SPI/SD/USB)

    Note over DEV: 系统启动 devices_init_api()
    DEV->>DEV: 遍历 REGISTER_DEVICES(device_table)
    DEV->>NF: init(norflash_data) -> SPI配置
    DEV->>SD: init(sd0_data) -> SDMMC初始化
    DEV->>USB: online(usb_stor_online) / OTG检测启动
    APP->>DEV: dev_open(__EXT_FLASH_NANE)
    DEV->>NF: open + flash_mutex_pend 互斥
    APP->>DEV: dev_read(buf, len, offset)
    DEV->>NF: bulk_read -> WINBOND_READ_DATA 命令
    NF->>HW: SPI 时序
    HW-->>NF: 数据回传
    NF-->>DEV: 完成/错误
    DEV-->>APP: 返回字节数
    loop bsp_loop 周期
        DEV->>NF: _norflash_cache_sync_timer(5) 同步缓存
    end
    APP->>DEV: dev_ioctl(FLASH_WRITE_PROTECT)
    DEV->>NF: norflash_set_write_protect(1)

初始化由 devices_init_api()(device_list.c 的 device_test_demo 中有调用示例)触发:依次对设备表执行 init/online,随后上层通过设备名打开设备执行读写。NorFlash 的缓存同步由 bsp_loop 每轮调用 _norflash_cache_sync_timer(5) 驱动(bsp_loop.c),启动阶段的 Flash 初始化在 flash_init.c 中完成并使能写保护。

使用示例

示例 1:外部 Flash 读取(蓝牙配置工具)

bt_config_tool.c 直接调用 sfc_norflash_read 从 Flash 保留区读取 8 字节 MAC 配置,再做字节序交换(bt_config_tool.c):

extern int sfc_norflash_read(struct device *device, void *buf, u32 len, u32 offset);
...
u32 cfg_reserve = boot_info.flash_size - RESERVED_MAC_ADDR_OFFSET;
sfc_norflash_read(device, read_buf, 8, cfg_reserve);
swapX(read_buf, reserve_bt_mac, 6);

Source: bt_config_tool.c

说明:这里展示了「Flash 保留区」的典型用法——从 Flash 末尾偏移量读取生产烧录的 MAC 地址,体现了分区模型(start_addr/size)与上层业务(蓝牙地址管理)的协作。

示例 2:设备读写自测框架

device_list.c 内置 device_test_demo()(device_list.c):

void device_test_demo(void)
{
    log_info("%s()  enter!\n", __func__);
    u32 i = 0;
    struct device *device;
    devices_init_api();
#if SD0_DEV_TEST
    log_info("%s(), line:%d\n", __func__, __LINE__);
    sdx_force_set_online("sd0");
#endif

Source: device_list.c

该示例展示了两点:设备驱动必须先 devices_init_api() 完成注册表遍历;SD 卡支持 sdx_force_set_online("sd0") 强制上线,便于在无卡检测硬件(或调试环境)下直接挂载卡。

示例 3:SPI 平台数据初始化

SPI 总线参数是 NorFlash 正常工作的前提(device_list.c):

{
    .port = {
        TFG_SPI_CLK_PORT_SEL,//clk
        TFG_SPI_DO_PORT_SEL, //do
        TFG_SPI_DI_PORT_SEL, //di
        0xff, //d2
        0xff, //d3
        TFG_SPI_CS_PORT_SEL,//cs
    },
    .role = SPI_ROLE_MASTER,
    .mode = TFG_SPI_WORK_MODE,
    .bit_mode = SPI_FIRST_BIT_MSB,
    .cpol = 0,//clk level in idle state:0:low,  1:high
    .cpha = 0,//sampling edge:0:first,  1:second
    .clk  = 10000000,
},

Source: device_list.c

注意 d2/d3 引脚为 0xff(未使用),表示当前配置为 1 线/2 线读取;clk = 10MHz 是 SPI1 的默认时钟,若 Flash 芯片支持更高频率可在此调整——这是性能调优的入口之一。

配置选项

存储驱动的裁剪与参数全部通过编译期宏与平台数据结构控制。核心开关宏(均在 app_config.h / 板级配置中定义):

配置宏作用说明
TFG_EXT_FLASH_EN使能外部 NorFlash打开后自动定义 TCFG_NORFLASH_DEV_ENABLE
TCFG_USB_EXFLASH_UDISK_ENABLE外部 Flash 作为 U 盘决定 __EXT_FLASH_NANE 挂 norflash_dev_ops 还是 norfs_dev_ops
TFG_SD_EN使能 SDMMC 驱动注册 __SD0_NANE 节点(sd_dev_ops)
TFG_SDPG_ENABLESD 卡供电控制使能后 sd0_data.power = set_sd_power
SD_CDROM_EN使能 CDROM/加密变体注册 sd_cdrom、sd_enc 只读/加密节点
TCFG_UDISK_ENABLE使能 U 盘注册 __UDISK(mass_storage_ops)
TCFG_PC_ENABLE使能 PC 连接模式与 UDISK 共同决定 __OTG 节点
VM_SFC_ENABLE使能 SFC 设备注册 __SFC_NANE(sfc_dev_ops)
NORFLASH_NO_SYSNorFlash 无操作系统模式=1 时启用裸机 flash_mutex 互斥实现
MAX_NORFLASH_PART_NUM最大 Flash 分区数固定为 4(norflash.c)

平台数据结构字段(norflash_dev_platform_data):

字段类型说明
spi_hw_nums8使用的 SPI 通道(仅 SPI1/SPI2)
spi_cs_portu8CS 片选引脚
spi_read_widthu8Flash 读取线宽(1/2/4 线)
spi_pdataconst struct spi_platform_data*SPI 端口/时钟/模式参数
start_addru32分区起始地址
sizeu32分区大小(单分区可忽略)

otg_dev_data 关键字段:slave_online_cnt/slave_offline_cnt(从机在线/离线防抖次数,均为 2)、host_online_cnt/host_offline_cnt(主机在线/离线防抖次数,2/3)、detect_mode(TCFG_OTG_MODE)、detect_time_interval(50ms)。

API 参考

flash_mutex_create(flash_mutex *sem, u8 count)

初始化 NorFlash 裸机互斥信号量,置初值 count。

flash_mutex_pend(flash_mutex *sem, u32 timeout): s8

获取互斥锁。timeout == 0 时死等;超时返回 -1,成功返回 0。等待期间调用 wdt_clear() 喂狗防复位。

flash_mutex_post(flash_mutex *sem)

释放互斥锁,将信号量置 1。

norflash_set_write_protect(u8 enable)

设置 Flash 写保护,启动阶段以参数 1 调用(flash_init.c)。

sfc_norflash_read(struct device *device, void *buf, u32 len, u32 offset): int

SFC 驱动的 Flash 读接口,按绝对偏移读取 len 字节到 buf。配置工具从 flash_size - RESERVED_MAC_ADDR_OFFSET 处读取保留区(bt_config_tool.c)。

_norflash_cache_sync_timer(u32 sync_step)

NorFlash 写缓存同步定时回调,由 bsp_loop 以步长 5 周期调用(bsp_loop.c),负责把写缓存落盘,保证掉电一致性。

设备抽象层操作表回调(struct device_operations)

回调说明
init设备硬件初始化(SPI 配置 / SDMMC 初始化)
online设备上线(SD 卡插入握手、U 盘枚举)
open / close打开/关闭设备句柄
read / write字节级读写(SD 字节接口)
bulk_read / bulk_write块级读写(文件系统主路径)
ioctl控制命令(写保护、卡信息查询等)

失败模式、边界情况与并发

  • SPI 互斥竞争:无 RTOS 时,若上层多个任务同时访问 NorFlash,flash_mutex_pend 保证串行化;等待期间持续喂狗,避免长擦写期间看门狗复位。但 query_flash_mutex_pend 是非阻塞版本,调用失败应立即重试或放弃,不能继续执行 Flash 操作。
  • 超时语义:flash_mutex_pend 超时返回 -1 表示互斥获取失败,上层必须处理该返回值;timeout=0 的死等模式在 Flash 硬件异常(挂死)时可能永久阻塞,需依赖看门狗兜底。
  • 擦除边界:Flash 必须先擦后写,驱动按页/扇区/块擦除枚举选择最小擦除单元;越界地址由分区 start_addr/size 约束,MAX_NORFLASH_PART_NUM=4 上限防止分区表溢出。
  • SD 卡热插拔:卡检测有 CMD/CLK/IO 三种模式,当前使用 CLK 检测(低电平表示有卡);检测抖动由 online 回调与状态机吸收,拔卡后读写应返回错误而非挂死。
  • OTG 状态防抖:从机在线/离线需连续 2 次确认、主机离线需连续 3 次确认,间隔 50ms,防止线缆抖动与电平毛刺引起状态频繁翻转。
  • 掉电一致性:NorFlash 写缓存由 _norflash_cache_sync_timer(5) 周期同步,若在同步窗口内掉电,最近写入可能丢失——关键数据(如 MAC 保留区)应在写完立即强制同步。

性能与运维注意事项

  • SPI 时钟是 NorFlash 吞吐瓶颈:默认 10MHz(SPI1),若芯片支持 Dual/Quad 读(spi_read_width、WINBOND_FAST_READ_DUAL_OUTPUT 0x3b),可提升读带宽;写性能受限于页编程(0x02)与擦除粒度。
  • SD 卡块接口优先:文件系统应使用 bulk_read/bulk_write(sdx_dev_read/write)而非字节接口,块传输减少命令开销,12MHz 时钟下可获得接近总线极限的吞吐。
  • 缓存同步频率:bsp_loop 每轮同步一次(步长 5),频繁写场景可适当增大步长降低 Flash 写放大,但会增加掉电丢失窗口。
  • 编译裁剪:未使能的驱动(TFG_EXT_FLASH_EN/TFG_SD_EN/TCFG_UDISK_ENABLE)不参与编译且不占用设备表项,量产固件应关闭不需要的存储以减小 ROM 占用。

扩展点

  • 新增 Flash 芯片:只需在 norflash.h 命令区补充厂商命令宏,并确认 spi_read_width/cpol/cpha 与芯片手册一致。
  • 新增存储介质:实现 struct device_operations 回调表,在 REGISTER_DEVICES(device_table) 增加节点条目即可接入设备抽象层,上层文件系统零改动。
  • SD 卡检测方式切换:修改 sd0_data 的 detect_mode/detect_func(CMD/CLK/IO 三选一)即可适配不同板卡的检测电路。
  • OTG 防抖参数调优:通过 otg_data 的 *_online_cnt/*_offline_cnt 调节状态切换灵敏度,适应不同线缆与电源质量。

相关链接

  • device_list.c - 设备注册表与平台数据
  • norflash.h - NorFlash API 与命令集
  • norflash.c - NorFlash 分区与驱动实现
  • flash_init.c - Flash 启动初始化与写保护
  • bsp_loop.c - 缓存同步定时驱动
  • bt_config_tool.c - Flash 保留区读取示例
Next
文件系统 (FAT / nor_fs / SYDF)