跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C

基于 RT-Thread 的 STM32 开发:SD 卡与 DFS 文件系统管理

在 STM32 平台上基于 RT-Thread 操作系统使用 DFS 文件系统管理 SD 卡的完整流程。内容涵盖 SD 卡规格分类、CubeMX 中的 SDIO 模式配置、RT-Thread 内核参数调整(如对象名长度、线程栈大小)、BSP 层宏定义及代码实现。通过示例展示了 SD 卡挂载、文件读写及目录管理的 FinSH 命令编写方法,解决了初始化延时、内存对齐等常见问题。

芝士奶盖发布于 2026/2/6更新于 2026/9/105.1K 浏览
基于 RT-Thread 的 STM32 开发:SD 卡与 DFS 文件系统管理

前言

在之前的 Flash 篇中介绍了使用 DFS 文件系统管理外置 Flash 芯片的存储空间,除此之外还有一种常见的外置存储设备即 SD 卡。本文基于 DFS 文件系统来管理 SD 卡。以 STM32F4 探索板为例,使用的 RT-Thread 驱动版本为 5.2.2。 关于 DFS 文件系统的详细介绍可参考相关文档,本文不再重复。

一、SD 卡概述

SD 卡(Secure Digital Memory Card)是一种基于闪存技术的便携式存储设备。主要分为以下两种规格:

  • 标准 SD 卡:尺寸为 32mm×24mm×2.1mm,多用于相机等设备。
  • microSD 卡:尺寸为 15mm×11mm×1mm,常用于手机、无人机等小型设备。

容量分类

  • SDSC(标准容量):最大支持 2GB,使用 FAT16 文件系统。
  • SDHC(高容量):容量范围为 4GB~32GB,使用 FAT32 文件系统。
  • SDXC(扩展容量):容量范围为 64GB~2TB,使用 exFAT 文件系统。

SD 卡接口示意图

SD 卡的接口主要有 SDIO 模式和 SPI 模式。下图是标准 SD 卡的两种模式的引脚定义。microSD 卡相比下少一个 VSS 引脚。

SD 卡引脚定义

因为我们使用的是操作系统,可以不用太关注底层硬件架构。简单说明一下 SDIO 模式的通讯。

SDIO 模式使用 SD 总线协议,支持 4 位数据线(DAT0-DAT3)和 1 位命令线(CMD),理论传输速率更高(UHS-I 可达 104MB/s)。SPI 模式仅使用 1 位数据线(MOSI/MISO)和 1 位时钟线(SCK),协议更简单但速率较低(通常低于 25MB/s)。

SDIO 模式需要两个时钟:

  1. 卡时钟(SDIO_CK):每个时钟周期在命令和数据线上传输一位命令和数据。对于 SD 卡,这个频率可以在 0-25MHz 之间。
  2. SDIO 适配时钟(SDIOCLK):该时钟用于驱动 SDIO 适配器,来自 PLL48CK,一般是 48MHz,并分频后产生卡时钟(SDIO_CK)。

SDIO_CK 与 SDIOCLK 关系为:

SDIOCK 公式

其中 CLKDIV 是分频系数。

二、工程创建

2.1 CubeMX 配置

STM32F4 系列芯片具有 SDIO 模式的驱动引脚。

总共有五种模式:

  1. SD 1 bit:只使用 D0 这一根数据线,兼容性最好,速度最慢。
  2. SD 4 bits Wide bus:使用 D0~D3 共 4 根数据线并行传输,理论速度是 1 位模式的 4 倍,最常用的高速模式。
  3. MMC 1 bit:仅使用 D0 数据线,针对 MMC 卡。
  4. MMC 4 bits Wide bus:使用 D0~D3 共 4 根数据线,适用于支持 4 位总线的 MMC 卡。
  5. MMC 8 bits Wide bus:使用 D0~D7 共 8 根数据线,主要用于对存储带宽要求极高的工业或专业设备。

SDIO 除了支持 SD 卡也支持 MMC 卡。我们这里用的是 SD 卡,所以选择SD 4 bits Wide bus最好。

