基于RT-Thread的STM32开发第十二讲SD卡篇——DFS文件系统

目录

前言

一、SD卡?

容量分类

二、工程创建

[2.1 cubemx](#2.1 cubemx)

[2.2 rt-thread setting](#2.2 rt-thread setting)

[2.3 其他配置](#2.3 其他配置)

三、代码编写

[3.1 sdio_sd.c](#3.1 sdio_sd.c)

[3.2 sdio_sd.h](#3.2 sdio_sd.h)

四、结果演示


前言

在十二讲flsh篇中我们介绍了使用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的接口主要有SDIO模式和SPI模式,下图是标准SD卡的两种模式的引脚定义。microSD卡相比下少一个VSS引脚。

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

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

SDIO模式需要两个时钟

卡时钟(SDIO_CK):每个时钟周期在命令和数据线上传输一位命令和数据。对于SD卡,这个频率可以在0-25MHz之间。

SDIO适配时钟(SDIOCLK):该时钟用于驱动SDIO适配器,来自OLL48CK,一般是48MHz,并分频后产生卡时钟(SDIO_CK)。

SDIO_CK与SDIOCLK关系为:

其中CLKDIV是分频系数。

二、工程创建

2.1 cubemx

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

总共有五种模式

SD 1 bit

  • 含义 :SD 卡工作在 1 位数据总线 模式。
  • 特点
    • 只使用 D0 这一根数据线来传输数据。
    • 兼容性最好,所有 SD 卡都支持,但传输速度最慢。
    • 适合对速度要求不高、只需要基本读写的场景。

SD 4 bits Wide bus

  • 含义 :SD 卡工作在 4 位宽总线 模式。
  • 特点
    • 使用 D0~D3 共 4 根数据线并行传输数据。
    • 理论速度是 1 位模式的 4 倍,是 SD 卡最常用的高速模式。
    • 需要硬件和 SD 卡都支持 4 位总线模式,大部分现代 SD 卡都支持。

MMC 1 bit

  • 含义 :MMC 卡(MultiMediaCard)工作在 1 位数据总线 模式。
  • 特点
    • 仅使用 D0 数据线,是 MMC 卡的基础模式。
    • MMC 卡是比 SD 卡更早的存储卡标准,现在使用较少。
    • 速度和兼容性都和 SD 1 bit 类似,但仅针对 MMC 卡。

MMC 4 bits Wide bus

  • 含义 :MMC 卡工作在 4 位宽总线 模式。
  • 特点
    • 使用 D0~D3 共 4 根数据线,是 MMC 卡的高速模式。
    • 速度比 1 位模式快,但仍慢于 SD 4 bits 模式。
    • 仅适用于支持 4 位总线的 MMC 卡。

MMC 8 bits Wide bus

  • 含义 :MMC 卡工作在 8 位宽总线 模式。
  • 特点
    • 使用 D0~D7 共 8 根数据线,是 MMC 卡的最快模式。
    • 这种模式需要专门的 8 位 MMC 卡,且硬件上要引出全部 8 根数据线,在普通开发板上很少见。
    • 主要用于对存储带宽要求极高的工业或专业设备。

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

工作设置如下

Clock transition on which the bit capture is made

  • 含义:SDIO 在时钟的哪个边沿采样数据。
  • 当前设置Rising transition(上升沿)
  • 说明
    • SDIO 总线的时钟和数据同步方式,标准协议规定在时钟的上升沿捕获数据,因此这个值一般不需要修改。
    • 下降沿捕获仅用于某些特殊兼容场景,通常保持默认即可。

SDIO Clock divider bypass

  • 含义:是否绕过 SDIO 时钟分频器。
  • 当前设置Disable(关闭)
  • 说明
    • 开启后,SDIO 直接使用 SDIOCLK 作为时钟源,不再分频。
    • 关闭时,时钟频率 = SDIOCLK / (divide factor + 2),这是更常见的做法,可灵活调整时钟速度。

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(禁用)
  • 说明
    • 硬件流控通过 DAT3 引脚来控制数据传输的启停,防止数据溢出。
    • 大多数普通场景(如 SD 卡读写)不需要启用,只有在高速、高吞吐的专业应用中才会用到。

SDIOCLK clock divide factor

  • 含义:SDIO 时钟的分频系数。
  • 当前设置0
  • 说明
    • 这个值决定了最终的 SDIO 时钟频率,公式为:SDIO 时钟频率 = SDIOCLK / (divide factor + 2)
    • 当值为 0 时,时钟频率 = SDIOCLK / 2。此时SD 卡的时钟频率为24MHz,低于25MHz,满足要求

DMA具有SDIO、SDIO_RX和SDIO_TX可以选择。

SDIO

  • 含义 :这是 SDIO 外设的 双向 DMA 通道,既可以用于接收(RX)也可以用于发送(TX)。
  • 特点
    • 当 SDIO 需要在单次传输中同时进行读写时,或硬件上只分配了一个 DMA 通道给 SDIO 时,会使用这个模式。
    • 实际传输方向由 SDIO 控制器的操作命令决定,灵活性高,但需要软件在传输前后切换方向。
    • 适合对传输方向不固定、或资源受限的场景。

资源足够下我们选择SDIO_RX和SDIO_TX双通道模式。

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

2.2 rt-thread setting

DFS的配置内容就不用多说了,上文中有详细介绍。在这里需要注意的是**Enable RT_DFS_ELM_USE_EXFAT 开关**

这个开关的作用是:让 RT-Thread 的 FatFs 组件支持 exFAT 文件系统,突破传统 FAT32 的容量和性能限制。

经过前面的SD卡容量介绍可知,当SD卡容量大于64GB时,使用的是exFAT文件系统。此时该开关就必须开,低于这个容量可以不开。

其次是最大扇区大小设置如下

  • 标准容量卡 (SDSC,≤2GB)

    • 最大支持块大小:1024 字节(由 CSD 寄存器的 READ_BL_LEN 决定,READ_BL_LEN<12,最大值为 11,2^11=1024)
    • 但 CMD16 命令设置的块长度最大为512 字节
  • 高容量卡 (SDHC,2-32GB)、扩展容量卡 (SDXC,32GB-2TB)、超大容量卡 (SDUC,2TB-128TB)

    • 块长度固定为 512 字节,无论 CMD16 如何设置
    • 默认扇区大小均为512 字节,这是 SD 卡协议和文件系统(如 FAT16/FAT32、exFAT)普遍采用的标准大小

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

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

最后一个开关是使用SDHCI模式。

SDHCI 全称是 SD Host Controller Interface,是一种标准化的 SD 卡主机控制器接口规范。它比传统的 SDIO 外设更高效,支持更高的传输速率(如 UHS-I 高速模式),并且硬件集成了 DMA、中断优化等功能,性能更好。

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

2.3 其他配置

其他配置就和普通外设一样,去board.h中开启BSP_USING_SDIO宏定义,在board.c末尾加入cubemx生成的初始化函数HAL_SD_MspInit和HAL_SD_MspDeInit。

三、代码编写

在qpplications创建下面两个文件

3.1 sdio_sd.c

cpp 复制代码
/*
 * 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给系统。

SDka设备的名称得设为"sd0",换其他名称会找不到。

下面得文件操作和文件夹操作finsh函数和上文一样,不多解释。

3.2 sdio_sd.h

cpp 复制代码
/*
 * 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即可。

cpp 复制代码
const struct dfs_mount_tbl mount_table[] =
{
    {"sd0", "/", "elm", 0, 0},
    {0}
};

四、结果演示

操作内容和上差不多,有时会失败,应该是我访问速度设置太高的问题。

相关推荐
一路往蓝-Anbo2 小时前
第37期:启动流程(二):C Runtime (CRT) 初始化与重定位
c语言·开发语言·网络·stm32·单片机·嵌入式硬件
三伏5225 小时前
HAL_I2C_ER_IRQHandler函数解析
stm32·单片机·hal库
想放学的刺客6 小时前
单片机嵌入式试题(第19期)嵌入式系统故障诊断与固件升级设计
c语言·stm32·嵌入式硬件·物联网·51单片机
一路往蓝-Anbo7 小时前
第46期:低功耗设计:软硬件协奏曲
驱动开发·stm32·单片机·嵌入式硬件
不能跑的代码不是好代码7 小时前
STM32 标准外设库中关于 GPIO(通用输入输出) 模块的函数声明
stm32·单片机·嵌入式硬件
Moonquakes5407 小时前
嵌入式开发基础学习笔记(LED实验C语言实现、蜂鸣器实验、SDK裸机驱动、链接脚本、BSP工程管理)
stm32·单片机·嵌入式硬件
Zeku15 小时前
Linux内核中SPI 子系统的整体架构
stm32·freertos·linux驱动开发·linux应用开发
炸膛坦客20 小时前
FreeRTOS 学习:(二十五)任务时间统计相关 API 函数
stm32·操作系统·freertos
时光の尘21 小时前
【STM32】两万字详解SD卡移植最新版本FatFs文件系统(ff16)
stm32·mcu·dma·sd·fatfs·sdio·ff16