【第二十一章 SDIO接口(SDIO)】

news2025/6/12 23:01:40

 第二十一章 SDIO接口

目录

第二十一章 SDIO接口(SDIO)

1 SDIO 主要功能

2 SDIO 总线拓扑

3 SDIO 功能描述

3.1 SDIO 适配器

3.2 SDIOAHB 接口

4 卡功能描述

4.1 卡识别模式

4.2 卡复位

4.3 操作电压范围确认

4.4 卡识别过程

4.5 写数据块

4.6 读数据块

4.7 数据流操作,数据流写入和数据流读出(只适用于多媒体卡)

4.8 擦除:成组擦除和扇区擦除

4.9 宽总线选择和解除选择

4.10 保护管理

5 例程设计

5.1 SDIO_SDCardFatfs

6 下载验证

6.1 SDIO_SDCardFatfs


1 SDIO 主要功能

        SD/SDIOMMC 卡主机模块(SDIO)在 AHB 外设总线和多媒体卡(MMC)、SD 存储卡、SDIO 卡和 CE-ATA设备间提供了操作接口。多媒体卡系统规格书由 MMC技术委员会发布,可以在多媒体卡协会的网站上(www.mmca.org)获得。CE-ATA 系统规格书可以在 CE-ATA 工作组的网站上(www.ce-ata.org)获得。SDIO 的主要功能如下:

  • 与多媒体卡系统规格书版本 4.2 全兼容。支持三种不同的数据总线模式:1 位(默认)、4 位和8 位。
  • 与较早的多媒体卡系统规格版本全兼容(向前兼容)。
  • 与 SD 存储卡规格版本 2.0 全兼容。
  • 与 SDI/O 卡规格版本 2.0 全兼容:支持良种不同的数据总线模式:1 位(默认)和 4 位。
  • 完全支持 CE-ATA 功能(与 CE-ATA 数字协议版本 1.1 全兼容)。
  • 8 位总线模式下数据传输速率可达 48MHz。
  • 数据和命令输出使能信号,用于控制外部双向驱动器。

注: 1.SDIO 没有 SPI 兼容的通信模式

2.在多媒体卡系统规格书版本 2.11 中,定义 SD 存储卡协议是多媒体卡协议的超集。只支持 I/O模式的 SD 卡或复合卡中的 I/O 部分不能支持 SD 存储备中很多需要的命令,这里有些命令在SDI/O 设备中不起作用,如擦除命令,因此 SDIO 不支持这些命令。另外,SD 存储卡和 SDI/O 卡中有些命令是不同的SDIO 也不支持这些命令。细节可以参考 SDI/O 卡规格书版本 1.0。使用现有的 MMC 命令机制,在 MMC 接口上可以实现 CE-ATA 的支持。SDIO 接口的电气和信号定义详见MMC 参考资料。多媒体卡/SD 总线将所有卡与控制器相连。当前版本的 SDIO 在同一时间里只能支持一个 SD/SDIO/MMC4.2 卡,但可以支持多个 MMC 版本4.1 或以前版本的卡。

2 SDIO 总线拓扑

        总线上的通信是通过传送命令和数据实现。在多媒体卡/SD/SDI/O 总线上的基本操作是命令/响应结构,这样的总线操作在命令或总线机制下实现信息交换;另外,某些操作还具有数据令牌。在 SD/SDIO 存储器卡上传送的数据是以数据块的形式传输;在 MMC 上传送的数据是以数据块或数据流的形式传输;在 CE-ATA 设备上传送的数据也是以数据块的形式传输。

SDIO“无响应”和“无数据”操作

SDIO(多)数据块读操作

SDIO(多)数据块写操作

SDIO 连续读操作

SDIO 连续写操作

3 SDIO 功能描述

SDIO 包含 2 个部分:

  • --SDIO 适配器模块:实现所有 MMC/SD/SDI/O 卡的相关功能,如时钟的产生、命令和数据的传送。
  • --AHB 总线接口:操作 SDIO 适配器模块中的寄存器,并产生中断和 DMA 请求信号。

SDIO 框图

        复位后默认情况下 SDIO_D0 用于数据传输。初始化后主机可以改变数据总线的宽度。如果一个多媒体卡接到了总线上,则 SDIO_D0、SDIO_D[3:0]或 SDIO_D[7:0]可以用于数据传输。MMC 版本 V3.31 和之前版本的协议只支持 1 位数据线,所以只能用 SDIO_D0。如果一个 SD 或 SDI/O 卡接到了总线上,可以通过主机配置数据传输使用 SDIO_D0 或 SDIO_D[3:0]。所有的数据线都工作在推挽模式。

SDIO_CMD 有两种操作模式:

  • 用于初始化时的开路模式(仅用于 MMC 版本 V3.31 或之前版本)
  • 用于命令传输的推挽模式(SD/SDI/O 卡和 MMCV4.2 在初始化时也使用推挽驱动)

        SDIO_CK 是卡的时钟:每个时钟周期在命令和数据线上传输 1 位命令或数据。对于多媒体卡 V3.31协议,时钟频率可以在 0MHz 至 20MHz 间变化;对于多媒体卡 V4.0/4.2 协议,时钟频率可以在0MHz 至 48MHz 间变化;对于 SD 或 SDI/O 卡,时钟频率可以在 0MHz 至 25MHz 间变化。SDIO 使用两个时钟信号:

  • SDIO 适配器时钟(SDIOCLK=HCLK)
  • AHB 总线时钟(HCLK/2)

下表适用于多媒体卡/SD/SDI/O 卡总线:

表 SDIO 引脚定义

引脚

方向

说明

SDIO_CK

输出

多媒体卡 / SD/SDIO 卡时钟。这是从主机至卡的时钟线。

SDIO_CMD

双向

多媒体卡 / SD/SDIO 卡命令。这是双向的命令 / 响应信号线。

SDIO_D[7:0]

双向

多媒体卡 / SD/SDIO 卡数据。这些是双向的数据总线。

3.1 SDIO 适配器

下图是简化的 SDIO 适配器框图:

SDIO 适配器框图

        SDIO 适配器是多媒体/加密数字存储卡总线的主设备(主机),用于连接一组多媒体卡或加密数字存储卡,它包含以下 5 个部分:

  • 适配器寄存器模块
  • 控制单元
  • 命令通道
  • 数据通道
  • 数据 FIFO

        注: 适配器寄存器和 FIFO 使用 AHB 总线一侧的时钟(HCLK/2),控制单元、命令通道和数据通道使SDIO 适配器一侧的时钟(SDIOCLK)。

适配器寄存器模块

        适配器寄存器模块包含所有系统寄存器。该模块还产生清除多媒体卡中静态标记的信号,当在SDIO 清除寄存器中的相应位写'1'时会产生清除信号。

