杰理 SDK 文档中心
首页
首页
  • 概述与快速开始

    • SDK 总览与芯片能力
    • 环境搭建与编译构建
    • 烧录与固件升级
    • 文档与版本资源
  • 应用与示例方案

    • demo 示例工程
    • WiFi 摄像头方案 (wifi_camera)
    • WiFi 音箱方案 (wifi_soundbox)
    • WiFi 婴儿监护方案 (wifi_bbm)
    • 公共应用模块库
    • 示例代码库 (example)
  • 系统架构与平台

    • 总体架构与工程分层
    • 系统启动与运行框架
    • 芯片驱动与板级适配
    • 设备管理与文件系统
    • 系统工具库与算法
  • 音频子系统

    • 音频框架与处理节点
    • 音频编解码与音效
    • 播放器与录音器
    • 语音交互与 AI 唤醒
    • LE Audio 与蓝牙音频
    • 音频调试与歌词
  • 视频与显示子系统

    • 摄像头驱动与 ISP
    • 视频编码与图像处理
    • 显示与 GPU 加速
    • 屏幕镜像 (screen_mirror)
  • 无线连接与网络

    • 蓝牙协议栈 (双模蓝牙)
    • WiFi 协议栈与配网
    • 网络协议栈
    • 云平台与 IoT 协议
  • UI 子系统

    • LVGL 集成与应用
    • UI 工程与工具链
  • 配置系统

    • 功能配置
    • 板级配置
    • 网络与蓝牙配置
    • 音频配置与提示音
  • 工具与测试

    • 产测与射频测试工具
    • 固件升级与更新机制
    • 调试与日志工具
  • 硬件参考设计

    • 原理图参考设计
    • 芯片数据手册

设备管理与文件系统

设备管理与文件系统是 AC792N SDK 中负责存储设备(SD 卡、U 盘、NOR Flash)的识别、挂载、卸载与格式化,并将设备抽象为可读写的文件系统路径(如 storage/sd0)供上层应用(播放器、录音、升级等)统一访问的基础能力模块。核心实现位于 sdk/apps/common/system/device_mount.c,它桥接了底层的设备框架(device/device.h)与文件系统框架(fs/fs.h)。

Purpose and Scope

本页面向「设备管理与文件系统」这一能力,完整覆盖:

  • 设备框架与文件系统框架的分层关系(dev_open / dev_ioctl / mount / unmount 等 API 的配合方式);
  • device_mount.c 中 SD 卡(SD0/SD1)的挂载、卸载、格式化全流程实现,包括挂载状态机、互斥保护、多分区(DMSDX)支持;
  • 文件系统类型选择(jlfat / fat)以及 SDK 中并存的其他文件系统(littlefs、NOR Flash 文件系统)的定位;
  • 对外公共 API(storage_device_ready、sdcard_storage_device_ready、sdcard_storage_device_format 等)及其语义;
  • 配置项、失败模式、并发与性能注意事项、扩展点。

不在本页范围:蓝牙设备的管理(如 ext_spi_bt/bt_device_manager.c 属于蓝牙协议栈的配对/连接管理,详见蓝牙相关页面);USB U 盘(MSD)与 USB 主机协议栈的细节;Flash 驱动的底层擦写算法。本页只讨论存储设备与文件系统的挂载、访问边界。

Overview

在 AC792N 这类嵌入式 SoC 平台上,存储介质种类繁多(SD 卡、TF 卡、NOR Flash、U 盘等),且文件系统也可能不同(FAT、jlfat、littlefs)。为了让上层业务代码(例如音乐播放器遍历歌曲、录音机写文件、OTA 升级读固件)不关心具体介质,SDK 采用了两层抽象:

  1. 设备框架(Device Framework):以 dev_open / dev_close / dev_ioctl / dev_online 等接口屏蔽具体硬件(SD/MMC 控制器、SPI NOR、USB MSD),设备通过名字(如 sd0、sd1、udisk0)寻址;
  2. 文件系统框架(FS Framework):以 mount / unmount / f_format / fmount_exist / fs_open 等接口屏蔽文件系统差异,把设备挂载成路径(如 storage/sd0),并支持缓存、分区、多文件系统并存。

