开发板:NodeMCU32s(v1.3)
SD卡读写模块:SDIO/SPI 二合一
SD卡:闪迪 32GB(TF卡,实际29.7GB)/ Kingston 32G (TF卡,实际29.1GB)
开发框架:Arduino
Esp32 可以通过 SDMMC 和 SPI 两种方式读取SD/TF卡数据。
- SDMMC 分为 1 位 SD 总线模式和 4 位 SD 总线模式
- Esp32 上有 4 组 SPI 外设,其中,控制器 SPI2 和 SPI3 分别使用带前缀 HSPI 和 VSPI 的信号总线。
注意:
setup
函数后恢复供电(因此需要在 setup
函数中加入适当延时),否则 Esp32 会无限重启。 【4 位 SD 总线模式、HSPI 会发生】下载程序到 Esp32 时未断开 SD 卡供电,出现报错信息:
Timed out waiting for packet header
硬件连线方式:
-----------------------------------------------
| NodeMCU32s | SD Card |
| --------------------------------------------
| GPIO12 | D02 |
| GPIO4 | D01 |
| GPIO15 | CMD(MOSI) |
| GPIO2 | D00(MISO) |
| GPIO14 | CLK(SCK) |
| GPIO13 | D03(SS) |
| GND | GND |
| VCC(3.3v) | VCC |
-----------------------------------------------
#include "FS.h"
#include "SD.h"
#include "SD_MMC.h"
void WriteFile(fs::FS &fs, const char *path, uint8_t *buf, int len)
{
//unsigned long start_time = millis();
Serial.printf("write [%s]...\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (!file.write(buf, len)) {
Serial.println("Write failed");
return;
}
file.flush();
file.close();
Serial.printf("Write [%s] Complete", path);
}
void ReadFile(fs::FS &fs, const char *path, uint8_t *buf, int len)
{
Serial.printf("read [%s]...\n", path);
File file = fs.open(path);
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
if (!file.read(buf, len)) {
Serial.println("Read failed");
return;
}
file.close();
Serial.printf("Read [%s] Complete: %s", path, buf);
}
void testIO(fs::FS &fs)
{
char buf[] = "hello world";
WriteFile(fs, "/test.txt", (uint8_t *)buf, strlen(buf));
ReadFile(fs, "/test.txt", (uint8_t *)buf, strlen(buf));
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("请插入内存卡");
delay(8000);
/* SD_MMC 4-bit Mode */
if (!SD_MMC.begin()) {
Serial.println("Card Mount Failed");
return;
}
testIO(SD_MMC);
SD_MMC.end(); // 取消SD挂载
void loop() {
}
硬件连线方式:
-----------------------------------------------
| NodeMCU32s | SD Card |
| --------------------------------------------
| - | D02 |
| - | D01 |
| GPIO15 | CMD(MOSI) |
| GPIO2 | D00(MISO) |
| GPIO14 | CLK(SCK) |
| - | D03(SS) |
| GND | GND |
| VCC(3.3v) | VCC |
-----------------------------------------------
#include "FS.h"
#include "SD.h"
#include "SD_MMC.h"
void WriteFile(fs::FS &fs, const char *path, uint8_t *buf, int len)
{
//unsigned long start_time = millis();
Serial.printf("write [%s]...\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (!file.write(buf, len)) {
Serial.println("Write failed");
return;
}
file.flush();
file.close();
Serial.printf("Write [%s] Complete", path);
}
void ReadFile(fs::FS &fs, const char *path, uint8_t *buf, int len)
{
Serial.printf("read [%s]...\n", path);
File file = fs.open(path);
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
if (!file.read(buf, len)) {
Serial.println("Read failed");
return;
}
file.close();
Serial.printf("Read [%s] Complete: %s", path, buf);
}
void testIO(fs::FS &fs)
{
char buf[] = "hello world";
WriteFile(fs, "/test.txt", (uint8_t *)buf, strlen(buf));
ReadFile(fs, "/test.txt", (uint8_t *)buf, strlen(buf));
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("请插入内存卡");
delay(8000);
/* SD_MMC 1-bit Mode */
if (!SD_MMC.begin("/cdcard", true)) {
Serial.println("Card Mount Failed");
return;
}
testIO(SD_MMC);
SD_MMC.end(); // 取消SD挂载
void loop() {
}
硬件连线方式:
-----------------------------------------------
| NodeMCU32s | SD Card |
| --------------------------------------------
| - | D02 |
| - | D01 |
| GPIO13 | CMD(MOSI) |
| GPIO12 | D00(MISO) |
| GPIO14 | CLK(SCK) |
| GPIO15 | D03(SS) |
| GND | GND |
| VCC(3.3v) | VCC |
-----------------------------------------------
#include "FS.h"
#include "SD.h"
#include "SD_MMC.h"
void WriteFile(fs::FS &fs, const char *path, uint8_t *buf, int len)
{
//unsigned long start_time = millis();
Serial.printf("write [%s]...\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (!file.write(buf, len)) {
Serial.println("Write failed");
return;
}
file.flush();
file.close();
Serial.printf("Write [%s] Complete", path);
}
void ReadFile(fs::FS &fs, const char *path, uint8_t *buf, int len)
{
Serial.printf("read [%s]...\n", path);
File file = fs.open(path);
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
if (!file.read(buf, len)) {
Serial.println("Read failed");
return;
}
file.close();
Serial.printf("Read [%s] Complete: %s", path, buf);
}
void testIO(fs::FS &fs)
{
char buf[] = "hello world";
WriteFile(fs, "/test.txt", (uint8_t *)buf, strlen(buf));
ReadFile(fs, "/test.txt", (uint8_t *)buf, strlen(buf));
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("请插入内存卡");
delay(8000);
/* SD SPI Mode at HSPI */
SPIClass spi = SPIClass(HSPI);
spi.begin(14 /* SCK */, 12 /* MISO */, 13 /* MOSI */, 15 /* SS */);
if (!SD.begin(15 /* SS */, spi, 80000000)) {
Serial.println("Card Mount Failed");
return;
}
testIO(SD);
SD_MMC.end(); // 取消SD挂载
void loop() {
}
采用 32GB 闪迪 TF卡出现以下问题:
换成 32GB Kingston TF 卡正常。如果还不正常可以尝试换容量小一点的 TF 卡。
硬件连线方式:
-----------------------------------------------
| NodeMCU32s | SD Card |
| --------------------------------------------
| - | D02 |
| - | D01 |
| GPIO23 | CMD(MOSI) |
| GPIO19 | D00(MISO) |
| GPIO18 | CLK(SCK) |
| GPIO5 | D03(SS) |
| GND | GND |
| VCC(3.3v) | VCC |
-----------------------------------------------
#include "FS.h"
#include "SD.h"
#include "SD_MMC.h"
void WriteFile(fs::FS &fs, const char *path, uint8_t *buf, int len)
{
//unsigned long start_time = millis();
Serial.printf("write [%s]...\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (!file.write(buf, len)) {
Serial.println("Write failed");
return;
}
file.flush();
file.close();
Serial.printf("Write [%s] Complete", path);
}
void ReadFile(fs::FS &fs, const char *path, uint8_t *buf, int len)
{
Serial.printf("read [%s]...\n", path);
File file = fs.open(path);
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
if (!file.read(buf, len)) {
Serial.println("Read failed");
return;
}
file.close();
Serial.printf("Read [%s] Complete: %s", path, buf);
}
void testIO(fs::FS &fs)
{
char buf[] = "hello world";
WriteFile(fs, "/test.txt", (uint8_t *)buf, strlen(buf));
ReadFile(fs, "/test.txt", (uint8_t *)buf, strlen(buf));
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("请插入内存卡");
delay(8000);
/* SD SPI Mode at VSPI */
SPIClass spi = SPIClass(HSPI);
spi.begin(18 /* SCK */, 19 /* MISO */, 23 /* MOSI */, 5 /* SS */);
if (!SD.begin(5 /* SS */, spi, 80000000)) {
Serial.println("Card Mount Failed");
return;
}
testIO(SD);
SD_MMC.end(); // 取消SD挂载
void loop() {
}
采用 32GB 闪迪 TF卡出现 VSPI 同样问题,换成 32GB Kingston TF 卡正常。
参考:
[1]: Arduino ESP32 读取SD卡接口选择参考
[2]: Arduino ESP32 使用HSPI和VSPI接口挂载SD卡区别
[3]: ESP32 使用HSPI 读写SD卡的方法
[4]: 玩转 ESP32 + Arduino (二十四) SD卡读写
SD(安全数码卡,Secure Digital Memory Card)
,由 MMC
基础发展而来。
SD card
、miniSD
、microSD(TF)
。SDSC(<2GB)
、SDHC(2~32GB)
、SDXC(32GB~2TB)
SD Bus
、SPI Bus
、UHS-Ⅱ Bus
。C
字包裹一个数字的图形,是一个写入速度的标准,这个标准主要有Class 2、Class 4、Class 6、Class10。U
字包裹一个数字的图形,这个同 C 字图形一样,也是一个写入速度的标准,如果同时出现,只需要看速度最快的那个即可。V+
数字表示视频速度等级,用来表示最低读写速度。V
下面一般有罗马数字 Ⅰ
、Ⅱ
、Ⅲ
,表示最低读写速度。Ⅰ
只有一排金属触点,而其它有两排金属触点,数字越大,速度越快。(以Ⅰ为例,代表着UHS-1,Ultra High SpeedClass 1)A1
、A2(application performance class)
标签,这种卡可用于手机扩容,因此划分等级用于说明程序打开速度,A2比A1快。参考:
[1]: SD介绍
[2]: SD卡上的相关标志