控制单元

控制单元包含电源管理功能和为存储器卡提供的时钟分频。共有三种电源阶段:

  • 电源关闭
  • 电源启动
  • 电源开

控制单元框图

        上图为控制单元的框图,有电源管理和时钟管理子单元。在电源关闭和电源启动阶段,电源管理子单元会关闭卡总线上的输出信号。时钟管理子单元产生和控制 SDIO_CK 信号。SDIO_CK 输出可以使用时钟分频或时钟旁路模式。下述情况下没有时钟输出:

  • 复位后
  • 在电源关闭和电源启动阶段
  • 当启动了省电模式并且卡总线处于空闲状态(命令通道和数据通道子单元进入空闲阶段后的 8个时钟周期)

命令通道

命令通道单元向卡发送命令并从卡接收响应。

SDIO适配器命令通道

命令通道状态机(CPSM)

        当写入命令寄存器并设置了使能位,开始发送命令。命令发送完成时,命令通道状态机(CPSM)设置状态标志并在不需要响应时进入空闲状态(见下图)。当收到响应后,接收到的CRC 码将会与内部产生的 CRC 码比较,然后设置相应的状态标志。

命令通道状态机(CPSM)

        当进入等待(Wait)状态时,命令定时器开始运行;当 CPSM 进入接收(Receive)状态之前,产生了超时,则设置超时标志并进入空闲(Idle)状态。

注: 命令超时固定为 64 个 SDIO_CK 时钟周期。

        如果在命令寄存器设置了中断位,则关闭定时器,CPSM 等待某一个卡发出的中断请求。如果命令寄存器中设置挂起位,CPSM 进入挂起(Pend)状态并等待数据通道子单元发出的 CmdPend 信号,在检测到 CmdPend 信号时,CPSM 进入发送(Send)状态,这将触发数据计数器发送停止命令的功能。

注: CPSM 保持在空闲状态至少 8 个 SDIO_CK 周期,以满足 NCC 和 NRC 时序限制。NCC 是两个主机命令间的最小间隔;NRC 是主机命令与卡响应之间的最小间隔。

SDIO命令传输

命令格式

        命令:命令是用于开始一项操作。主机向一个指定的卡或所有的卡发出带地址的命令或广播命令(广播命令只适合于 MMCV3.31 或之前的版本)。命令在 CMD 线上串行传送。所有命令的长度固定为 48 位,下表给出了多媒体卡、SD 存储卡和 SDIO 卡上一般的命令格式。CE-ATA 命令是 MMCV4.2 命令的扩充,所以具有相同的格式。命令通道操作于半双工模式,这样命令和响应可以分别发送和接收。如果 CPSM 不处在发送状态,SDIO_CMD 输出处于高阻状态,如图 所示。SDIO_CMD 上的数据与 SDIO_CK 的上升沿同步。

宽度

数值

说明

47

1

0

开始位

46

1

1

传输位

[45:40]

6

-

命令索引

[39:8]

32

-

参数

[7:1]

7

-

CRC7

0

1

1

结束位

        响应:响应是由一个被指定地址的卡发送到主机,对于 MMCV3.31 或以前版本所有的卡同时发送响应;响应是对先前接收到命令的一个应答。响应在 CMD 线上串行传送。

SDIO 支持 2 种响应类型,2 种类型都有 CRC 错误检测:

  • 48 位短响应
  • 136 位长响应

注: 如果响应不包含 CRC(如 CMD1 的响应),设备驱动应该忽略 CRC 失败状态

短响应格式

宽度

数值

说明

47

1

0

开始位

46

1

1

传输位

[45:40]

6

-

命令索引

[39:8]

32

-

参数

[7:1]

7

-

CRC7 (或 1111111)

0

1

1

结束位

长响应格式

宽度

数值

说明

135

1

0

开始位

134

1

0

传输位

[133:128]

6

111111

保留

[127:1]

127

-

CID 或 CSD (包含内部 CRC7)

0

1

1

结束位

        命令寄存器包含命令索引(发至卡的 6 位)和命令类型;命令本身决定了是否需要响应和响应的类型、48 位还是 136 位(见 20.9.4 节)。命令通道中的状态标志示于下表:

命令通道状态标志

标志

说明

CMDRCEND

响应的 CRC 正确

CCRCFAIL

响应的 CRC 错误

CMDSENT

命令 (不需要响应的命令) 已经送出

CTIMEOUT

响应超时

CMDACT

正在发送命令

        CRC发生器计算 CRC码之前所有位的 CRC校验和,包括开始位、发送位、命令索引和命令参数(或卡状态)。对于长响应格式,CRC 校验和计算的是 CID 或 CSD 的前 120 位;注意,长响应格式中的开始位、传输位和 6 个保留位不参与 CRC 计算。

CRC 校验和是一个 7 位的数值:

CRC[6:0]=余数[(M(x)*x7)/G(x)]

G(x)=x7+x3+1

M(x)=(开始位)*x39+…+(CRC 前的最后一位)*x0,或

M(x)=(开始位)*x119+…+(CRC 前的最后一位)*x0,或

数据通道

        数据通道子单元在主机与卡之间传输数据。下图是数据通道的框图。

数据通道

        在时钟控制寄存器中可以配置卡的数据总线宽度。如果选择了 4 位总线模式,则每个时钟周期四条数据信号线(SDIO_D[3:0])上将传输 4 位数据;如果选择了 8 位总线模式,则每个时钟周期八条数据信号线(SDIO_D[7:0])上将传输 8 位数据;如果没有选择宽总线模式,则每个时钟周期只在SDIO_D0 上传输 1 位数据。根据传输的方向(发送或接收),使能时数据通道状态机(DPSM)将进入 Wait_S 或 Wait_R 状态:

  • 发送:DPSM 进入 Wait_S 状态。如果发送 FIFO 中有数据,则 DPSM 进入发送状态,同时数据通道子单元开始向卡发送数据。
  • 接收:DPSM 进入 Wait_R 状态并等待开始位;当收到开始位时,DPSM 进入接收状态,同时数据通子单元开始从卡接收数据。

数据通道状态机(DPSM)

        DPSM 工作在 SDIO_CK 频率,卡总线信号与 SDIO_CK 的上升沿同步。DPSM 有 6 个状态,如下图所示:

数据通道状态机(DPSM)

  • 空闲(Idle):数据通道不工作,SDIO_D[7:0]输出处于高阻状态。当写入数据控制寄存器并设置使能位时,DPSM 为数据计数器加载新的数值,并依据数据方向位进入 Wait_S 或 Wait_R状态。
  • Wait_R:如果数据计数器等于 0,当接收 FIFO 为空时 DPSM 进入到空闲(Idle)状态。如果数据计数器不等于 0,DPSM 等待 SDIO_D上的开始位。如果 DPSM 在超时之前接收到一个开始位,它会进入接收(Receive)状态并加载数据块计数器。如果 DPSM 在检测到一个开始位前出现超时,或发生开始位错误,DPSM 将进入空闲状态并设置超时状态标志。
  • 接收(Receive):接收到的串行数据被组合为字节并写入数据 FIFO。根据数据控制寄存器中传输模式位的设置,数据传输模式可以是块传输或流传输:
    • 在块模式下,当数据块计数器达到 0 时,DPSM 等待接收 CRC 码,如果接收到的代码与内部产生的 CRC 码匹配,则 DPSM 进入 Wait_R 状态,否则设置 CRC 失败状态标志同时 DPSM进入到空闲状态。
    • 在流模式下,当数据计数器不为 0 时,DPSM 接收数据;当计数器为 0 时,将移位寄存器中的剩余数据写入数据 FIFO,同时 DPSM 进入 Wait_R 状态。如果产生了 FIFO 上溢错误,DPSM 设置 FIFO 的错误标志并进入空闲状态。
  • Wait_S:如果数据计数器为 0,DPSM 进入空闲状态;否则 DPSM 等待数据 FIFO 空标志消失后,进入发送状态。

注: DPSM 会在 Wait_S 状态保持至少 2 个时钟周期,以满足 NWR 的时序要求,NWR 是接收到卡的响应至主机开始数据传输的间隔。

  • 发送(Send):DPSM 开始发送数据到卡设备。根据数据控制寄存器中传输模式位的设置,数据传输模式可以是块传输或流传输:
    • 在块模式下,当数据块计数器达到 0 时,DPSM 发送内部产生的 CRC 码,然后是结束位,并进入繁忙状态。
    • 在流模式下,当使能位为高同时数据计数器不为 0 时,DPSM 向卡设备发送数据,然后进入空闲状态。如果产生了 FIFO 下溢错误,DPSM 设置 FIFO 的错误标志并进入空闲状态。
  • 繁忙(Busy):DPSM 等待 CRC 状态标志:
    • 如果没有接收到正确的 CRC 状态,则 DPSM 进入空闲状态并设置 CRC 失败状态标志。
    • 如果接收到正确的 CRC 状态,则当 SDIO_D0 不为低时(卡不繁忙)DPSM 进入 Wait_S 状态。

当 DPSM 处于繁忙状态时发生了超时,DPSM 则设置数据超时标志并进入空闲状态。

当 DPSM 处于 Wait_R 或繁忙状态时,数据定时器被使能,并能够产生数据超时错误:

    • 发送数据时,如果 DPSM 处于繁忙状态超过程序设置的超时间隔,则产生超时。
    • 接收数据时,如果未收完所有数据,并且 DPSM 处于 Wait_R 状态超过程序设置的超时间隔,则产生超时。
  • 数据:数据可以从主机传送到卡,也可以反向传输。数据在数据线上传输。数据存储在一个32 字的 FIFO 中,每个字为 32 位宽。

数据令牌格式

说明

开始位

数据

CRC16

结束位

块数据

0

-

1

流数据

0

-

1

数据 FIFO

        数据 FIFO(先进先出)子单元是一个具有发送和接收单元的数据缓冲区。FIFO 包含一个每字 32 位宽、共 32 个字的数据缓冲区,和发送与接收电路。因为数据 FIFO 工作在AHB 时钟区域(HCLK/2),所有与 SDIO 时钟区域(SDIOCLK)连接的信号都进行了重新同步。依据 TXACT 和 RXACT 标志,可以关闭 FIFO、使能发送或使能接收。TXACT 和 RXACT 由数据通道子单元设置而且是互斥的:

    • 当 TXACT 有效时,发送 FIFO 代表发送电路和数据缓冲区
    • 当 RXACT 有效时,接收 FIFO 代表接收电路和数据缓冲区
  • 发送 FIFO:当使能了 SDIO 的发送功能,数据可以通过 AHB 接口写入发送 FIFO。

发送 FIFO 有 32 个连续的地址。发送 FIFO 中有一个数据输出寄存器,包含读指针指向的数据字。当数据通道子单元装填了移位寄存器后,它移动读指针至下个数据并传输出数据。如果未使能发送 FIFO,所有的状态标志均处于无效状态。当发送数据时,数据通道子单元设置TXACT 为有效。

发送 FIFO 状态标志

标志

说明

TXFIFOF

当所有 32 个发送 FIFO 字都有有效的数据时,该标志为高。

TXFIFOE

当所有 32 个发送 FIFO 字都没有有效的数据时,该标志为高。

TXFIFOHE

当 8 个或更多发送 FIFO 字为空时,该标志为高。该标志可以作为 DMA 请求。

TXDAVL

当发送 FIFO 包含有效数据时,该标志为高。该标志的意思刚好与 TXFIFOE 相反。

TXUNDERR

当发生下溢错误时,该标志为高。写入 SDIO 清除寄存器时清除该标志。

  • 接收 FIFO:当数据通道子单元接收到一个数据字,它会把数据写入 FIFO,写操作结束后,写指针自动加一;在另一端,有一个读指针始终指向FIFO中的当前数据。如果关闭了接收FIFO,所有的状态标志会被清除,读写指针也被复位。在接收到数据时数据通道子单元设置 RXACT。下表列出了接收 FIFO 的状态标志。通过 32 个连续的地址可以访问接收 FIFO。

接收 FIFO 状态标志

标志

说明

RXFIFOF

当所有 32 个接收 FIFO 字都有有效的数据时,该标志为高。

RXFIFOE

当所有 32 个接收 FIFO 字都没有有效的数据时,该标志为高。

RXFIFOHF

当 8 个或更多接收 FIFO 字有有效的数据时,该标志为高。该标志可以作为 DMA 请求。

RXDAVL

当接收 FIFO 包含有效数据时,该标志为高。该标志的意思刚好与 RXFIFOE 相反。

RXOVERR

当发生上溢错误时,该标志为高。写入 SDIO 清除寄存器时清除该标志。

3.2 SDIOAHB 接口

        AHB 接口产生中断和 DMA 请求,并访问 SDIO 接口寄存器和数据 FIFO。它包含一个数据通道、寄存器译码器和中断/DMA 控制逻辑。

SDIO 中断

        当至少有一个选中的状态标志为高时,中断控制逻辑产生中断请求。有一个屏蔽寄存器用于选择可以产生中断的条件,如果设置了相应的屏蔽标志,则对应的状态标志可以产生中断。