device_mount.c 处于两者之间:它监听设备的在线状态,负责「设备 → 文件系统路径」的挂载生命周期管理,并对外暴露 storage_device_ready() 这类"存储就绪"查询 API,供系统初始化、播放器、录音等模块在访问存储前确认文件系统可用。

关键设计意图:

  • 惰性挂载(Lazy Mount):不强制在开机时挂载所有存储设备,而是由业务方通过 storage_device_ready() 触发;设备未插入时返回失败,插入后下次调用即可挂载成功;
  • 状态记忆:用 sd_mount[] 数组记录每个 SD 槽位的挂载状态(未挂载/成功/失败),避免重复挂载,也避免对已失败的设备反复发起耗时操作;
  • 互斥保护:挂载/卸载可能被多个任务(事件回调、业务线程)同时触发,因此用 OS_MUTEX(sd_mutex)串行化,防止 FAT 表被并发操作破坏;
  • 失败自愈:挂载失败时打印错误日志;对外提供格式化 API,业务可在设备"未格式化"场景下先格式化再挂载。

Architecture

下图展示了设备管理与文件系统的整体架构以及 device_mount.c 在其中的位置:

flowchart TD
    subgraph sg_App["应用层 (apps/common)"]
        App["应用代码 / 播放器 / 录音 / OTA"]
        Ready["storage_device_ready()"]
        SdReady["sdcard_storage_device_ready()"]
        Fmt["sdcard_storage_device_format()"]
        SubFmt["sdcard_storage_subdevice_format()"]
    end

    subgraph sg_Mount["设备挂载模块 device_mount.c"]
        MountSd["mount_sd_to_fs()"]
        UnmountSd["unmount_sd_to_fs()"]
        Mutex["sd_mutex (OS_MUTEX)"]
        State["sd_mount[2] 状态数组"]
    end

    subgraph sg_FS["文件系统框架 (fs/fs.h)"]
        MountAPI["mount(dev, path, fstype, cache, NULL)"]
        UnmountAPI["unmount(path)"]
        FmtAPI["f_format(dev, fstype, cluster)"]
        ExistAPI["fmount_exist(path)"]
        JLFAT["jlfat / fat (FAT 文件系统)"]
    end

    subgraph sg_Dev["设备框架 (device/device.h)"]
        DevOpen["dev_open() / dev_close()"]
        DevIoctl["dev_ioctl(IOCTL_GET_CLASS)"]
        DevOnline["dev_online()"]
        SD0["SD0 设备 (sd0)"]
        SD1["SD1 设备 (sd1)"]
    end

    App --> Ready
    App --> SdReady
    App --> Fmt
    Ready --> MountSd
    SdReady --> MountSd
    Fmt --> UnmountSd
    Fmt --> FmtAPI
    SubFmt --> FmtAPI
    MountSd --> Mutex
    MountSd --> State
    UnmountSd --> Mutex
    UnmountSd --> State
    MountSd --> MountAPI
    MountSd --> DevOnline
    MountSd --> DevOpen
    MountSd --> DevIoctl
    UnmountSd --> UnmountAPI
    UnmountSd --> ExistAPI
    MountAPI --> JLFAT
    JLFAT --> DevOpen
    DevOpen --> SD0
    DevOpen --> SD1

架构说明:

  • 应用层是挂载模块的调用方。storage_device_ready() 用于默认存储路径(CONFIG_STORAGE_PATH,通常为 storage/sd0),sdcard_storage_device_ready(sd_name) 用于指定 SD 槽位,格式化 API 供设备管理 UI 或产测工具调用;
  • 挂载模块(本页核心)维护每个 SD 槽位的状态与互斥锁,调用文件系统框架完成真正的挂载;CONFIG_DMSDX_ENABLE 开启时还会探测 SD 卡的多分区(sd0.0、sd0.1…),分别挂载到 storage/sd0.0、storage/sd0.1…;
  • 文件系统框架把设备块读写抽象成文件系统。SDK 默认使用 FAT 文件系统(CONFIG_JLFAT_ENABLE 时用杰理自研 jlfat,否则用 fat);SDK 中还并存 littlefs(sdk/apps/common/example/third_party/littlefs/lfs.c)与 NOR Flash 文件系统(sdk/apps/common/fat_nor/nor_fs.c),它们通过同一套 mount 接口注册,业务可按介质选择;
  • 设备框架负责与真实硬件交互:dev_online() 判断设备是否插入,dev_open() 获得句柄,dev_ioctl(IOCTL_GET_CLASS) 读取 SD 卡速度等级(用于日志打印),文件系统层通过设备句柄完成块读写。