工作设置如下:

  • Clock transition on which the bit capture is made:当前设置为 Rising transition(上升沿)。SDIO 总线的时钟和数据同步方式,标准协议规定在时钟的上升沿捕获数据。
  • SDIO Clock divider bypass:当前设置为 Disable(关闭)。开启后直接使用 SDIOCLK 作为时钟源,关闭时通过分频调整速度。
  • SDIO Clock output enable when the bus is idle:当前设置为 Disable the power save for the clock(关闭时钟省电模式)。空闲时仍会输出时钟,兼容性更好。
  • SDIO hardware flow control:当前设置为 The hardware control flow is disabled(禁用)。大多数普通场景不需要启用。
  • SDIOCLK clock divide factor:当前设置为 0。此时 SD 卡的时钟频率为 24MHz,低于 25MHz,满足要求。

CubeMX 配置

DMA 具有 SDIO、SDIO_RX 和 SDIO_TX 可以选择。资源足够下我们选择 SDIO_RX 和 SDIO_TX 双通道模式。

在时钟配置下,注意要保证红框内时钟频率为 48MHz,这个就是提供给 SDIO 的适配器时钟。

时钟配置

2.2 RT-Thread 配置

DFS 的配置内容无需过多赘述,在此需要注意的是**Enable RT_DFS_ELM_USE_EXFAT 开关**。

这个开关的作用是:让 RT-Thread 的 FatFs 组件支持 exFAT 文件系统,突破传统 FAT32 的容量和性能限制。当 SD 卡容量大于 64GB 时,使用的是 exFAT 文件系统,此时该开关必须打开。

其次是最大扇区大小设置:

  • 标准容量卡 (SDSC,≤2GB):最大支持块大小 1024 字节,但 CMD16 命令设置的块长度最大为 512 字节。
  • 高容量卡 (SDHC)、扩展容量卡 (SDXC)、超大容量卡 (SDUC):块长度固定为 512 字节,默认扇区大小均为 512 字节。

RT-Thread 配置

注意内核对象名称的最大长度默认为 8,需改为 32。因为 SD 卡设备初始化时,系统会自动创建的对象名 mmcsdhotplugmb 长度超过了这个限制,会导致系统初始化失败。

将两个 SD 卡的线程栈可以适当增大,按原先大小发现会栈溢出,导致错误。

线程栈配置

最后一个开关是使用 SDHCI 模式。如果芯片内置了 SDHCI 控制器,则可以开启,没有的话必须关闭。STM32F4 系列没有配置这个模式。

2.3 其他配置

去 board.h 中开启 BSP_USING_SDIO 宏定义,在 board.c 末尾加入 cubemx 生成的初始化函数 HAL_SD_MspInit 和 HAL_SD_MspDeInit。

此时编译文件会发生报错,原因是 'cache_buf' undeclared (first use in this function),经排查为 ALIGN 未定义。我们需要加上 ALIGN 定义。

进入文件 drivers\drv_sdio.c,在文件开头加入如下定义:

#define ALIGN(align) __attribute__((aligned(align)))

此时再编译发现报错消失,工程配置完毕。

三、代码编写

在 applications 创建下面两个文件。

3.1 sdio_sd.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date Author Notes
 * 2026-01-20 H1567 the first version
 */
#include "sdio_sd.h"
#define DBG_TAG "SDIO"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define SD_NAME "sd0"
#define SD_path "/"

void sdio_sd_init(void)
{
    rt_thread_mdelay(500);
    uint8_t dfs_flag;
    dfs_flag = dfs_mount(SD_NAME, SD_path, "elm", 0, 0);
    if(dfs_flag != 0){
        LOG_D("enable dfs mkfs");
        dfs_mkfs("elm", SD_NAME);
        dfs_flag = dfs_mount(SD_NAME, SD_path, "elm", 0, 0);
        if(dfs_flag != 0){
            LOG_D("failed to SD device dfs mount");
        }
    }
}

int fd;
char readBuf[32] = {0};
char writeBuf[32] = "Hello rt-thread!\n";

void file(int argc, char **argv)
{
    if(!rt_strcmp(argv[1], "open")){
        if(argv[2] != 0) fd = open(argv[2], O_RDWR|O_CREAT|O_APPEND);
        else {
            rt_kprintf("error\n");
        }
    } else if (!rt_strcmp(argv[1], "close")) {
        close(fd);
    } else if (!rt_strcmp(argv[1], "read")) {
        read(fd, readBuf, sizeof(readBuf));
    } else if (!rt_strcmp(argv[1], "write")) {
        write(fd, writeBuf, sizeof(writeBuf));
    } else if (!rt_strcmp(argv[1], "rename")) {
        if(argv[2] != 0) rename(argv[2], argv[3]);
        else {
            rt_kprintf("error\n");
        }
    } else if (!rt_strcmp(argv[1], "unlink")) {
        if(argv[2] != 0) unlink(argv[2]);
        else {
            rt_kprintf("error\n");
        }
    }
}
MSH_CMD_EXPORT(file, dfs file management);