SDIO/DMA 接口:在 SDIO 和存储器之间数据传输的过程

        在下面的例子中,主机控制器使用 CMD24(WRITE_BLOCK)从主机传送 512 字节到 MMC 卡,DMA 控制器用于从存储器向 SDIO 的 FIFO 填充数据。

  1. 执行卡识别过程。
  2. 提高 SDIO_CK 频率。
  3. 发送 CMD7 命令选择卡。
  4. 按下述步骤配置 DMA2:
    1. 使能 DMA2 控制器并清除所有的中断标志位。
    2. 设置 DMA2 通道 4 的源地址寄存器为存储器缓冲区的基地址,DMA2 通道 4 的目标地址寄存器为SDIO_FIFO 寄存器的地址。
    3. 设置 DMA2 通道 4 控制寄存器(存储器递增,非外设递增,外设和源的数据宽度为字宽度)。
    4. 使能 DMA2 通道 4。
  5. 发送 CMD24(WRITE_BLOCK),操作如下:
    1. 设置 SDIO 数据长度寄存器(SDIO 数据时钟寄存器应该在执行卡识别过程之前设置好)。
    2. 设置 SDIO 参数寄存器为卡中需要传送数据的地址。
    3. 设置 SDIO 命令寄存器:CmdIndex 置为 24(WRITE_BLOCK);WaitRest 置为 1(SDIO 卡主机等待响应);CPSMEN 置为 1(使能 SDIO 卡主机发送命令),保持其它域为他们的复位值。
    4. 等待 SDIO_STA[6]=CMDREND 中断,然后设置 SDIO 数据寄存器:DTEN 置为 1(使能 SDIO卡主机发送数据);DTDIR 置为 0(控制器至卡方向);DTMODE 置为 0(块数据传送);DMAEN 置为 1(使能 DMA);DBLOCKSIZE 置为 9(512 字节);其它域不用设置。
    5. 等待 SDIO_STA[10]=DBCKEND。
  6. 查询 DMA 通道的使能状态寄存器,确认没有通道仍处于使能状态。

4 卡功能描述

4.1 卡识别模式

        在卡识别模式,主机复位所有的卡、检测操作电压范围、识别卡并为总线上每个卡设置相对地址(RCA)。在卡识别模式下,所有数据通信只使用命令信号线(CMD)。

4.2 卡复位

        GO_IDLE_STATE 命令(CMD0)是一个软件复位命令,它把多媒体卡和 SD 存储器置于空闲状态。

        IO_RW_DIRECT 命令(CMD52)复位 SDI/O 卡。上电后或执行 CMD0 后,所有卡的输出端都处于高阻状态,同时所有卡都被初始化至一个默认的相对卡地址(RCA=0x0001)和默认的驱动器寄存器设置(最低的速度,最大的电流驱动能力)。

4.3 操作电压范围确认

        所有的卡都可以使用任何规定范围内的电压与 SDIO卡主机通信,可支持的最小和最大电压 VDD数值由卡上的操作条件寄存器(OCR)定义。内部存储器存储了卡识别号(CID)和卡特定数据(CSD)的卡 ,仅能在数据传输 VDD 条件下传送这些信息。

        当 SDIO 卡主机模块与卡的 VDD 范围不一致时,卡将不能完成识别周期,也不能发送 CSD数据;因此,在 VDD 范围不匹配时,SDIO 卡主机可以用下面几个特殊命令去识别和拒绝卡:SEND_OP_COND(CMD1)、SD_APP_OP_COND(SD 存储卡的 ACMD41)和 IO_SEND_OP_COND(SDI/O卡的 CMD5)。

        SDIO 卡主机在执行这几个命令时会产生需要的 VDD 电压。不能在指定的电压范围进行数据传输的卡,将从总线断开并进入非激活状态。使用这些不包含电压范围作为操作数的命令,SDIO 卡主机能够查询每个卡并在确定公共的电压范围前,把不在此范围内的卡置于非激活状态。当 SDIO 卡主机能够选择公共的电压范围或用户需要知道卡是否能用时,SDIO 卡主机可以进行这样的查询。

4.4 卡识别过程

        多媒体卡和 SD 卡的卡识别过程是有区别的;对于多媒体卡,卡识别过程以时钟频率 Fod 开始,所有 SDIO_CMD 输出为开路驱动,允许在这个过程中的卡的并行连接,识别过程如下:

1. 总线被激活。

2. SDIO 卡主机广播发送 SEND_OP_COND(CMD1)命令,并接收操作条件。

3. 得到的响应是所有卡的操作条件寄存器内容的“线与”。

4. 不兼容的卡会被置于非激活状态。

5. SDIO 卡主机广播发送 ALL_SEND_CID(CMD2)至所有激活的卡。

6. 所有激活的卡同时串行地发送他们的 CID 号,那些检测到输出的 CID 位与命令线上的数据不相符的卡必须停止发送,并等待下一个识别周期。最终只有一个卡能够成功地传送完整的CID 至 SDIO 卡主机并进入识别状态。

7. SDIO 卡主机发送 SET_RELATIVE_ADDR(CMD3)命令至这个卡,这个新的地址被称为相对卡地址(RCA),它比 CID 短,用于对卡寻址。至此,这个卡转入待机状态,并不再响应新的识别过程,同时它的输出驱动从开路转变为推挽模式。

SDIO 卡主机重复上述步骤 5 至 7,直到收到超时条件。对于 SD 卡而言,卡识别过程以时钟频率 Fod 开始,所有 SDIO_CMD 输出为推挽驱动而不是开路驱动,识别过程如下:

1. 总线被激活

2. SDIO 卡主机广播发送 SEND_APP_OP_COND(ACMD41)命令

3. 得到的响应是所有卡的操作条件寄存器的内容

4. 不兼容的卡会被置于非激活状态

5. SDIO 卡主机广播发送 ALL_SEND_CID(CMD2)至所有激活的卡

6. 所有激活的卡发送回他们唯一卡识别号(CID)并进入识别状态。

7. SDIO 卡主机发送 SET_RELATIVE_ADDR(CMD3)命令和一个地址到一个激活的卡,这个新的地址被称为相对卡地址(RCA),它比 CID 短,用于对卡寻址。至此,这个卡转入待机状态。SDIO卡主机可以再次发送该命令更改 RCA,卡的 RCA 将是最后一次的赋值。

8. SDIO 卡主机对所有激活的卡重复上述步骤 5 至 7。

对于 SDI/O 卡而言,卡识别过程如下:

1. 总线被激活

2. SDIO 卡主机发送 IO_SEND_OP_COND(CMD5)命令

3. 得到的响应是卡的操作条件寄存器的内容

4. 不兼容的卡会被置于非激活状态