挂载状态机

挂载模块用 sd_mount[] 数组记录每个 SD 槽位的生命周期状态,状态定义见 device_mount.c 第 45-49 行:

enum {
    SD_UNMOUNT = 0,
    SD_MOUNT_SUSS,
    SD_MOUNT_FAILD,
};
stateDiagram-v2
    [*] --> SD_UNMOUNT
    SD_UNMOUNT --> SD_MOUNT_SUSS: mount_sd_to_fs() 挂载成功
    SD_UNMOUNT --> SD_MOUNT_FAILD: 设备不在线 / mount 返回 NULL
    SD_MOUNT_SUSS --> SD_UNMOUNT: unmount_sd_to_fs() 卸载
    SD_MOUNT_FAILD --> SD_UNMOUNT: unmount_sd_to_fs()(格式化前先卸载)
    SD_MOUNT_FAILD --> SD_MOUNT_SUSS: 格式化成功后重新挂载

设计要点:

  • SD_MOUNT_SUSS 状态下重复调用 mount_sd_to_fs() 会直接返回成功(幂等),避免重复挂载导致 FAT 缓存错乱;
  • SD_MOUNT_FAILD 状态下重复调用会直接返回 -EFAULT,不会在本次调用内重试——重试由上层在设备重新插入(dev_online 重新为真)后发起,防止挂载失败时反复进行耗时的块设备探测;
  • 卸载(unmount_sd_to_fs())无论当前状态如何都执行 unmount() 并把状态复位为 SD_UNMOUNT,因此格式化流程可以安全地"先卸载 → 格式化 → 再挂载"。

核心实现:设备挂载(mount_sd_to_fs)

mount_sd_to_fs() 是挂载模块的心脏,完整实现见 device_mount.c 第 60-148 行。它依次完成:加锁 → 状态检查 → 在线检查 → 挂载 → 读取卡等级 → 更新状态 → 解锁。

static int mount_sd_to_fs(const char *sd_name)
{
    int err = 0;
    u32 class = 0, capacity = 0, block_size = 0;
    struct imount *mt;
    int id = sd_name[2] - '0';
    const char *dev = sd_name;
    void *fd = NULL;

    err = os_mutex_pend(&sd_mutex, 0);
    if (err) {
        return -EFAULT;
    }

    if (sd_mount[id] == SD_MOUNT_SUSS) {
        goto __exit;
    }
    if (sd_mount[id] == SD_MOUNT_FAILD) {
        err = -EFAULT;
        goto __exit;
    }
    if (!dev_online(dev)) {
        err = -EFAULT;
        goto __exit;
    }
    ...
    mt = mount(dev, id ? "storage/sd1" : "storage/sd0", FAT_FILE_SYSTEM_NAME, FAT_CACHE_NUM, NULL);
    if (!mt) {
        log_error("mount %s fail", sd_name);
        err = -EFAULT;
        goto __err;
    } else {
        log_info("mount %s suss", sd_name);
    }

    fd = dev_open(dev, 0);
    if (!fd) {
        err = -EFAULT;
        goto __err;
    }
    dev_ioctl(fd, IOCTL_GET_CLASS, (u32)&class);
    if (class == SD_CLASS_10) {
        log_info("%s card class: 10", sd_name);
    } else {
        log_info("%s card class: %d", sd_name, class * 2);
    }
    dev_close(fd);

__err:
    sd_mount[id] = err ? SD_MOUNT_FAILD : SD_MOUNT_SUSS;
__exit:
    os_mutex_post(&sd_mutex);
    return err;
}

来源:device_mount.c

