单片机/MCU论坛
直播中

jf_85110202

1年用户 442经验值
擅长:嵌入式技术 开发板 仪表仪器 单片机
私信 关注
[文章]

《DNESP32S3使用指南-IDF版_V1.6》第三十七章 SPI_SDCARD实验

第三十七章 SPI_SDCARD实验
本很多单片机系统都需要大容量存储设备,以存储数据。目前常用的有U盘,FLASH芯片,SD卡等。他们各有优点,综合比较,最适合单片机系统的莫过于SD卡了,它不仅容量可以做到很大(32GB以上),支持SPI/SDIO驱动,而且有多种体积的尺寸可供选择(标准的SD卡尺寸及Micro SD卡尺寸等),能满足不同应用的要求。
只需要少数几个IO口即可外扩一个高达32GB或以上的外部存储器,容量从几十M到几十G选择范围很大,更换也很方便,编程也简单,是单片机大容量外部存储器的首选。
正点原子ESP32-S3开发板使接口是Micro SD卡接口,座带自锁功能SD SPI主机驱动程序基于SPI Master Driver实现。借助SPI主控驱动程序,SD卡及其他SPI设备可以共享同一SPI总线。SPI主机驱动程序将处理来自不同任务的独占访问。在本章中,我们将向大家介绍,如何在正点原子ESP32-S3开发板上实现Micro SD卡的读取。本章分为如下几个部分:
本章分为如下几个小节:
37.1 SD卡简介
37.2 硬件设计
37.3 程序设计
37.4 下载验证
37.1 SD卡简介
37.1.1 SD物理结构
SD规范SD卡协会明确,可以访问https://www.sdcard.org查阅更多标准SD卡主SDMini SDmicroSD(TF卡,2004正式更名为Micro SD Card为方便本文microSD表示)三种类型Mini SD已经被microSD取代,使用得不多,根据最新的SD规格列出的参数如表37.1.1.1
形状
SD
microSD
尺寸
第三十七章 SPI891.png
32 x 24 x 2.1 mm,
32 x 24 x 1.4 mm,
约重1.2g~2.5g
第三十七章 SPI959.png