5. SDIO 卡主机发送 SET_RELATIVE_ADDR(CMD3)命令和一个地址到一个激活的卡,这个新的地址被称为相对卡地址(RCA),它比 CID 短,用于对卡寻址。至此,这个卡转入待机状态。SDIO卡主机可以再次发送该命令更改 RCA,卡的 RCA 将是最后一次的赋值。

4.5 写数据块

        执行写数据块命令(CMD24-27)时,主机把一个或多个数据块从主机传送到卡中,同时在每个数据块的末尾传送一个 CRC 码。一个支持写数据块命令的卡应该始终能够接收由 WRITE_BL_LEN 定义的数据块。如果CRC校验错误,卡通过SDIO_D信号线指示错误,传送的数据被丢弃而不被写入,所有后续(在多块写模式下)传送的数据块将被忽略。

        如果主机传送部分数据,而累计的数据长度未与数据块对齐,当不允许块错位(未设置 CSD 的参数WRITE_BLK_MISALIGN),卡将在第一个错位的块之前检测到块错位错误(设置状态寄存器中的ADDRESS_ERROR 错误位)。当主机试图写一个写保护区域时,写操作也会被中止,此时卡会设置WP_VIOLATION 位。设置 CID 和 CSD 寄存器不需要事先设置块长度,传送的数据也是通过 CRC 保护的。如果 CSD 或CID 寄存器的部分是存储在 ROM 中,则这个不能更改的部分必须与接收缓冲区的对应部分相一致,如果有不一致之处,卡将报告一个错误同时不修改任何寄存器的内容。有些卡需要长的甚至不可预计的时间完成写一个数据块,在接收一个数据块并完成 CRC 检验后,卡开始写操作,如果它的写缓冲区已经满并且不能再从新的 WRITE_BLOCK 命令接受新的数据时,它会把 SDIO_D 信号线拉低。

        主机可以在任何时候使用 SEND_STATUS(CMD13)查询卡的状态,卡将返回当前状态。READY_FOR_DATA 状态位指示卡是否可以接受新的数据或写操作是否还在进行。主机可以使用CMD7(选择另一个卡)不选中某个卡,而把这个卡置于断开状态,这样可以释放 SDIO_D 信号线而不中断未完成的写操作;当重新选择了一个卡,如果写操作仍然在进行并且写缓冲区仍不能使用,它会重新通过拉低 SDIO_D 信号线指示忙的状态。

4.6 读数据块

        在读数据块模式下,数据传输的基本单元是数据块,它的大小在 CSD 中(READ_BL_LEN)定义。如果设置了 READ_BL_PARTIAL,同样可以传送较小的数据块,较小数据块是指开始和结束地址完全包含在一个物理块中,READ_BL_LEN 定义了物理块的大小。为保证数据传输的正确,每个数据块后都有一个 CRC 校验码。CMD17(READ_SINGLE_BLOCK)启动一次读数据块操作,在传输结束后卡返回到发送状态。

        CMD18(READ_MULTIPLE_BLOCK)启动一次连续多个数据块的读操作。主机可以在多数据块读操作的任何时候中止操作,而不管操作的类型。发送停止传输命令即可中止操作。如果在多数据块读操作中(任一种类型)卡检测到错误(例如:越界、地址错位或内部错误),它将停止数据传输并仍处于数据状态;此时主机必须发送停止传输命令中止操作。在停止传输命令的响应中报告读错误。如果主机发送停止传输命令时,卡已经传输完一个确定数目的多个数据块操作中的最后一个数据块,因为此时卡已经不在数据状态,主机会得到一个非法命令的响应。如果主机传输部分数据块,而累计的数据长度不能与物理块对齐同时不允许块错位,卡会在出现第一个未对齐的块时检测出一个块对齐错误,并在状态寄存器中设置 ADDRESS_ERROR 错误标志。

4.7 数据流操作,数据流写入和数据流读出(只适用于多媒体卡)

        在数据流模式,数据按字节传输,同时每个数据块后没有 CRC。数据流写(只适用于多媒体卡)WRITE_DAT_UNTIL_STOP(CMD20)开始从 SDIO 卡主机至卡的数据传输,从指定的地址开始连续传输直到 SDIO 卡主机发出一个停止命令。如果允许部分数据块传输(设置了 CSD 参 数WRITE_BL_PARTIAL),则数据流可以在卡的地址空间中的任意地址开始和停止,否则数据流只能在数据块的边界开始和停止。因为传输的数据数目没有事先设定,不能使用 CRC 校验。

        如果发送数据时达到了存储器的最大地址,即使 SDIO 卡主机没有发送停止命令,随后传输的数据也会被丢弃。

数据流写操作的最大时钟频率可以通过下式计算:

  • Maximumspeed=最大写频率
  • TRANSPEED=最大数据传输率
  • writebllen=最大写数据块长度
  • NSAC=以 CLK 周期计算的数据读操作时间 2
  • TAAC=数据读操作时间 1
  • R2WFACTOR=写速度因子

        如果主机试图使用更高的频率,卡可能不能处理数据并停止编程,同时在状态寄存器中设置OVERRUN 错误位,丢弃所有随后传输的数据并(在接收数据状态)等待停止命令。如果主机试图写入一个写保护区域,写操作将被中止,同时卡将设置 WP_VIOLATION 位。数据流读(只适用于多媒体卡)READ_DAT_UNTIL_STOP(CMD11)控制数据流数据传输。这个命令要求卡从指定的地址读出数据,直到 SDIO 卡主机发送 STOP_TRANSMISSION(CMD12)。

        因为串行命令传输的延迟,停止命令的执行会有延迟,数据传送会在停止命令的结束位后停止。如果发送数据时达到了存储器的最大地址,SDIO 卡主机没有发送命令,随后传输的数据将是无效数据。

数据流读操作的最大时钟频率可以通过下式计算:

  • Maximumspeed=最大写频率
  • TRANSPEED=最大数据传输率
  • readbllen=最大读数据块长度
  • NSAC=以 CLK 周期计算的数据读操作时间 2
  • TAAC=数据读操作时间 1
  • R2WFACTOR=写速度因子

        如果主机试图使用更高的频率,卡将不能处理数据传输,此时卡在状态寄存器中设置 UNDERRUN错误位,中止数据传输并在数据状态等待停止命令。

4.8 擦除:成组擦除和扇区擦除

        多媒体卡的擦除单位是擦除组,擦除组是以写数据块计算,写数据块是卡的基本写入单位。擦除组的大小是卡的特定参数,在 CSD 中定义。