逐段解读:

  1. 解析槽位 id:id = sd_name[2] - '0'。SD 设备名约定为 sd0 / sd1 三个字符,第三个字符即槽位号,因此 id 只有 0/1 两个取值,对应 sd_mount[2] 数组与 storage/sd0 / storage/sd1 挂载路径;
  2. 互斥加锁:os_mutex_pend(&sd_mutex, 0) 以 0 超时(阻塞等待)获取互斥锁。挂载期间持有锁,防止与卸载、格式化并发。锁的创建通过 early_initcall(__sd_mutex_init) 在系统早期初始化阶段完成(device_mount.c 第 54-58 行);
  3. 状态短路:已成功(SD_MOUNT_SUSS)直接返回 0;已失败(SD_MOUNT_FAILD)直接返回 -EFAULT——失败状态被"记住",避免每次调用都重复探测硬件;
  4. 在线检查:dev_online(dev) 询问设备框架 SD 卡是否插入且可访问;不在线直接失败,不进入耗时的挂载流程;
  5. 执行挂载:mount(dev, path, FAT_FILE_SYSTEM_NAME, FAT_CACHE_NUM, NULL)。文件系统名由编译宏决定(见下节),缓存数为 FAT_CACHE_NUM(默认 32),最后一个参数为挂载私有参数(此处为 NULL)。返回值 struct imount * 为空表示挂载失败;
  6. 读取卡等级(仅日志):dev_open(dev, 0) 打开设备句柄,dev_ioctl(fd, IOCTL_GET_CLASS, ...) 读取 SD 卡速度等级(SD_CLASS_10 直接打印 10,否则打印 class * 2),随后 dev_close()。这一步纯粹用于调试日志,不参与业务逻辑;
  7. 状态落定:__err 标签处统一根据 err 设置 SD_MOUNT_SUSS 或 SD_MOUNT_FAILD;__exit 标签处统一释放互斥锁。注意 goto __err 与 goto __exit 的差别——只有挂载失败才需要更新状态为 FAILED(挂载成功路径已通过 goto __exit 跳过状态写入吗?并非如此:挂载成功后 err == 0,走到 __err 时 sd_mount[id] 会被写为 SD_MOUNT_SUSS,因为 err 为 0)。实际控制流是:成功路径最终也会落入 __err 写状态。

文件系统类型选择

文件系统名不是写死的,而是由编译宏决定(device_mount.c 第 33-41 行):

#ifdef CONFIG_JLFAT_ENABLE
#define FAT_FILE_SYSTEM_NAME "jlfat"
#else
#define FAT_FILE_SYSTEM_NAME "fat"
#endif

#ifndef FAT_CACHE_NUM
#define FAT_CACHE_NUM 32
#endif

来源:device_mount.c

  • CONFIG_JLFAT_ENABLE 定义时使用杰理自研的 jlfat 文件系统,否则使用通用 fat(FAT16/32)。两者都通过文件系统框架注册,对上层 mount / f_format 接口透明;
  • FAT_CACHE_NUM 控制挂载时分配的 FAT 缓存条目数,默认 32。更大的缓存可减少随机读写的扇区访问次数,但会占用更多 RAM——嵌入式平台需要按内存预算权衡。

多分区支持(CONFIG_DMSDX_ENABLE)

开启 CONFIG_DMSDX_ENABLE 后,SD 卡可被切成多个分区。挂载逻辑会枚举设备子名(sd0.0、sd0.1…),并把每个分区挂载到对应路径(storage/sd0.0、storage/sd0.1…):

#ifdef CONFIG_DMSDX_ENABLE
    u8 i = 0;
    char sub_dev_name[7] = {0};
    char mount_path_name[16] = {0};

    strcpy(sub_dev_name, dev);
    strcpy(mount_path_name, id ? "storage/sd1" : "storage/sd0");

    do {
        sub_dev_name[strlen(dev)] = '.';
        sub_dev_name[strlen(dev) + 1] = '0' + i;
        fd = dev_open(sub_dev_name, 0);
        if (fd) {
            dev_close(fd);
            mount_path_name[11] = '.';
            mount_path_name[12] = '0' + i;
            ++i;
            mt = mount(sub_dev_name, mount_path_name, FAT_FILE_SYSTEM_NAME, FAT_CACHE_NUM, NULL);
            if (!mt) {
                log_error("%s mount %s %s fail, format...", sub_dev_name, mount_path_name, FAT_FILE_SYSTEM_NAME);
                f_format(sub_dev_name, FAT_FILE_SYSTEM_NAME, 32 * 1024);
                mt = mount(sub_dev_name, id ? "storage/sd1" : "storage/sd0", FAT_FILE_SYSTEM_NAME, FAT_CACHE_NUM, NULL);
            } else {
                log_info("%s mount %s %s suss", sub_dev_name, mount_path_name, FAT_FILE_SYSTEM_NAME);
            }
        }
    } while (fd != NULL && i < 10);