struct dirent *d;
DIR *dirp;

void directory(int argc, char **argv)
{
    if(!rt_strcmp(argv[1], "mkdir")){
        if(argv[2] != 0) mkdir(argv[2], 0x777);
        else {
            rt_kprintf("error\n");
        }
    } else if (!rt_strcmp(argv[1], "rmdir")) {
        if(argv[2] != 0) rmdir(argv[2]);
        else {
            rt_kprintf("error\n");
        }
    } else if (!rt_strcmp(argv[1], "opendir")) {
        if(argv[2] != 0) dirp = opendir(argv[2]);
        else {
            rt_kprintf("error\n");
        }
    } else if (!rt_strcmp(argv[1], "closedir")) {
        closedir(dirp);
    } else if (!rt_strcmp(argv[1], "readdir")) {
        d = readdir(dirp);
        rt_kprintf("found %s\n",d->d_name);
    }
}
MSH_CMD_EXPORT(directory, dfs directory management);

注意初始化函数 sdio_sd_init 开始的 500ms 延时不可少。用户不需要配置 SDIO 的各种初始化,这些系统自动完成,但只有初始化完成后才能进行设备绑定,所以要预留 500ms 给系统。

SD 卡设备的名称得设为'sd0',换其他名称会找不到。

下面的文件操作和文件夹操作 FinSH 函数逻辑同上。

3.2 sdio_sd.h

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date Author Notes
 * 2026-01-20 H1567 the first version
 */
#ifndef APPLICATIONS_SDIO_SD_H_
#define APPLICATIONS_SDIO_SD_H_

#include <board.h>
#include <dfs_fs.h>
#include <unistd.h>

void sdio_sd_init(void);

#endif /* APPLICATIONS_SDIO_SD_H_ */

特别说明:

DFS 具有自动挂载模式,但这个模式有时会挂载失败,因此本例不使用。若使用的话在 setting 打开开关,用下面常量结构体代替初始化函数 sdio_sd_init 即可。

const struct dfs_mount_tbl mount_table[] = {
    {"sd0", "/", "elm", 0, 0},
    {0}
};

四、结果演示

运行结果

操作流程与前述 Flash 篇类似,若遇异常可尝试降低时钟频率以确保稳定性。

目录

  1. 前言
  2. 一、SD 卡概述
  3. 容量分类
  4. 二、工程创建
  5. 2.1 CubeMX 配置
  6. 2.2 RT-Thread 配置
  7. 2.3 其他配置
  8. 三、代码编写
  9. 3.1 sdio_sd.c
  10. 3.2 sdio_sd.h
  11. 四、结果演示

更多推荐文章

查看全部
  • 网络安全基础与黑客入门指南
  • 网络安全基础入门教程与学习路线规划
  • 异构算力成本健康度审计与优化实践指南
  • 家庭 AI 助手(三):QQ 机器人接入 OpenClaw
  • Windows 系统下 Java JDK 下载与安装配置
  • LeetCode 刷题记录:第 31 至 40 题
  • AFDM 与信号处理:仿射频分复用扩展 OFDM 实现场景灵活性
  • 链表分割:以给定值 x 为基准划分链表
  • Coze 低代码 AI 智能体平台全解析:100 个落地场景与发布指南
  • GitHub CLI 跨平台安装与配置指南
  • MySQL 表设计核心:深入理解三大范式
  • Flutter 返回键监听实战:双击退出与跳转桌面处理
  • 从接口文档到前端调用:Axios 封装与实战详解
  • 二叉树算法实战:美国血统与公共祖先求解
  • MAC 系统下 NS3-GYM 环境搭建与调试工具配置
  • Figma Make 实测:AI 辅助前端开发的可行性分析
  • 网络安全工程师面试真题整理(116 道)
  • LlamaFactory v0.9.4 正式发布:全面升级的 LLM 微调框架
  • OpenClaw 技能扩展实战:Tavily 联网与多维表格自动化
  • FPGA 与嵌入式开发对比:技术路线与职业前景分析

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online