摘要:本文介绍SD和TF卡模块的使用方法
前面介绍了非易失性存储的使用方法,由于空间和本身只支持键值对的限制,非易失性存储只适用于少量数据的记录。而不适用于各种声音、图片、大量数据等情况的使用。这时候就需要有文件系统或者更大容量存储空间的支持。SD卡(Secure Digital Memory Card)和TF卡(Trans-flash Card,也叫Micro SD卡)就是扩展存储空间的不错的选择,因为这两种卡都支持SPI模式,也就是可以通过SPI通信协议直接访问,因此这两种卡成了很多移动设备扩展存储空间的不二选择。
SD卡通常有两种工作模式:SDIO模式和SPI模式。其实这也是两种通信协议,也就是说SD卡支持SDIO和SPI两种通信方式。SPI通信协议在前面已经介绍过了。SDIO是安全数字输入输出接口(Secure Digital Input and Output)的缩写,是从SD内存卡接口的基础上演化出来的一种外设接口。SDIO接口兼容以前的SD内存卡,并且可以连接支持SDIO接口的设备。SDIO接口有4根数据线,数据传输速度非常快。有兴趣的可以进一步的了解SDIO的详细信息。
下面来看一下SD卡和TF卡的接口定义:
通过上面的接口可知,应该有两种方法用来读取SD卡上的数据,一种是通过SDIO接口,一种是SPI接口,为了简单方便,现在大部分访问SD卡都使用SPI的方式,市面上能买到的SD开模块,也都是SPI接口的。
虽然可以直接把SD卡与ESP32单片机相连接来进行数据的读写操作,但接线不是那么方便,因此还是使用的功能模块来进行实验,通常的功能模块都是只支持SPI模式的,将SD卡的SPI数据线引出到了接线排针上。因为现在TF卡更普遍一些,因此这次使用的是TF卡模块,其使用方法与SD卡功能模块没有任何区别。模块的样子如下图所示:
熟悉SPI协议的应该对上图中的引脚非常的属性了,应该不需要再进一步的解释了,不了解的可以看之前关于SPI通信协议的文章。下面就把TF卡模块连接到我们的ESP32单片机引脚上。
ESP32芯片共有3个SPI接口,其中一个被用作片上SPI Flash使用,另外两个可以给开发人员使用,其引脚定义如下:
Pin Name | HSPI(GPIO Number) | VSPI(GPIO Number) |
CSO* | 15 | 5 |
SCLK | 14 | 18 |
MISO | 12 | 19 |
MOSI | 13 | 23 |
QUADWP | 2 | 22 |
QUADHD | 4 | 21 |
其中的QUADWP为写保护信号,QUADHD为保持信号。这两个引脚只用于4位传输。
在设计电路的时候,可以尽量使用默认的引脚,这样在开发中就不需要再进行额外的配置了。当然,ESP32在强大的GPIO交换矩阵的支持下,基本上可以用任意的引脚来连接到SPI控制器,实现使用任意引脚与外部SPI设备的连接。我所使用的连接方法如下表所示:
TF卡模块 | ESP32扩展板 |
GND | GND |
VCC | +5V |
MISO | P19 |
MOSI | P23 |
SCK | P18 |
CS | P5 |
这是使用VSPI默认引脚的连接方法。SPI通信协议中有明显的主从设备定义,因此收发引脚也明确了主从,这样连接起来就非常的方便,不容易接错。回想前面UART通信协议中,收发引脚都是站在自身的角度来说的,因此在与其他设备通信的时候,就应该自己的接收与对方的发送相连接,对方的接收与自己的发送相连接。这点不同之处要关注一下,很容易搞错了。
接下来就来看一下在Arduino IDE中,如何读写SD卡中的内容,这其中会包含非默认引脚的处理方法。下面来看一下主要的操作过程。
1. 引入头文件
#include "SD.h"
#include "SPI.h"
2. 配置SPI通信引脚
如果使用HSPI的默认引脚,那么可以忽略本步骤,直接进入到下一步。否则,就需要使用这里列举的几种方法来配置SPI的通信引脚。通常有下面两种方法:
(1)调用SPI实例的begin()方法来指定SPI通信的引脚,该方法的定义如下:
void begin(int8_t sck = -1, int8_t miso = -1, int8_t mosi = -1, int8_t ss = -1)
这个方法有4个参数,依次是:sck、miso、mosi和ss。
(2)创建一个新的SPIClass的实例
SPIClass spi = SPIClass(VSPI);
创建的时候可以指定使用VSPI控制器,也可以使用默认的HSPI控制器。如果这个时候恰好使用的就是VSPI的默认引脚,那么就不用再进行任何操作了,如果是自己随意选择的几个引脚,那么同样可以调用begin()方法来指定每一个引脚。具体方法同前一种一样,默认的SPI实例就是SPIClass类型的。
3. 初始化SD实例
SD是SDFS类的实例。其初始化方法begin()的定义如下:
bool begin(uint8_t ssPin = SS, SPIClass &spi = SPI,
uint32_t frequency = 4000000, const char *mountpoint = "/sd",
uint8_t max_files = 5, bool format_if_empty = false)
这个begin()方法的参数比较多,重点关注前两个,第一个是片选引脚。第二个就是前面创建的SPIClass类的配置信息。默认值是SPI,如果自己生成了新的SPIClass对象的实例,那么把它作为第二个参数传递给begin()方法。
这个begin()方法返回值位布尔类型的,true表示成功,false表示初始化失败。这时,应检查接线是否牢固、正确。前边的初始化配置与接线是否相符合。
4. 得到卡的类型
调用SD.cardType()方法可以得到卡的类型。该方法返回一个枚举类型的变量,该枚举类型可以有以下几个数值:
CARD_NONE 无卡
CARD_MMC MMC卡
CARD_SD SD卡
CARD_SDHC SDHC卡
CARD_UNKNOWN 未知
下面再说一下SD卡类型的小知识。目前SD卡分为三种,分别是SD、SDHC和SDXC卡。这三者的区别是容量上限的不同,其次在传输速度上有的有些差别,但传输速度不是划分的标准。SD卡是由MMC卡发展而来的,最早的SD卡由于FAT16文件系统的限制,其最大容量只有2GB,所以在开发中,小于2GB容量的SD卡,就叫SD卡。容量在2GB至32GB之间的称为SDHC(Secure Digital High Capacity),也就是高容量的SD卡。它使用了FAT32文件系统,能够支持大于2G的文件。相比之下,SDXC(Secure Digital eXtended Capacity)卡的容量更大,可以达到2TB(2048GB)。它是为需要更高容量的设备设计的,并且支持更快的读写速度。SDXC使用了FAT32或者EXFAT文件系统。
设备读取SD卡是向下兼容的,支持高容量的设备是可以读取低容量SD卡的,只支持低容量SD卡的设备,则无法读写高容量SD卡中的内容。这里所说的容量的高低是以2GB和32GB为分界线的。
之后,利用SD实例就可以对SD卡中的目录和文件进行各种操作了。这将在下一篇中进行介绍。