来源:device_mount.c

设计要点:

  • 探测方式为尝试打开子设备名:dev_open("sd0.N") 返回非空即认为该分区存在;枚举上限 10 个分区(i < 10);
  • 分区挂载失败时自动格式化(f_format(sub_dev_name, ..., 32 * 1024),簇大小 32KB),然后重新挂载到不带分区号的基础路径(storage/sd0)——这是为"未格式化分区"场景提供的自愈逻辑,但请注意它会销毁分区数据,仅适用于可接受数据丢失的场景(如出厂预格式化);
  • 卸载时(unmount_sd_to_fs())会先遍历 storage/sd0.N 直到 fmount_exist() 返回假,逐一卸载所有分区,再卸载基础路径。

卸载与格式化

unmount_sd_to_fs()(device_mount.c 第 150-179 行)加锁后先卸载全部分区(DMSDX 模式)或基础路径,把 sd_mount[id] 复位为 SD_UNMOUNT,最后解锁。它被格式化和"安全弹出"场景调用。

sdcard_storage_device_format()(device_mount.c 第 216-236 行)是完整的格式化流程:校验设备名长度(必须为 3 个字符,防止非法输入)→ 卸载 → f_format(sd_name, FAT_FILE_SYSTEM_NAME, 32 * 1024) → 成功后重新挂载。格式化期间持有互斥锁,保证不会有其他任务并发访问文件系统。

int sdcard_storage_device_format(const char *sd_name)
{
#if TCFG_SD0_ENABLE || TCFG_SD1_ENABLE
    int err;

    if (!sd_name || strlen(sd_name) != 3) {
        return -EPERM;
    }

    unmount_sd_to_fs(sd_name);

    err = f_format(sd_name, FAT_FILE_SYSTEM_NAME, 32 * 1024);
    if (err == 0) {
        mount_sd_to_fs(sd_name);
    }

    return err;
#else
    return 0;
#endif
}

来源:device_mount.c

核心流程:存储就绪与格式化

就绪检查流程(storage_device_ready)

storage_device_ready()(device_mount.c 第 182-196 行)是系统中最常用的入口。它检查默认 SD 设备(SDX_DEV,宏定义在 app_config.h 中,通常为 "sd0")是否在线,若在线且尚未挂载则执行挂载,最后以 fmount_exist(CONFIG_STORAGE_PATH) 判定存储是否可用:

int storage_device_ready(void)
{
#if TCFG_SD0_ENABLE || TCFG_SD1_ENABLE
    if (!dev_online(SDX_DEV)) {
        return false;
    }
    if (SDX_DEV[2] - '0' < 2 && sd_mount[SDX_DEV[2] - '0'] == SD_UNMOUNT) {
        mount_sd_to_fs(SDX_DEV);
    }

    return fmount_exist(CONFIG_STORAGE_PATH);
#else
    return false;
#endif
}

来源:device_mount.c

sequenceDiagram
    participant App as 应用层(播放器/录音/升级)
    participant DM as device_mount.c
    participant Mutex as sd_mutex
    participant Dev as 设备框架
    participant FS as 文件系统框架
    participant SD as SD 卡

    App->>DM: storage_device_ready() / sdcard_storage_device_ready("sd0")
    DM->>Dev: dev_online("sd0")
    Dev-->>DM: true(已插入)
    DM->>Mutex: os_mutex_pend(&sd_mutex)
    DM->>DM: sd_mount[0]==SD_UNMOUNT ? 继续 : 短路返回
    DM->>FS: mount("sd0", "storage/sd0", "jlfat|fat", 32, NULL)
    FS->>SD: 读分区表/初始化 FAT
    SD-->>FS: 块读写就绪
    FS-->>DM: struct imount*(非空)
    DM->>Dev: dev_open("sd0") → dev_ioctl(IOCTL_GET_CLASS) → dev_close()
    DM->>DM: sd_mount[0]=SD_MOUNT_SUSS
    DM->>Mutex: os_mutex_post(&sd_mutex)
    DM-->>App: fmount_exist("storage/sd0") → true

时序说明:就绪检查是"检查 → 必要时挂载 → 再确认"三步走。dev_online 先行判断避免了在无卡时发起挂载;fmount_exist 作为最终确认,保证返回值语义是"路径确实可用",而不是"设备在线"。