主机可以擦除一个连续范围的擦除组,开始擦除操作有三个步骤。

        首先,主机使用 ERASE_GROUP_START(CMD35)命令定义连续范围的开始地址,然后使用ERASE_GROUP_END(CMD36)命令定义连续范围的结束地址,最后发送擦除命令 ERASE(CMD38)开始擦除操作。擦除命令的地址域是以字节为单位的擦除组地址。卡会舍弃未与擦除组大小对齐的部分,把地址边界对齐到擦除组的边界。如果未按照上述步骤收到了擦除命令,卡在状态寄存器中设置 ERASE_SEQ_ERROR 位,并重新等待第一个步骤。如果收到了除 SEND_STATUS 和擦除命令之外的其它命令,卡在状态寄存器中设置 ERASE_RESET位,解除擦除序列并执行新的命令。如果擦除范围包含了写保护数据块,这些块不被擦除,只有未保护的块被擦除,同时卡在状态寄存器中设置 WP_ERASE_SKIP 状态位。在擦除过程中,卡拉低 SDIO_D 信号。实际的擦除时间可能很长,主机可以使用 CMD7 解除卡的选择。

4.9 宽总线选择和解除选择

        可以通过 SET_BUS_WIDTH(ACMD6)命令选择或不选择宽总线(4 位总线宽度)操作模式,上电后或GO_IDLE_STATE(CMD0)命令后默认的总线宽度为 1 位。SET_BUS_WIDTH(ACMD6)命令仅在传输状态时有效,即只有在使用 SELECT/DESELECT_CARD(CMD7)命令选择了卡后才能改变总线宽度。

4.10 保护管理

SDIO 卡主机模块支持三种保护方式:

1. 内部卡保护(卡内管理)

2. 机械写保护开关(仅由 SDIO 卡主机模块管理)

3. 密码管理的卡锁操作

内部卡的写保护

        卡的数据可以被保护不被覆盖或擦除。在 CSD 中永久地或临时地设置写保护位,生产厂商或内容提供商可以永久地对整个卡施行写保护。对于支持在 CSD 中设置 WP_GRP_ENABLE 位从而提供一组扇区写保护的卡,部分数据可以被保护,写保护可以通过程序改变。写保护的基本单位是 CSD参数 WP_GRP_SIZE 个扇区。

        SET_WRITE_PROT 和 CLR_WRITE_PROT 命令控制指定组的保护,SEND_WRITE_PROT 命令与单数据块读命令类似,卡送出一个包含 32 个写保护位(代表从指定地址开始的 32 个写保护组)的数据块,跟着一个 16 位的 CRC 码。写保护命令的地址域是一个以字节为单位的组地址。卡将截断所有组大小以下的地址。

机械写保护开关

        在卡的侧面有一个机械的滑动开关,允许用户设置或清除卡的写保护。当滑动开关置于小窗口打开的位置时,卡处于写保护状态,当滑动开关置于小窗口关闭的位置时,可以更改卡中内容。在卡的插槽上的对应部位也有一个开关指示 SDIO 卡主机模块,卡是否处于写保护状态。卡的内部电路不知道写保护开关的位置。

密码保护

        密码保护功能允许 SDIO 卡主机模块使用密码对卡实行上锁或解锁。密码存储在 128 位的 PWD 寄存器中,它的长度设置在 8 位的 PWD_LEN 寄存器中。这些寄存器是不可挥发的,即掉电后它们的内容不丢失。已上锁的卡能够响应和执行相应的命令,即允许 SDIO 卡主机模块执行复位、初始化和查询状态等操作,但不允许操作卡中的数据。当设置了密码后(即 PWD_LEN 的数值不为 0),上电后卡自动处于上锁状态。

        正如 CSD 和 CID 寄存器写命令,上锁/解锁命令仅在传输状态下有效,在这个状态下,命令中没有地址参数,但卡已经被选中。卡的上锁/解锁命令具有单数据块写命令的结构和总线操作类型,传输的数据块包含所有命令所需要的信息(密码设置模式、PWD内容和上锁/解锁指示)。在发送卡的上锁/解锁命令之前,命令数据块的长度由 SDIO 卡主机模块定义,命令结构示于表 110。

位的设置如下:

  • ERASE:设置该位将执行强制擦除,所有其它位必须为 0,只发送命令字节。
  • LOCK_UNLOCK:设置该位锁住卡,LOCK_UNLOCK 与 SET_PWD 可以同时设置,但不能与CLR_PWD 同时设置。
  • CLR_PWD:设置该位清除密码数据。
  • SET_PWD:设置该位将密码数据保存至存储器。
  • PWD_LEN:以字节为单位定义密码的长度。
  • PWD:密码(依不同的命令,新的密码或正在使用的密码)

以下几节列出了设置/清除密码、上锁/解锁和强制擦除的命令序列。

设置密码

1. 选择一个卡(SELECT/DESELECT_CARD,CMD7)。

2. 定义要在 8 位的卡上锁/解锁模式下发送的数据块长度(SET_BLOCKLEN,CMD16),8 位的PWD_LEN,新密码的字节数目。当更换了密码后,发送命令的数据块长度必须同时考虑新旧密码的长度。

3. 以合适的数据块长度在数据线上发送 LOCK/UNLOCK(CMD42)命令,并包含 16 位的 CRC 码。数据块包含了操作模式(SET_PWD=1)、长度(PWD_LEN)和密码(PWD)。当更换了密码后,长度数值(PWD_LEN)包含了新旧两个密码的长度,PWD 域包含了旧的密码(正在使用的)和新的密码。

4. 当旧的密码匹配后,新的密码和它的长度被分别存储在 PWD 和 PWD_LEN 域。如果送出的旧密码与期望的密码(长度或内容)不吻合,则设置状态寄存器中的 LOCK_UNLOCK_FAILED 错误位,同时密码不变。密码长度域(PWD_LEN)指示当前是否设置了密码,如果该域为非零,则表示使用了密码,卡在上电时自动上锁。在不断电的情况下,如果设置了密码,可以通过设置 LOCK_UNLOCK 位或发送一个额外的上锁命令,立即锁住卡。

清除密码

1. 选择一个卡(SELECT/DESELECT_CARD,CMD7)。

2. 定义要在 8 位的卡上锁/解锁模式下发送的数据块长度(SET_BLOCKLEN,CMD16),8 位的PWD_LEN,当前使用密码的字节数目。

3. 当密码匹配后,PWD 域被清除同时 PWD_LEN 被设为 0。如果送出的密码与期望的密码(长度或内容)不吻合,则设置状态寄存器中的 LOCK_UNLOCK_FAILED 错误位,同时密码不变。

卡上锁

1. 选择一个卡(SELECT/DESELECT_CARD,CMD7)

2. 定义要在 8 位的卡上锁/解锁模式(见表 110 的字节 0)下发送的数据块长度(SET_BLOCKLEN,CMD16),8 位的 PWD_LEN,和当前密码的字节数目。