11 x 15 x 1.0 mm, 约重0.5g
卡片种类(容量范围)
SD(2GB)SDHC(2GB~32GB)SDXC(32GB ~2TB)
SDUC(2TB ~128TB)
硬件规格
脚位数
High Speed and UHS-I : 9 pins
High Speed and UHS-I : 8 pins
UHS-II and UHS-III: 17 pins
UHS-II and UHS-III: 16 pins
SD Express 1-lane: 17-19 pins
SD Express 1-lane: 17 pins
SD Express 2-lane: 25-27 pins
电压范围
3.3V版本: 2.7V - 3.6V
1.8V电压版: 1.70V-1.95V
防写开关
37.1.1.1 SD卡的主要规格参数
述表格的“脚位”,对应于实卡上的“金手指”数,不同类型的卡的触点数不同,访问的速度SD允许了同的接口来访问的内存储单元。常见的是SDIO模式和SPI模式,根据这两接口模式,们也列出SD引脚对应于这两种不同的电路模式的引脚功能定义,如37.1.1.2
SD
SD Mode
SPI Mode
引脚编号
引脚名
引脚类型
功能描述
引脚名
引脚类型
功能描述
1
CD/DAT3
I/O/PP
识别/数据线位3
CS
I3
低电平有效
2
CMD
I/O/PP
命令/响应
DI
I
数据
3
VSS1
S
VSS
S
电源地
4
VDD
S
DC电源
VDD
S
DC电源正极
5
CLK
I
Clock
SCLK
I
时钟
6
VSS2
S
电源地
VSS2
S
电源地
7
DAT0
I/O/PP
数据线位0
DO
O/PP
数据输出
8
DAT1
I/O/PP
数据线位1
RSV
9
DAT2
I/O/PP
数据线位2
RSV
37.1.1.2 SD引脚编号(注:S:电源 I:输入 O挽输出 PP
们对比看一下microSD引脚见只比SD卡少了一个电源引脚VSS2其它引脚功能类似
microSD
SD Mode
SPI Mode
引脚编号
引脚名
引脚类型
功能描述
引脚名
引脚类型
功能描述
1
DAT2
I/O/PP
数据线位2
RSV
2
CD/DAT3
I/O/PP
识别/数据线位3
CS
I
选低电平有效
3
CMD
PP
命令/响应
DI
I
数据
4
VDD
S
DC电源正极
VDD
S
DC电源正极
5
CLK
I
时钟
SCLK
I
时钟
6
VSS
S
电源地
VSS
S
电源地
7
DAT0
I/O/PP
数据线位0
DO
O/PP
数据输出
8
DAT1
I/O/PP
数据线位1
RSV
37.1.1.3 microSD引脚编号(注:S:电源 I:输入 O挽输出 PP
SD卡和Micro SD有引脚和形状大小不同,内部结构类似,操作时序完全相同,可以使用完全相同的代码驱动,下面以9’Pin SD卡的内部结构为例,展示SD的存储结构
第三十七章 SPI2350.png
37.1.1.1 SD卡内部物理结构RCA 存器SPI模式下不访问
SD有自己的寄存器,但它不直接行读写操作,需要通过命令来控制,SDIO协议定义了些命令用于实现某一特定功能,SD卡根据收到的命令要求对内部寄存器进行修改。37.1.1.4描述SD的寄存器我们SD卡进行数据通讯的主要通道,如
名称
位宽
描述
CID
128
标识(Card identification):每个卡都是唯一的
RCA
16
相对地址(Relative card address):卡的本地系统地址,初始化时,动态地由卡建议,经主机核准。
DSR
16
驱动级寄存器(Driver Stage Register):配置卡的输出驱动
CSD
128
卡的特定数据(Card Specific Data):卡的操作条件信息
SCR
64
SD配置寄存器(SD Configuration Register):SD卡特殊特性信息
OCR
32
操作条件寄存器(Operation conditions register):卡电源和状态标识
SSR
512
SD状态(SD Status):SD卡专有特征的信息
CSR
32
卡状态(Card Status):卡状态信息
37.1.1.4 SD信息
SD多信息和硬件设计规范可以参考SD协议Physical Layer Simplified Specification Version 2.00的相关章节
37.1.2命令和响应
一个整的SD操作过程是:主(单片机等)发起命令SD卡根据命令内容决定否发送响应信息及数据等,如果数据/操作,主还需要发送停止/数据的命令结束次操作,这意味着主机发起命令指令后,SD卡可以没响应、数据等过程,这取决于命令的含义这一过程37.1.2.1示。
第三十七章 SPI3169.png
37.1.2.1 SD卡命令格式
SD有多种命令响应,它的格式义及含义在《SD协议V2.0第三和第四详细介绍,发送命令主机只能通过CMD引脚送给SD,串行逐位发送时发送最高(MSB),然后次高位样类推……接下来,我们看看SD卡的命令格式,如表 37.1.2.1所示:
字节
字节1
字节2--5
字节6
47
46
45:40
39:8
7:1
0
描述
0
1
command
命令参数
CRC7
1
37.1.2.1 SD卡控制命令格式
SD卡的命令固定为48,由6个字节组成,字节1的最高2位固定为01,低6位为命令号(比如CMD16,为10000B16进制的0X10,完整的CMD16,第一个字节为01010000,即0X10+0X40)。字节2~5为命令参数,有些命令是没有参数的。字节6的高七位为CRC值,最低位恒定为1
SD卡的命令总共有12类,分为Class0~Class11,本章,我们仅介绍几个比较重要的命令,如表37.1.2.2所示:
命令
参数
响应
描述
CMD0(0X00)
NONE
R1
复位SD
CMD8(0X08)
VHS+Checkpattern
R7
发送接口状态命令
CMD9(0X09)
NONE
R1
读取卡特定数据寄存器
CMD10(0X0A)
NONE
R1
读取卡标志数据寄存器
CMD16(0X10)
块大小
R1
设置块大小(字节数)
CMD17(0X11)
地址
R1
读取一个块的数据
CMD24(0X18)
地址
R1
写入一个块的数据
CMD41(0X29)
NONE
R3
发送给主机容量支持信息和激活卡初始化过程
CMD55(0X37)
NONE
R1
告诉SD卡,下一个是特定应用命令
CMD58(0X3A)
NONE
R3
读取OCR寄存器
37.1.2.2 SD卡部分命令
上表中,大部分的命令是初始化的时候用的。表中的R1R3R7等是SD卡的应答个响应也有规定好的格式,如37.1.2.2所示:
第三十七章 SPI4053.png
37.1.2.2 SD卡命令传输过程
规定为有响应的命令下,每发送一个命令,SD卡都会给出一个应答,以告知主机该命令的执行情况,或者返回主机需要获取的数据,应答可以是R1~R7R1的应答,各位描述如表37.1.2.3所示:
R1响应
描述
起始位
传输位
命令号
卡状态
CRC7
终止位
Bit
47
46
[45:40]
[39:8]
[7:1]
0
位宽
1
1
6
32
7
1
"0"
"0"
x
x
x
"1"
37.1.2.3 R1响应
R2~R7的响应,限于篇幅,我们就不介绍了但需要注意的是除了R2响应128外,其它的响应都是48,请大家参考SD2.0协议。
37.1.3 卡模式
SD卡系统(包括主机和SD)定义了SD卡的工作模式,在每个操作模式下,SD卡都有几种状态,参考表37.1.3.1状态通过命令控制实现卡状态的切换。
无效模式
(Inactive)
无效状态(Inactive State)
卡识别模
(Card identification mode)
空闲状态(Idle State)
准备状态(Ready State)
识别状态(Identification State)
数据传输模式
(Data transfer mode)
待机状态(Stand-by State)
传输状态(Transfer State)
发送数据状态(Sending-data State)
接收数据状态(Receive-data State)
编程状态(Programming State)
断开连接状态(Disconnect State)
37.1.3.1 SD卡状态与操作模式
对于我们来说两种效操作模式:卡识别模式和数据传输模式。在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡识别状态接收到CMD3 (SEND_RCA)命令后,SD卡就进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。
在卡识别模式下,主机会复位所有处于卡识别模式SD卡,确认其工作电压范围,识别SD卡类型,并且获取SD卡的相对地址(卡相对地址较短,便于寻址)。在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下。卡识别模式下SD卡状态转换37.1.3.1
主机上电后,所有卡处于空闲状态,包括当前处于无效状态的卡。主机也可以发送GO_IDLE_STATE(CMD0)让所有卡软复位从而进入空闲状态,但当前处于无效状态的卡并不会复位。
主机在开始与卡通信前,需要先确定双方在互相支持的电压范围内。SD卡有一个电压支持范围,主机当前电压必须在该范围可能才能与卡正常通信。SEND_IF_COND(CMD8)命令就是用于验证卡接口操作条件的(主要是电压支持)。卡会根据命令的参数来检测操作条件匹配性,如果卡支持主机电压就产生响应,否则不响应。而主机则根据响应内容确定卡的电压匹配性。CMD8SD卡标准V2.0版本才有的新命令,所以如果主机有接收到响应,可以判断卡为V2.0或更高版本SD卡。
SD_SEND_OP_COND(ACMD41)命令可以识别或拒绝不匹配它的电压范围的卡。ACMD41命令的VDD电压参数用于设置主机支持电压范围,卡响应会返回卡支持的电压范围。对于对CMD8有响应的卡,把ACMD41命令的HCS位设置为1,可以测试卡的容量类型,如果卡响应的CCS位为1说明为高容量SD卡,否则为标准卡。卡在响应ACMD41之后进入准备状态,不响应ACMD41的卡为不可用卡,进入无效状态。ACMD41是应用特定命令,发送该命令之前必须先发CMD55
第三十七章 SPI5758.png
第三十七章 SPI3169.png
37.1.3.1 卡识别模式状态转换图
ALL_SEND_CID(CMD2)用来控制所有卡返回它们的卡识别号(CID),处于准备状态的卡在发送CID之后就进入识别状态。之后主机就发送SEND_RELATIVE_ADDR(CMD3)命令,让卡自己推荐一个相对地址(RCA)并响应命令。这个RCA16bit地址,而CID128bit地址,使用RCA简化通信。卡在接收到CMD3并发出响应后就进入数据传输模式,并处于待机状态,主机在获取所有卡RCA之后也进入数据传输模式。
37.1.4 数据模式
数据模式下们可以对SD卡的存储块进行读写访问操作SD电后默认一位数据总线访问,可以通过指令设置为宽总线模式,可以时使有4总线并行读数据,这样对于支持宽总线模式的接口(如:SDIOQSPI等)都能数据操作速度。
第三十七章 SPI6126.png
37.1.4.1  1数据线传输8bit数据流格式
SD卡有两种数据模式,种是常规的8位宽一次按一字传输,另一种是一次按512节传输,我们只介绍前面一种。当按8-bit连续传输时,每次传输最低字节开始,每字节从最高位(MSB)开始送,当使一条数据线时,只能通过DAT0进行数据传输,它的数据传输结构如37.1.4.1示。
使4线模式8-bit结构的数据时,数据仍按MSB送的原则,DAT[3:0]发送高数据位,低位发送低数据位。硬件支持的情况下,使用4线传输可提升传输速率。
第三十七章 SPI6390.png
37.1.4.2  4数据线传输8bit格式数据流格式
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。数据传输模式下,SD卡状态转换过程见37.1.4.3
第三十七章 SPI6560.png 37.1.4.3数据传输模式卡状态转换
CMD7用来选定和取消指定的卡,卡在待机状态下还不能进行数据通信,因为总线上可能有多个卡都是出于待机状态,必须选择一个RCA地址目标卡使其进入传输状态才可以进行数据通信。同时通过CMD7命令也可以让已经被选择的目标卡返回到待机状态。
数据传输模式下的数据通信都是主机和目标卡之间通过寻址命令点对点进行的。卡处于传输状态下可以通过命令对卡进行数据读写、擦除。CMD12可以中断正在进行的数据通信,让卡返回到传输状态。CMD0CMD15会中止任何数据编程操作,返回卡识别模式,注意谨慎使用,当操作可能导致卡数据被损坏。       
我们已经介绍了SD操作的一些知识知道了SD卡操作的命令、响应和数据传输等状态,接下来我们来分析实际硬件接口如何SD卡发送我需要的数据       
37.1.5 SD卡初始化流程
1,模式下的SD卡初
一节,我们来看看SD卡的初始化流程,要实现SDIO驱动SD卡,最重要的步骤就是SD卡的初始化,只要SD卡初始化完成了,那么剩下的(读写操作)就简单了,所以我们这里重点介绍SD卡的初始化。从SD2.0协议(见光盘资料)文档,我们得到SD卡初始化流程图如图37.1.5.1所示:
第三十七章 SPI7093.png
37.1.5.1 SD卡初始化流程Card Initialization and Identification Flow (SD mode)
从图中,我们看到,不管什么卡(这里我们将卡分为4类:SD2.0高容量卡(SDHC,最大32G),SDv2.0标准容量卡(SDSC,最大2G),SD1.x卡和MMC卡),首先我们要执行的是卡上电(需要设置SDIO_POWER[1:0]=11),上电后发送CMD0,对卡进行软复位,之后发送CMD8命令,用于区分SD2.0,只有2.0及以后的卡才支持CMD8命令,MMC卡和V1.x的卡,是不支持该命令的。CMD8的格式如表37.1.5.1所示:
位序
47
46
[45:40]
[39:20]
[19:16]
[15:8]
[7:1]
0
占用
1
1
6
20
4
8
7
1
命令
‘0’
‘1’
‘001000’
‘00000h’
x
x
x
‘1’
描述
起始位
传输
命令索
保留
电源(VHS)
校验
CRC7
结束位
37.1.5.1 CMD8命令格式
这里,我们需要在发送CMD8的时候,通过其带的参数我们可以设置VHS位,以告诉SD卡,主机的供电情况,VHS位定义如表37.1.5.2所示:
第三十七章 SPI7635.png
37.1.5.2 VHS位定义
这里我们使用参数0X1AA,即告诉SD卡,主机供电为2.7~3.6V之间,如果SD卡支持CMD8,且支持该电压范围,则会通过CMD8的响应(R7)将参数部分原本返回给主机,如果不支持CMD8,或者不支持这个电压范围,则不响应。
在发送CMD8后,发送ACMD41(注意发送ACMD41之前要先发送CMD55),来进一步确认卡的操作电压范围,并通过HCS位来告诉SD卡,主机是不是支持高容量卡(SDHC)。ACMD41的命令格式如表37.1.5.3所示:
第三十七章 SPI7881.png
37.1.5.3 ACMD41命令格式
ACMD41得到的响应(R3)包含SDOCR寄存器内容,OCR寄存器内容定义如表37.1.5.4所示:
第三十七章 SPI7957.png
37.1.5.4 OCR寄存器定义
对于支持CMD8指令的卡,主机通过ACMD41的参数设置HCS位为1,来告诉SD卡主机支SDHC卡,如果设置为0,则表示主机不支持SDHC卡,SDHC卡如果接收到HCS0,则永远不会反回卡就绪状态。对于不支持CMD8的卡,HCS位设置为0即可。
SD卡在接收到ACMD41后,返回OCR寄存器内容,如果是2.0的卡,主机可以通过判断OCRCCS位来判断是SDHC还是SDSC;如果是1.x的卡,则忽略该位。OCR寄存器的最后一个位用于告诉主机SD卡是否上电完成,如果上电完成,该位将会被置1
对于MMC卡,则不支持ACMD41,不响应CMD55,对MMC卡,我们只需要在发送CMD0后,在发送CMD1(作用同ACMD41),检查MMC卡的OCR寄存器,实现MMC卡的初始化。
至此,我们便实现了对SD卡的类型区分,图37.1.5.1中,最后发送了CMD2CMD3命令,用于获得卡CID寄存器数据和卡相对地址(RCA)。
CMD2,用于获得CID寄存器的数据,CID寄存器数据各位定义如表37.1.5.5所示:
Name
Field
Width
CID-slice
Manufacturer ID
MID
8
[127:120]
OEM/Application ID
OID
16
[119:104]
Product name
PNM
40
[103:64]
Product revision
PRV
8
[63:56]
Product serial number
PSN
32
[55:24]
reserved
--
4
[23:20]
Manufacturing date
MDT
12
[19:8]
CRC7 checksum
CRC
7
[7:1]
not used, always 1
-
1
[0:0]
37.1.5.5 CID寄存器位定义
SD卡在收到CMD2后,将返回R2长响应(136位),其中包含128位有效数据(CID寄存器内容),存放在SDIO_RESP1~44个寄存器里面。通过读取这四个寄存器,就可以获得SD卡的CID信息。
CMD3,用于设置卡相对地址(RCA,必须为非0),对于SD卡(非MMC卡),在收到CMD3后,将返回一个新的RCA给主机,方便主机寻址。RCA的存在允许一个SDIO接口挂多个SD卡,通过RCA来区分主机要操作的是哪个卡。而对于MMC卡,则不是由SD卡自动返回RCA,而是主机主动设置MMC卡的RCA,即通过CMD3带参数(高16位用于RCA设置),实现RCA设置。同样MMC卡也支持一个SDIO接口挂多个MMC卡,不同于SD卡的是所有的RCA都是由主机主动设置的,而SD卡的RCA则是SD卡发给主机的。
在获得卡RCA之后,我们便可以发送CMD9(带RCA参数),获得SD卡的CSD寄存器内容,从CSD寄存器,我们可以得到SD卡的容量和扇区大小等十分重要的信息。CSD寄存器我们在这里就不详细介绍了,关于CSD寄存器的详细介绍,请大家参考《SD2.0协议.pdf》。
至此,我们的SD卡初始化基本就结束了,最后通过CMD7命令,选中我们要操作的SD卡,即可开始对SD卡的读写操作了,SD卡的其他命令和参数,我们这里就不再介绍了,请大家参考《SD2.0协议.pdf》,里面有非常详细的介绍。
2SPI模式下的SD卡初
ESP32SDIO驱动模式和SPI模式不兼容,二者使用时需要区分开来。《SD2.0协议.pdf中提供了SD卡的SPI初始化时序,们可以按它建议的流程进行SD卡的初始化,如图37.1.5.2所示
第三十七章 SPI9509.png
37.1.5.2 SDSPI初始化流程SPI Mode Initialization Flow
要使SPI模式驱动SD卡,先得让SD卡进入SPI模式。方法如下:在SD卡收到复位命令(CMD0)时,CS为有效电平(低电平)则SPI模式被启用。不过在发送CMD0之前,要发送>74个时钟,这是因为SD卡内部有个供电电压上升时间,大概为64CLK,剩下的10CLK用于SD卡同步,之后才能开始CMD0的操作,在卡初始化的时候,CLK时钟最大不能超过400Khz
接着我们看看SD卡的初始化,前面SD基本介绍,我们知道SD卡是先发送数据高位的,SD卡的典型初始化过程如下:
1、初始化与SD卡连接的硬件条件(MCUSPI配置,IO口配置);
2低片选信号,上电延时(>74CLK);
3、复位卡(CMD0),进入IDLE状态;
4、发送CMD8,检查是否支持2.0协议;
5、根据不同协议检查SD卡(命令包括:CMD55ACMD41CMD58CMD1等);
6、取消片选,发多8CLK,结束初始化
这样我们就完成了对SD卡的初始化,注意末尾发送的8CLK是提供SD卡额外的时钟,完成某些操作。通过SD卡初始化,我们可以知道SD卡的类型(V1V2V2HC或者MMC),在完成了初始化之后,就可以开始读写数据了。
SD扇区读取数据,这里通过CMD17来实现,具体过程如下:
1、发送CMD17
2、接收卡响应R1
3、接收数据起始令牌0XFE
4、接收数据;
5、接收2个字节的CRC,如果不使用CRC,这两个字节在读取后可以丢掉。
6、禁止片选之后,发多8CLK
以上就是一个典型的读取SD卡数据过程,SD卡的写于读数据差不多,写数据通过CMD24来实现,具体过程如下:
1、发送CMD24;
2、接收卡响应R1
3、发送写数据起始令牌0XFE
4、发送数据;
5、发送2字节的伪CRC
6、禁止片选之后,发多8CLK
以上就是一个典型的写SD卡过程。关于SD卡的介绍,我们就介绍到这里,更详细的SPI操作SD方法可以参考ESP32F1 Mini板》SD卡的章节,我们这里就不再展开了
332-S3SD/MMC概述
ESP32-S3存储卡接口控制器提供了一个访问安全数字输入输出卡、MMC卡以及CD-ATA设备的硬件接口,用于连接高级外设总线和外部存储设备。该控制器支持两个外部卡,分别为:卡0和卡1。所有SD/MMC模块接口信号都必须通过GPIO矩阵传输至GPIO padSD/MMC控制器支持两组外设工作,但不支持同时工作,其连接的拓扑结构如下所示:
第三十七章 SPI10630.png
37.1.5.3 ESP32-S3SD/MMC控制器连接的拓扑结构
        SD/MMC的外部接口信号主要为时钟信号(sdhost_cclk_out_1.eg:card1)、命令信号(sdhost_ccmd_out_1)、数据信号(sdhost_cdata_in_1[7:0]/sdhost_cdata_out_1[7:0])SD/MMC控制器可通过这些外部接口信号与外部设备通信。其它信号还包括卡中断信号、卡检测信号和写保护信号等。
第三十七章 SPI10850.png
37.1.5.4 P32-S3SD/MMC控制器外部接口信号
管脚
方向
描述
sdhost_cclk_out
输出
向从机发送信号
sdhost_ccmd
双向
双向命令/响应线
sdhost_cdata
双向
双向数据读/写线
sdhost_card_detect_n
输入
卡检测输入线
sdhost_card_write_prt
输入
卡写保护状态输入
37.1.5.6 ESP32-S3SD/MMC信号描述
37.2 硬件设计
37.2.1 例程功能
本章实验功能简介:经过一系列初始化之后,通过一个while循环以SD卡初始化为条件,以检测SD卡是否初始化成功,若初始化SD卡成功,则会通过串口或者VSCode终端输出SD卡的相关参数,并在LCD上显示SD卡的总容量以及剩余容量。此时LED闪烁,表示程序正在运行。
37.2.2 硬件资源
1. LED
LED -IO0
2. XL9555
IIC_SDA-IO41
IIC_SCL-IO42
3. SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在P5端口,使用跳线帽将IO_SETLCD_DC相连)
PWR- IO1_3XL9555
RST- IO1_2XL9555
4. SD
CS-IO2
SCK-IO12
MOSI-IO11
MISO-IO13
37.2.3 原理图
本章实验使用SPI接口与SD卡进行连接,开发板板载了一个Micro SD卡座用于连接SD卡,SD卡与ESP32-S3的连接原理图,如下图所示:
第三十七章 SPI11528.png
37.2.3.1 SD卡接口与ESP32-S3的连接电路图
37.3 程序设计
37.3.1 程序流程图
程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:
第三十七章 SPI11671.png

37.3.1.1 SD卡实验程序流程图
37.3.2 SD卡函数解析
ESP-IDF提供了一套API来配置SD卡。要使用此功能,需要导入必要的头文件:
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"
#include "spi.h"
接下来,作者将介绍一些常用的ESP32-S3中的SD函数,这些函数的描述及其作用如下:
1,挂载SD
该函数用给定的配置,挂载SD卡,该函数原型如下所示:
esp_err_t esp_vfs_fat_sdspi_mount(const char* base_path,
                                  const sdmmc_host_t* host_config_input,
                                  const sdspi_device_config_t* slot_config,
                                  const esp_vfs_fat_mount_config_t*mount_config,
                                  sdmmc_card_t** out_card);
该函数的形参描述如下表所示:
参数
描述
base_path     
应该注册分区的路径(例如/sdcard”
host_config_input  
指向描述SDMMC主机的结构的指针。此结构可以使用SDSPI_HOST_DEFAULT宏初始化。
slot_config   
指向具有插槽配置的结构的指针,对于SPI外设,将指针传递到使用sdspi_device_config_DEFAULT初始化的sdspi_device_config_t结构。
mount_config  
指向具有用于安装FATFS的额外参数的结构的指针
out_card
如果不是NULL,指向卡片信息结构的指针将通过此参数返回。
37.3.2.1 esp_vfs_fat_sdspi_mount()函数形参描述
该函数的返回值描述,如下表所示:
返回值
描述
ESP_OK
返回:0,配置成功
ESP_ERR_INVALID_STATE
如果已经调用了esp_vfs_fat_sdmmc_mount
ESP_ERR_NO_MEM
如果无法分配内存
ESP_FAIL
如果分区无法安装,则来自SDMMCSPI驱动程序、SDMMC协议或FATFS驱动程序的其他错误代码
37.3.2.2 函数esp_vfs_fat_sdspi_mount ()返回值描述
该函数使用sdmmc_host_t类型的结构体变量传入,该结构体的定义如下所示:
结构体
成员变量
可选参数
sdmmc_host_t
flags
定义主机属性的标志:SPI协议且可调用deinit函数,有如下值可以配置:
SDMMC_HOST_FLAG_1BIT
SDMMC_HOST_FLAG_4BIT
SDMMC_HOST_FLAG_8BIT
SDMMC_HOST_FLAG_SPI
SDMMC_HOST_FLAG_DDR
SDMMC_HOST_FLAG_DEINIT_ARG
slot
使用SPI2端口 ,有如下值可以配置:
SPI1_HOST
SPI2_HOST
max_freq_khz
主机支持的最大频率:20000
io_voltage
控制器使用的I/O电压
init
用于初始化驱动程序的主机函数
set_bus_width
设置总线宽度的主机功能 ,例程设置为NULL
get_bus_width
取总线宽度的主机函数,例程设置为NULL
set_bus_ddr_mode
设置DDR模式的主机功能,例程设置为NULL
set_card_clk
设置板卡时钟频率的主机函数
do_transaction
执行事务的主机函数
deinit_p
用于取消初始化驱动程序的主机函数
io_int_enable
启用SDIO中断线的主机功能
io_int_wait
等待SDIO中断线路激活的主机功能
command_timeout_ms
超时,默认为0
37.3.2.3 sdmmc_host_t结构体参数值描述
完成上述结构体参数配置之后,可以将结构传递给 esp_vfs_fat_sdspi_mount () 函数,用以实例化SD卡,挂载文件系统
2,取消挂载SD
该函数用于取消挂载SD卡,该函数原型如下所示:
esp_err_t esp_vfs_fat_sdcard_unmount(const char* base_path, sdmmc_card_t *card);
该函数的形参描述如下表所示:
参数
描述
base_path     
应该注册分区的路径(例如/sdcard”
card
SD / MMC卡结构
37.3.2.4 esp_vfs_fat_sdcard_unmount ()函数形参描述
该函数的返回值描述,如下表所示:
返回值
描述
ESP_OK
返回:0,配置成功
ESP_ERR_INVALID_ARG
如果card参数未注册
ESP_ERR_INVALID_STATE
如果尚未调用esp_vfs_fat_sdmmc_mount
37.3.2.5 函数esp_vfs_fat_sdcard_unmount ()返回值描述
37.3.3 SD卡驱动解析
IDF版的26_sd例程中,作者在26_sd \components\BSP路径下新增了一个SDIO文件夹,用于存放spi_sdcard.cspi_sdcard.h这两个文件。其中,spi_sdcard.h文件负责声明SDIO相关的函数和变量,而spi_sdcard.c文件则实现了SDIO的驱动代码。下面,我们将详细解析这两个文件的实现内容。
1spi_sdcard.h文件
/* 引脚定义 */
#define SD_NUM_CS       GPIO_NUM_2
#define MOUNT_POINT     "/0:"   
1, spi_sdcard.c文件
sd_pi_init的设就比较简单了,我们只需要填充SPI结构体的控制句柄然后添加SPI总线设备并配置文件系统挂载即根据外设的情况,我们可以设置SPI的使用端口为SPI2端口
spi_device_handle_t MY_SD_Handle;
/**
* @Brief       SD卡初始化
* @param      
* @retval      esp_err_t
*/
esp_err_t sd_spi_init(void)
{
    esp_err_t ret = ESP_OK;
   
    /* SD / MMC卡结构 */
    sdmmc_card_t *card;
   
    /* 挂载点/根目录 */
    const char mount_point[] = MOUNT_POINT;
    /* SPI驱动接口配置,SPISD卡时钟是20-25MHz */
    spi_device_interface_config_t devcfg = {
        
        /* SPI时钟 */
        .clock_speed_hz = 20 * 1000 * 1000,
        
        /* SPI模式0 */
        .mode = 0,
        
        /* 片选引脚 */
        .spics_io_num = SD_NUM_CS,
        
        /* 事务队列尺寸 7*/
        .queue_size = 7,
    };
    /* 添加SPI总线设备 */
    ret = spi_bus_add_device(SPI2_HOST, &devcfg, &MY_SD_Handle);
    /* 文件系统挂载配置 */
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        
        /* 如果挂载失败:true会重新分区和格式化/false不会重新分区和格式化 */
        .format_if_mount_failed = false,
        
        /* 打开文件最大数量 */
        .max_files = 5,
        
        /* 硬盘分区簇的大小 */
        .allocation_unit_size = 16 * 1024,
    };
    /* SD卡参数配置 */
    sdmmc_host_t host = {0};
   
    /* 定义主机属性的标志:SPI协议且可调用deinit函数 */
    host.flags = SDMMC_HOST_FLAG_SPI | SDMMC_HOST_FLAG_DEINIT_ARG;
   
    /* 使用SPI2端口 */
    host.slot = SPI2_HOST;
   
    /* 主机支持的最大频率:20000 */
    host.max_freq_khz = SDMMC_FREQ_DEFAULT;
   
    /* 控制器使用的I/O电压 */
    host.io_voltage = 3.3f;
   
    /* 用于初始化驱动程序的主机函数 */
    host.init = &sdspi_host_init;
   
    /* 设置总线宽度的主机功能 */
    host.set_bus_width = NULL;
   
    /* 取总线宽度的主机函数 */
    host.get_bus_width = NULL;
   
    /* 设置DDR模式的主机功能 */
    host.set_bus_ddr_mode = NULL;
   
    /* 设置板卡时钟频率的主机函数 */
    host.set_card_clk = &sdspi_host_set_card_clk;
   
    /* 执行事务的主机函数 */
    host.do_transaction = &sdspi_host_do_transaction;
   
    /* 用于取消初始化驱动程序的主机函数 */
    host.deinit_p = &sdspi_host_remove_device;
   
    /* 启用SDIO中断线的主机功能 */
    host.io_int_enable = &sdspi_host_io_int_enable;
   
    /* 等待SDIO中断线路激活的主机功能 */
    host.io_int_wait = &sdspi_host_io_int_wait;
   
    /* 超时,默认为0*/
    host.command_timeout_ms = 0;
    /* SD卡引脚配置 */
    sdspi_device_config_t slot_config = {0};
    slot_config.host_id   = host.slot;
    slot_config.gpio_cs   = SD_NUM_CS;
    slot_config.gpio_cd   = GPIO_NUM_NC;
    slot_config.gpio_wp   = GPIO_NUM_NC;
    slot_config.gpio_int  = GPIO_NUM_NC;
    /* 挂载文件系统 */
ret = esp_vfs_fat_sdspi_mount(mount_point,
                                                                  &host,
                                                                  &slot_config,
                                                                  &mount_config,
                                                                  &card);
    if (ret != ESP_OK)
    {
        spi_bus_remove_device(MY_SD_Handle);    /* 移除SPI上的SD卡设备 */
        return ret;
    }
    return ret;
}
接下来这个函数用于获取SD相关信息,比如容量大小,以及剩余容量,如下所示:
/**
* @brief       获取SD卡相关信息
* @param       out_total_bytes:总大小
* @param       out_free_bytes:剩余大小
* @retval      
*/
void sd_get_fatfs_usage(size_t *out_total_bytes, size_t *out_free_bytes)
{
    FATFS *fs;
    size_t free_clusters;
    int res = f_getfree("0:", (size_t *)&free_clusters, &fs);
    assert(res == FR_OK);
    size_t total_sectors = (fs->n_fatent - 2) * fs->csize;
    size_t free_sectors = free_clusters * fs->csize;
    size_t sd_total = total_sectors / 1024;
    size_t sd_total_KB = sd_total * fs->ssize;
    size_t sd_free = free_sectors / 1024;
    size_t sd_free_KB = sd_free*fs->ssize;
    /* 假设总大小小于4GiB,对于SPI Flash应该为true */
    if (out_total_bytes != NULL)
    {
        *out_total_bytes = sd_total_KB;
    }
   
    if (out_free_bytes != NULL)
    {
        *out_free_bytes = sd_free_KB;
    }
}
37.3.4 CMakeLists.txt文件
打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
set(src_dirs
            IIC
            LCD
            LED
            SDIO
            SPI
            XL9555)
set(include_dirs
            IIC
            LCD
            LED
            SDIO
            SPI
            XL9555)
set(requires
            driver
            fatfs)
idf_component_register(SRC_DIRS ${src_dirs}
INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
上述的红色SDIO驱动以及fatfs依赖库需要由开发者自行添加,以确保SDIO驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了SDIO驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。
37.3.5 实验应用代码
打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
i2c_obj_t i2c0_master;
/**
* @brief       程序入口
* @param      
* @retval      
*/
void app_main(void)
{
    esp_err_t ret;
    size_t bytes_total, bytes_free;                          /* SD卡的总空间与剩余空间 */
    ret = nvs_flash_init();                             /* 初始化NVS */
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    led_init();                                                 /* 初始化LED */
    i2c0_master = iic_init(I2C_NUM_0);                               /* 初始化IIC0 */
    spi2_init();                                               /* 初始化SPI */
    xl9555_init(i2c0_master);                                  /* 初始化IO扩展芯片 */  
    lcd_init();                                                 /* 初始化LCD */
    lcd_show_string(30, 50, 200, 16, 16, "ESP32-S3", RED);
    lcd_show_string(30, 70, 200, 16, 16, "FATFS TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    while (sd_spi_init())                                       /* 检测不到SD*/
    {
        lcd_show_string(30, 110, 200, 16, 16, "SD Card Error!", RED);
        vTaskDelay(500);
        lcd_show_string(30, 130, 200, 16, 16, "Please Check! ", RED);
        vTaskDelay(500);
    }
    lcd_show_string(30, 110, 200, 16, 16, "SD Card OK!", RED);
    lcd_show_string(30, 130, 200, 16, 16, "Total:      MB", RED);
    lcd_show_string(30, 150, 200, 16, 16, "Free :      MB", RED);
    sd_get_fatfs_usage(&bytes_total, &bytes_free);
    lcd_show_num(80, 130,(int)bytes_total / 1024,5,16,BLUE);
    lcd_show_num(80, 150,(int)bytes_free / 1024,5,16,BLUE);
    while (1)
    {
        LED_TOGGLE();
        vTaskDelay(500);
    }
}
可以看到,本实验的应用代码中,通过初始化SD卡判断与SD卡的连接是否有误,SD卡初始化成功后便通过函数sd_get_fatfs_usage()获取容量等信息,同时也在LCD上显示了SD的容量信息。
37.4 下载验证
程序下载到开发板后,可以看到SPILCD显示SD卡目录下的test.txt文件的内容,如下图所示。37.4.1
第三十七章 SPI20715.png
37.4.1 程序运行效果图

更多回帖

发帖
×
20
完善资料,
赚取积分