格式化流程(sdcard_storage_device_format)

sequenceDiagram
    participant App as 应用/产测工具
    participant DM as device_mount.c
    participant Mutex as sd_mutex
    participant FS as 文件系统框架
    participant SD as SD 卡

    App->>DM: sdcard_storage_device_format("sd0")
    DM->>DM: 校验 strlen(sd_name)==3,非法返回 -EPERM
    DM->>Mutex: os_mutex_pend(&sd_mutex)
    DM->>FS: unmount("storage/sd0")(含 DMSDX 分区逐一卸载)
    DM->>DM: sd_mount[0]=SD_UNMOUNT
    DM->>FS: f_format("sd0", "jlfat|fat", 32*1024)
    FS-->>DM: err==0(格式化完成)
    DM->>FS: mount("sd0", "storage/sd0", "jlfat|fat", 32, NULL)
    DM->>DM: sd_mount[0]=SD_MOUNT_SUSS
    DM->>Mutex: os_mutex_post(&sd_mutex)
    DM-->>App: err(0=成功)

格式化流程体现了"先卸载、后格式化、再挂载"的经典安全顺序:格式化会重写 FAT 表,若文件系统仍处于挂载态,缓存中的旧 FAT 数据会造成不一致;先 unmount() 冲刷缓存,格式化后重新 mount() 建立干净的文件系统视图。

使用示例

示例 1:播放器/录音前的存储就绪检查

系统模块在访问 storage/sd0 前调用 storage_device_ready(),例如 init.c 中初始化时对 sdfile_ext_mount_init() 的引用:

extern void app_main(void);
extern void sdfile_ext_mount_init(void);
void __attribute__((weak)) board_init() {}

来源:init.c

storage_device_ready() 的典型调用模式(基于其实现语义):

/* 伪代码模式:业务侧在读写存储前确认文件系统就绪 */
if (storage_device_ready()) {
    /* storage/sd0 已挂载,可以 fs_open / fwrite / fread */
} else {
    /* 无卡或挂载失败:提示用户插入 SD 卡 */
}

示例 2:指定槽位就绪与格式化

/* 指定 SD1 槽位:等待其文件系统就绪 */
if (sdcard_storage_device_ready("sd1")) {
    /* storage/sd1 可用 */
}

/* 设备管理菜单中的"格式化"动作:先卸载 → 格式化 → 重新挂载 */
int err = sdcard_storage_device_format("sd0");
if (err == 0) {
    /* 格式化成功,storage/sd0 已重新挂载 */
}

两个函数的完整实现见 device_mount.c 第 198-236 行。

示例 3:多分区设备(CONFIG_DMSDX_ENABLE)

开启 CONFIG_DMSDX_ENABLE 后,SD 卡子分区自动挂载到 storage/sd0.N 路径,挂载模块内部逻辑见前述 mount_sd_to_fs() 的 DMSDX 分支;业务侧通过 fmount_exist("storage/sd0.0") 判断分区是否存在,使用 sdcard_storage_subdevice_format(dev_name) 可对单个子设备格式化(该函数声明于 device_mount.c 第 239 行起)。

配置选项

设备管理与文件系统相关的配置分散在 app_config.h(板级配置)与编译宏(Kconfig/构建系统)中,挂载模块实际使用的选项如下:

选项类型默认值说明
TCFG_SD0_ENABLEbool板级配置是否启用 SD0 设备;为 0 时 storage_device_ready() 直接返回 false,挂载模块整体不编译
TCFG_SD1_ENABLEbool板级配置是否启用 SD1 设备
CONFIG_JLFAT_ENABLEbool未定义定义时使用杰理自研 jlfat 文件系统,否则使用通用 fat(见 device_mount.c 第 33-37 行)
FAT_CACHE_NUMint32挂载时分配的 FAT 缓存条目数;越大随机读写越快、RAM 占用越高(device_mount.c 第 39-41 行)
CONFIG_DMSDX_ENABLEbool未定义启用 SD 卡多分区探测与挂载(sd0.0、sd0.1… → storage/sd0.0…);分区挂载失败时自动格式化(device_mount.c 第 86-115 行)
SDX_DEV字符串板级定义默认 SD 设备名(通常 "sd0"),storage_device_ready() 使用(device_mount.c 第 185 行)
CONFIG_STORAGE_PATH字符串"storage/sd0"默认存储路径,storage_device_ready() 用 fmount_exist() 确认其可用性
TCFG_USB_SLAVE_ENABLE / TCFG_USB_HOST_ENABLEbool板级配置决定是否编译 USB 相关头文件(usb_stack.h、msd.h 等),影响 U 盘等 USB 存储设备支持(device_mount.c 第 11-23 行)