3. 以合适的数据块长度在数据线上发送 LOCK/UNLOCK(CMD42)命令,并包含 16 位的 CRC 码。数据块包含了操作模式(LOCK_UNLOCK=1)、长度(PWD_LEN)和密码(PWD)。

4. 当密码匹配后,卡被上锁并则设置状态寄存器中的 CARD_IS_LOCKED 状态位。如果送出的密码与期望的密码(长度或内容)不吻合,则设置状态寄存器中的 LOCK_UNLOCK_FAILED 错误位,同时上锁操作失败。设置密码和为卡上锁可以在同一个操作序列中进行,此时 SDIO 卡主机模块按照前述的步骤设置密码,但在发送新密码命令的第 3 步需要设置 LOCK_UNLOCK 位。如果曾经设置过密码(PWD_LEN 不为 0),卡会在上电复位时自动地上锁。对已经上锁的卡执行上锁操作或对没有密码的卡执行上锁操作会导致失败,并设置状态寄存器中的LOCK_UNLOCK_FAILED 错误位。

卡解锁

1. 选择一个卡(SELECT/DESELECT_CARD,CMD7)

2. 定义要在 8 位的卡上锁/解锁模式下发送的数据块长度(SET_BLOCKLEN,CMD16),8 位的 PWD_LEN,和当前密码的字节数目。

3. 以合适的数据块长度在数据线上发送 LOCK/UNLOCK(CMD42)命令,并包含 16 位的 CRC 码。数据块包含了操作模式(LOCK_UNLOCK=0)、长度(PWD_LEN)和密码(PWD)。

4. 当密码匹配后,卡锁被解除,同时状态寄存器中的 CARD_IS_LOCKED 位被清除。如果送出的密码与期望的密码(长度或内容)不吻合,则设置状态寄存器中的 LOCK_UNLOCK_FAILED 错误位,同时卡仍保持上锁状态。解锁状态只在当前的供电过程中有效,只要不清除 PWD 域,下次上电后卡会被自动上锁。试图对

已经解了锁的卡执行解锁操作会导致操作失败,并设置状态寄存器中的 LOCK_UNLOCK_FAILED 错误位。

强制擦除

如果用户忘记了密码(PWD 的内容),可以在清除卡中的所有内容后使用卡。强制擦除操作擦除所

有卡中的数据和密码。

1. 选择一个卡(SELECT/DESELECT_CARD,CMD7)

2. 设置发送的数据块长度(SET_BLOCKLEN,CMD16)为 1,仅发送 8 位的卡上锁/解锁字节。

3. 以合适的数据块长度在数据线上发送 LOCK/UNLOCK(CMD42)命令,并包含 16 位的 CRC 码。数据块包含了操作模式(ERASE=1)所有其它位为 0。

当 ERASE 位是数据域中仅有的位时,卡中的所有内容将被擦除,包括 PWD 和 PWD_LEN 域,同时卡不再被上锁。如果有任何其它位不为 0,则设置状态寄存器中的LOCK_UNLOCK_FAILED 错误位,卡中的数据保持不变,同时卡仍保持上锁状态。试图对已经解了锁的卡执行擦除操作会导致操作失败,并设置状态寄存器中的LOCK_UNLOCK_FAILED 错误位。

5 例程设计

5.1 SDIO_SDCardFatfs

        该程序通过清晰的模块化设计,实现了对 SD 卡 FATFS 文件系统的全面测试,适用于嵌入式系统存储功能验证和教学演示。

程序功能概述

1.硬件初始化

  • 系统时钟、延时函数、串口(UART1)初始化。
  • SD卡通过SDIO接口初始化,支持FATFS文件系统。

2.用户交互


    while (1)
    {
        cmd = GetCmd();
        switch (cmd)
        {
        case '1': {
            printf("1.--->>>FatfsTest\r\n");
            FatfsTest();
            TestList();
            break;
        }
        case '2': {
            printf("2.--->>>FatfsBigDataTest\r\n");
            FatfsBigDataTest();
            TestList();
            break;
        }
        case '3': {
            printf("3.--->>>ViewRootDir\r\n");
            ViewRootDir();
            TestList();
            break;
        }
        case '4': {
            printf("4.--->>>CreateDir\r\n");
            CreateDir();
            TestList();
            break;
        }
        case '5': {
            printf("5.--->>>DeleteDirFile\r\n");
            DeleteDirFile();
            TestList();
            break;
        }
        }
    }
}

void TestList(void)
{
    printf("/***************************SD Card Test*******************************/\n");
    printf("==========================List==========================\n");
    printf("1: Create a new file (FatFs read-write test file.txt) for read-write testing\n");
    printf("2: Read and write large amounts of data (FatFs read and write test file .txt), perform read and write tests\n");
    printf("3: Show the file test in the root directory of the SD Card\n");
    printf("4: Create directory(/Dir1,/Dir1/Die1_1,/Dir2)\n");
    printf("5: Delete files and directories (/Dir1,/Dir1/Dir1_1,/Dir2, FatFs read and write test files.txt)\n");
    printf("****************************************************************************/\n");
}

void SDInfoShow(void)
{
    printf("/***************************SD Info Show*******************************/\n");
    printf("SDCardInfo.CardType : %d\n", SDCardInfo.CardType);
    printf("SDCardInfo.CardCapacity : %lld Byte\n", (SDCardInfo.CardCapacity));
    printf("SDCardInfo.CardBlockSize : %d Byte\n", SDCardInfo.CardBlockSize);
}

uint8_t GetCmd(void)
{
    uint8_t tmp = 0;

    if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
    {
        tmp = USART_ReceiveData(USART1);
    }
    return tmp;
}

通过串口接收用户命令(1-5),执行对应操作:

  • 1:基础文件操作测试(创建文件、读写数据)。
  • 2:大数据读写性能测试(2MB文件)。
  • 3:显示SD卡根目录内容。
  • 4:创建多级目录(/Dir1, /Dir1/Dir1_1, /Dir2)。
  • 5:删除目录及测试文件。

3.关键函数

