存储设备驱动 (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)即可透明访问底层存储介质。
这种设计的目的:
- 介质无关:上层无需关心 Flash/SD/USB 的时序差异,统一走
device_read/device_write接口。 - 裁剪灵活:通过配置宏即可启用/禁用某类存储,SDK 构建时按需编译。
- 平台可移植:引脚、时钟、线宽等差异全部收敛到平台数据结构中(如
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_ENABLE | SD 卡供电控制 | 使能后 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_SYS | NorFlash 无操作系统模式 | =1 时启用裸机 flash_mutex 互斥实现 |
MAX_NORFLASH_PART_NUM | 最大 Flash 分区数 | 固定为 4(norflash.c) |
平台数据结构字段(norflash_dev_platform_data):
| 字段 | 类型 | 说明 |
|---|---|---|
spi_hw_num | s8 | 使用的 SPI 通道(仅 SPI1/SPI2) |
spi_cs_port | u8 | CS 片选引脚 |
spi_read_width | u8 | Flash 读取线宽(1/2/4 线) |
spi_pdata | const struct spi_platform_data* | SPI 端口/时钟/模式参数 |
start_addr | u32 | 分区起始地址 |
size | u32 | 分区大小(单分区可忽略) |
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调节状态切换灵敏度,适应不同线缆与电源质量。