文件系统配套组件(与挂载模块通过同一 mount 框架协作,各自独立页面/模块):

  • littlefs:sdk/apps/common/example/third_party/littlefs/lfs.c,适用于小容量、掉电安全要求高的 NOR Flash 场景;
  • NOR Flash 文件系统:sdk/apps/common/fat_nor/nor_fs.c,把 FAT 语义映射到 SPI NOR 介质;
  • 录音专用文件系统:sdk/apps/common/fat_nor/phone_rec_fs.c。

API 参考

int storage_device_ready(void)

检查默认 SD 存储是否就绪。设备在线且未挂载时自动挂载,返回 fmount_exist(CONFIG_STORAGE_PATH) 的结果。

返回值:1(就绪,storage/sd0 已挂载);0(无卡、挂载失败或 TCFG_SD0_ENABLE/TCFG_SD1_ENABLE 均未开启)。

实现位置:device_mount.c 第 182-196 行

int sdcard_storage_device_ready(const char *sd_name)

检查指定 SD 槽位("sd0" / "sd1")的存储是否就绪,语义与 storage_device_ready() 相同,但允许指定槽位。

参数:sd_name —— 设备名,如 "sd0";内部取 sd_name[2] - '0' 作为槽位 id。

返回值:1 就绪;0 不可用。

实现位置:device_mount.c 第 198-214 行

int sdcard_storage_device_format(const char *sd_name)

格式化指定 SD 设备:卸载 → f_format(簇 32KB)→ 重新挂载。

参数:sd_name —— 设备名,必须恰好 3 个字符(如 "sd0")。

返回值:0 成功;-EPERM 参数非法(NULL 或长度≠3);-EFAULT 挂载/格式化失败。

注意:会销毁卡上全部数据。

实现位置:device_mount.c 第 216-236 行

int sdcard_storage_subdevice_format(const char *dev_name)

(CONFIG_DMSDX_ENABLE 时可用)格式化单个 SD 子设备(如 "sd0.0"),用于多分区场景的局部格式化。

实现位置:device_mount.c 第 238-240 行起

挂载模块使用的底层框架 API

API来源头文件在挂载模块中的用途
dev_online(name)device/device.h判断设备是否插入/可访问
dev_open(name, flag) / dev_close(fd)device/device.h打开/关闭设备句柄,探测子分区、读取卡等级
dev_ioctl(fd, cmd, arg)device/device.h读取设备属性,如 IOCTL_GET_CLASS(SD 卡速度等级)
mount(dev, path, fs_name, cache_num, priv)fs/fs.h把设备挂载为文件系统路径,返回 struct imount *
unmount(path)fs/fs.h卸载文件系统并冲刷缓存
fmount_exist(path)fs/fs.h查询路径是否已挂载
f_format(dev, fs_name, cluster_size)fs/fs.h格式化设备(簇大小 32KB)
os_mutex_create/pend/post系统内核挂载/卸载/格式化的互斥保护

失败模式与边界情况

无卡 / 设备不在线

dev_online() 返回假时,mount_sd_to_fs() 直接返回 -EFAULT,不进入挂载流程。这是预期的快速失败路径:上层通过返回值知道存储不可用,可提示用户插卡。由于状态数组仍保持 SD_UNMOUNT,卡片插入后下一次 storage_device_ready() 调用即可正常挂载,无需重启或手工干预。

挂载失败(未格式化 / 损坏的 FAT)

mount() 返回 NULL 时:

  • 普通模式:打印 mount %s fail,状态置为 SD_MOUNT_FAILD。之后重复调用会直接返回 -EFAULT(失败记忆),防止反复探测;恢复路径是上层调用格式化 API 或设备重新插拔后由状态复位触发重试;
  • DMSDX 模式:单个分区挂载失败会自动格式化该分区(簇 32KB)并重新挂载到基础路径。这是有数据破坏风险的激进自愈策略,设计意图是保证"分区表异常"的卡也能被用户正常使用,但不应在含用户数据的卡上依赖此行为。