void FatfsTest(void)
{
    res_sd = f_mount(&fs, "0:", 1);

    /***************************Format test**************************/
    printf("\n format test\n");
    if (res_sd == FR_NO_FILESYSTEM)
    {
        printf("The SD card has no file system and is about to be formatted\r\n");

        res_sd = f_mkfs("0:", 0, 0);

        if (res_sd == FR_OK)
        {
            printf("The SD card successfully mounted the file system\r\n");
            res_sd = f_mount(NULL, "0:", 1);
            res_sd = f_mount(&fs, "0:", 1);
        }
        else
        {
            printf("SD card formatting failed\r\n");
            while (1);
        }
    }
    else if (res_sd != FR_OK)
    {
        printf("SD card mount failed (%d), maybe SD card initialization failed\r\n", res_sd);
        while (1);
    }
    else
    {
        printf("The file system is mounted and can be read and written for testing\r\n");
    }

    SDInfoShow();
    /***************************File system testing --->>> Write test*********************/
    printf("\n file system test --->>> Write test\n");
    res_sd = f_open(&fnew, "0:FatFs read and write test files.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
    if (res_sd == FR_OK)
    {
        printf("Open/create FatFs to read and write the test file.txt successfully, and write data to the file\r\n");

        res_sd = f_write(&fnew, WriteBuffer, sizeof(WriteBuffer), &fnum);

        if (res_sd == FR_OK)
        {
            printf("The file was written successfully, the number of bytes written:% d The data written is: \n%s\r\n", fnum, WriteBuffer);
        }
        else
        {
            printf("File write failed (%d)\n", res_sd);
        }
        f_close(&fnew);
    }
    else
    {
        printf("Failed to open/create, file\r\n");
    }

    /*************************File system testing --->>> read test**************************/
    printf("\n file system test --->>> read test\n");
    res_sd = f_open(&fnew, "0:FatFs read and write test files.txt", FA_OPEN_ALWAYS | FA_READ);
    if (res_sd == FR_OK)
    {
        printf("File successfully opened\r\n");
        res_sd = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
        if (res_sd == FR_OK)
        {
            printf("File read successful. Bytes read:% d The data read was: \n%s\r\n", fnum, ReadBuffer);
        }
        else
        {
            printf("File read failed (%d)\n", res_sd);
        }
    }
    else
    {
        printf("File opening failed\n");
    }

    f_close(&fnew);

    f_mount(NULL, "0:", 1);
}

FatfsTest():挂载文件系统,格式化(若需要),测试文件读写。

void FatfsBigDataTest(void)
{
    uint32_t i;

    res_sd = f_mount(&fs, "0:", 1);

    /***************************File system testing --->>> Big data write test*********************/
    printf("\nFile system test --->>> write test\n");
    res_sd = f_open(&fnew, "0:FatFs read and write test files.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
    if (res_sd == FR_OK)
    {
        printf("Open/create FatFs to read and write the test file.txt successfully, and write data to the file\r\n");

        for (i = 0; i < 0xFFFFF; i++)
        {
            res_sd = f_write(&fnew, WriteBuffer, sizeof(WriteBuffer), &fnum);
            if ((i % 0x8FFF) == 0)
            {
                printf("......\n");
            }
        }
        if (res_sd == FR_OK)
        {
            printf("File written successfully\n");
        }
        else
        {
            printf("File write failed (%d)\n", res_sd);
        }
        f_close(&fnew);
    }
    else
    {
        printf("Failed to open/create, file\r\n");
    }
}

FatfsBigDataTest():写入大量数据测试性能。

void CreateDir(void)
{
    res_sd = f_mount(&fs, "0:", 1);
    if (res_sd != FR_OK)
    {
        printf("Failed to mount file system (%d)\r\n", res_sd);
    }

    res_sd = f_mkdir("/Dir1");
    if (res_sd == FR_OK)
    {
        printf("f_mkdir Dir1 OK\r\n");
    }
    else if (res_sd == FR_EXIST)
    {
        printf("Dir1 Target already exists(%d)\r\n", res_sd);
    }
    else
    {
        printf("f_mkdir Dir1 fail(%d)\r\n", res_sd);
        return;
    }

    res_sd = f_mkdir("/Dir2");
    if (res_sd == FR_OK)
    {
        printf("f_mkdir Dir2 OK\r\n");
    }
    else if (res_sd == FR_EXIST)
    {
        printf("Dir2 Target already exists(%d)\r\n", res_sd);
    }
    else
    {
        printf("f_mkdir Dir2 fail (%d)\r\n", res_sd);
        return;
    }

    res_sd = f_mkdir("/Dir1/Dir1_1");
    if (res_sd == FR_OK)
    {
        printf("f_mkdir Dir1_1 OK\r\n");
    }
    else if (res_sd == FR_EXIST)
    {
        printf("Dir1_1 Target already exists(%d)\r\n", res_sd);
    }
    else
    {
        printf("f_mkdir Dir1_1 fail (%d)\r\n", res_sd);
        return;
    }

    f_mount(NULL, "0:", 1);
}

CreateDir() 和 DeleteDirFile():目录创建与删除。

void ViewRootDir(void)
{
    DIR      dirinf;
    FILINFO  fileinf;
    uint32_t cnt = 0;
    char     name[256];

    res_sd = f_mount(&fs, "0:", 1);
    if (res_sd != FR_OK)
    {
        printf("Failed to mount file system (%d)\r\n", res_sd);
    }

    res_sd = f_opendir(&dirinf, "/");
    if (res_sd != FR_OK)
    {
        printf("Failed to open root directory (%d)\r\n", res_sd);
        return;
    }

    fileinf.lfname = name;
    fileinf.lfsize = 256;

    printf("attribute		|	file size	|	short filename	|	long file name\r\n");
    for (cnt = 0;; cnt++)
    {
        res_sd = f_readdir(&dirinf, &fileinf);
        if (res_sd != FR_OK || fileinf.fname[0] == 0)
        {
            break;
        }

        if (fileinf.fname[0] == '.')
        {
            continue;
        }

        if (fileinf.fattrib & AM_DIR)
        {
            printf("(0x%02d)directory", fileinf.fattrib);
        }
        else
        {
            printf("(0x%02d)attribute", fileinf.fattrib);
        }

        printf("%10d	", fileinf.fsize);
        printf("	%s |", fileinf.fname);
        printf("	%s\r\n", (char *)fileinf.lfname);
    }

    f_mount(NULL, "0:", 1);
}

ViewRootDir():显示根目录内容。

关键代码分析:

1.文件系统初始化与挂载

res_sd = f_mount(&fs, "0:", 1);

  • 挂载SD卡(逻辑驱动号0:),失败时尝试格式化(f_mkfs)。

2.文件读写操作

  • 创建/打开文件:f_open使用FA_OPEN_ALWAYS模式,若文件不存在则创建。
  • 写入数据:f_write将缓冲区数据写入文件。
  • 读取数据:f_read从文件读取数据到缓冲区。

3.目录操作

  • 创建目录:f_mkdir创建目录,处理已存在情况(FR_EXIST)。
  • 删除目录/文件:f_unlink删除文件或空目录(非空目录需递归删除)。

6 下载验证

6.1 SDIO_SDCardFatfs

命令1:基础文件测试(FatfsTest):

命令2:大数据读写测试(FatfsBigDataTest):

命令3:查看根目录(ViewRootDir):

命令4:创建目录(CreateDir):

命令5:删除目录与文件(DeleteDirFile):

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2407777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…