非法参数

sdcard_storage_device_format() 对 sd_name 做 NULL 与长度(必须为 3)校验,非法输入返回 -EPERM,避免越界解析 sd_name[2]。槽位 id 取自 sd_name[2] - '0',因此调用方必须传入 sd0/sd1 格式的名字;storage_device_ready() 额外检查 SDX_DEV[2] - '0' < 2 以防宏配置错误导致数组越界。

分区枚举上限

DMSDX 模式分区枚举上限为 10(i < 10),超过 10 个分区的卡只会挂载前 10 个;mount_path_name 为 16 字节缓冲区,路径形如 storage/sd0.9,不满足该长度约束的分区名会被截断。

并发与一致性

  • 单一互斥锁串行化全部挂载操作:mount_sd_to_fs()、unmount_sd_to_fs()、sdcard_storage_device_format() 都持有 sd_mutex。挂载/卸载/格式化期间任何其他任务都不能执行同类操作,保证 FAT 结构的一致性;
  • 锁内持有时间较长:mount() 内部涉及块设备探测与 FAT 表读取,可能耗时数毫秒到数十毫秒(取决于卡速)。os_mutex_pend(&sd_mutex, 0) 是阻塞式等待,因此在低优先级任务中调用可能被高优先级任务抢占,需评估实时性要求;若在中断上下文调用,应改用非阻塞或事件通知方式(当前实现面向任务上下文);
  • 状态与锁的配合:状态检查(sd_mount[id])与状态写入都发生在锁内,避免了"两个任务同时挂载同一张卡"的竞态——第二个任务会在锁内看到 SD_MOUNT_SUSS 并短路返回。

性能与运维

  • 挂载成本:挂载是较重的操作(探测 + 读 FAT 表)。SD_MOUNT_SUSS 状态短路和 dev_online 前置检查,使重复调用 storage_device_ready() 的开销降为一次互斥锁操作 + 一次 fmount_exist 查询,适合被业务高频调用;
  • 日志:模块启用 LOG_INFO/DEBUG/ERROR(device_mount.c 第 25-31 行),挂载成功/失败、卡等级、分区卸载均有带 [DEVICE_MOUNT] 标签的日志,排查"存储不可用"问题应先看该标签日志确认挂载是否成功;
  • 缓存调优:FAT_CACHE_NUM 是吞吐与内存的权衡点。音乐播放/录音等流式读写场景,32 条目通常够用;若出现明显的随机读写卡顿且 RAM 有余量,可增大该值。

扩展点

  • 新增存储介质:本模块仅覆盖 SD 卡。新增介质(如 U 盘、eMMC)可参照 mount_sd_to_fs() 的模式,在设备框架中注册设备名,再调用 mount() / f_format() 挂载到新的 storage/ 路径;文件系统侧只需保证该文件系统通过 fs/fs.h 注册了名字(jlfat/fat/littlefs 均满足);
  • 文件系统选择:通过 CONFIG_JLFAT_ENABLE 在 jlfat 与 fat 间切换,无需修改挂载逻辑;littlefs(sdk/apps/common/example/third_party/littlefs/lfs.c)与 NOR 文件系统(sdk/apps/common/fat_nor/nor_fs.c)作为独立模块存在,可用于 SPI Flash 等介质;
  • 板级裁剪:TCFG_SD0_ENABLE/TCFG_SD1_ENABLE 关闭时整个挂载模块不参与编译(代码以 #if 包裹),ROM 占用最小化;
  • 测试观察点:SD 卡等级日志(IOCTL_GET_CLASS 结果)可用于产测/调试中快速判断卡速等级;格式化返回值可接入设备管理 UI 的提示文案。

相关链接

  • device_mount.c(本页核心实现)
  • 系统初始化 init.c(含 sdfile_ext_mount_init 引用)
  • littlefs 第三方文件系统示例
  • NOR Flash 文件系统 nor_fs.c
  • 录音文件系统 phone_rec_fs.c
  • 蓝牙设备管理(ext_spi_bt/bt_device_manager.c)属于蓝牙协议栈范畴,详见蓝牙相关页面
  • USB 存储设备(U 盘/MSD)涉及 USB 主机栈,详见 USB 相关页面
Prev
芯片驱动与板级适配
Next
系统工具库与算法