ESP32与SD卡交互实现:文件读写实战与初始化详解及引脚定义

发布于:2024-04-26 ⋅ 阅读:(36) ⋅ 点赞:(0)

本代码实现ESP32与SD卡的交互,包括定义SPI引脚、创建自定义SPI类实例、编写WriteFile与ReadFile函数进行文件读写。setup函数初始化串口、SPI、SD卡,向“/test.txt”写入“myfirstmessage”,读取并打印其内容。loop函数留空待扩展。

1. 需要准备的软硬件:

1.1 硬件:

  1. ESP32开发板在这里插入图片描述

  2. SD卡模块(如下图),可以是单独的TF卡模块也可以是集成到TFT屏幕的SD模块,TF卡与SD卡的硬件逻辑是一样,这主里不做区分在这里插入图片描述

  3. SD卡,Arduino 库仅支持 FAT16 和 FAT32 文件系统。 确保您的 SD 卡仅使用这两种格式进行格式化,否则将初始化错误。
    写本文时,在电脑上将SD卡以FAT32格式来格式化
    对于不同卡格式如下:

  • 对于SDSC卡:
    容量介于 16 MB 和 32 MB 之间 - FAT 16
    容量大于32 MB-FAT 16B
  • 对于SDHC卡
    容量小于 7.8 GB- FAT 32
    容量大于 7.8 GB- FAT 32
  • 对于SDXC卡
    exFAT

1.2 软件

  1. Arduino IDE或者在VS Code里的PlatformIO

1.3 连线方式一及代码

1.3.1 连接方式一如下表,本连线方式是用的ESP32的默认SPI接线方式,注意1.1的引脚图里23脚是VSPI MISO, 19脚是VSPI MOSI, 18脚是VSPI CLK 因此在接下来的代码中没有单独定义MISO、MOSI和CLK引脚。

引脚名称 描述 引脚编号
CS Chip Select 5
SCK Clock 18
MISO Master In Slave Out 19
MOSI Master Out Slave In 23

1.3.2 代码一:

/*
  SD Card Interface code for ESP32
  SPI Pins of ESP32 SD card as follows:
  CS    = 5;
  MOSI  = 23;
  MISO  = 19;
  SCK   = 18; 
*/

#include <SPI.h>
#include <SD.h>

File myFile;
const int CS = 5;

/**
 * @brief 将指定消息写入到文件中。
 * 
 * @param path 指向要写入的文件路径的字符指针。
 * @param message 指向要写入文件的消息的字符指针。
 * 该函数尝试打开指定路径的文件以进行写操作。如果文件成功打开,它将写入给定的消息,
 * 然后关闭文件,并通过串口打印一条完成消息。如果无法打开文件,它将打印错误消息和文件路径。
 */
void WriteFile(const char *path, const char *message){
  // 尝试打开文件以写入
  myFile = SD.open(path, FILE_WRITE);
  if (myFile) { // 文件打开成功
    Serial.printf("Writing to %s ", path); // 打印正在写入的文件路径
    myFile.println(message); // 写入消息到文件
    myFile.close(); // 关闭文件
    Serial.println("completed."); // 打印写入完成信息
  } 
  else { // 文件打开失败
    Serial.println("error opening file "); // 打印错误信息
    Serial.println(path); // 打印文件路径
  }
}

/**
 * 读取指定路径的文件
 * 
 * @param path 指向要打开的文件路径的字符指针
 * 
 * 该函数尝试打开指定路径的文件,如果成功打开,则逐字节读取文件内容并通过串口打印。
 * 如果无法打开文件,将打印错误信息。
 */
void ReadFile(const char *path){
  myFile = SD.open(path); // 尝试打开文件
  
  if (myFile) { // 如果文件成功打开
     Serial.printf("Reading file from %s\n", path); // 打印读取文件的路径信息
     while (myFile.available()) { // 循环,直到文件没有更多可用数据
      Serial.write(myFile.read()); // 读取并打印文件的一个字节
    }
    myFile.close(); // 关闭文件
  } 
  else {
    Serial.println("error opening test.txt"); // 如果无法打开文件,打印错误信息
  }
}

void setup() {
  Serial.begin(9600);
  delay(500);
  while (!Serial) { ; }
  Serial.println("Initializing SD card...");
  if (!SD.begin(CS)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  WriteFile("/test.txt", "ElectronicWings.com");
  ReadFile("/test.txt");
}

void loop() {
  // nothing happens after setup
}

1.4 连线方式二及代码

1.4.1 连接方式二如下表,本连线方式并没有使用ESP32默认的引脚,而是对SPI各个引脚进行了重定义

引脚名称 描述 引脚编号
CS Chip Select 23
SCK Clock 16
MISO Master In Slave Out 2
MOSI Master Out Slave In 17

1.4.2 代码二:

与代码一的主要不同只有下面几行:

const int MOSI_PIN = 17; // 定义SPI通信中MOSI(Master Out Slave In)信号的常量
const int MISO_PIN = 2; // 定义SPI通信中MISO(Master In Slave Out)信号的常量
const int SCK_PIN = 16; // 定义SPI通信中SCK(Serial Clock)信号的常量

// 定义一个自定义的SPI类实例
SPIClass CustomSPI;

void setup(){
  // 初始化自定义SPI通信,传入SCK、MISO、MOSI引脚及CS控制信号
  CustomSPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS);

    // 初始化SD卡,如果初始化失败则打印错误信息并返回
  if (!SD.begin(CS, CustomSPI, 1000000))
  {
    Serial.println("initialization failed!");
    return;
  }
}


完整代码如下:

#include <Arduino.h>
/*
  SD Card Interface code for ESP32
  SPI Pins of ESP32 SD card as follows:
  CS    = 23;
  MOSI  = 17;
  MISO  = 2;
  SCK   = 16;
*/

#include <SPI.h>
#include <SD.h>

// 定义一个文件对象
File myFile; 

const int CS = 23; // 定义SPI通信中CS(Chip Select)信号的常量
const int MOSI_PIN = 17; // 定义SPI通信中MOSI(Master Out Slave In)信号的常量
const int MISO_PIN = 2; // 定义SPI通信中MISO(Master In Slave Out)信号的常量
const int SCK_PIN = 16; // 定义SPI通信中SCK(Serial Clock)信号的常量

// 定义一个自定义的SPI类实例
SPIClass CustomSPI;
/**
 * @brief 将指定消息写入到文件中。
 * 
 * @param path 指向要写入的文件路径的字符指针。
 * @param message 指向要写入文件的消息的字符指针。
 * 说明:函数不返回任何值,但会在串口打印操作的结果。
 */
void WriteFile(const char *path, const char *message)
{
  // 尝试打开文件以便写入
  myFile = SD.open(path, FILE_WRITE);
  if (myFile)
  {
    // 打印正在写入的文件路径
    Serial.printf("Writing to %s ", path);
    // 写入消息到文件,并在末尾添加换行符
    myFile.println(message);
    // 关闭文件
    myFile.close(); 
    // 打印写入操作完成的通知
    Serial.println("completed.");
  }

  else
  {
    // 打印打开文件失败的通知
    Serial.println("error opening file ");
    // 打印失败的文件路径
    Serial.println(path);
  }
}

/**
 * 读取指定路径的文件
 * 
 * @param path 指向要打开的文件路径的字符指针
 * 
 * 该函数尝试打开指定路径的文件,如果成功打开,则逐字节读取文件内容并通过串口打印。
 * 如果无法打开文件,将打印错误信息。
 */
void ReadFile(const char *path)
{
  myFile = SD.open(path); // 尝试打开文件
  
  if (myFile) // 如果文件成功打开
  {
    Serial.printf("Reading file from %s\n", path); // 打印读取文件的路径信息
    while (myFile.available()) // 循环读取文件中的所有内容
    {
      Serial.write(myFile.read()); // 将读取到的每个字节通过串口发送
    }
    myFile.close(); // 关闭文件
  }
  else // 如果文件打开失败
  {
    Serial.println("error opening test.txt"); // 打印错误信息
  }
}

/**
 * @brief 初始化设置函数
 * 该函数主要完成以下初始化工作:
 * 1. 初始化串口通信,设置波特率为9600;
 * 2. 初始化自定义SPI通信,并传入SCK、MISO、MOSI引脚及CS控制信号;
 * 3. 等待串口完全初始化;
 * 4. 初始化SD卡,如果初始化失败则打印错误信息并返回;
 * 5. 写入测试文件"/test.txt";
 * 6. 读取测试文件"/test.txt"的内容。
 * 
 * 该函数没有参数和返回值。
 */
void setup()
{
  // 初始化串口通信,设置波特率为9600,并延迟500ms
  Serial.begin(9600);
  delay(500);

  // 初始化自定义SPI通信,传入SCK、MISO、MOSI引脚及CS控制信号
  CustomSPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS);

  // 等待直到串口完全初始化
  while (!Serial)
  {
    ;
  } 

  // 打印初始化SD卡的开始信息
  Serial.println("Initializing SD card...");
  
  // 初始化SD卡,如果初始化失败则打印错误信息并返回
  if (!SD.begin(CS, CustomSPI, 1000000))
  {
    Serial.println("initialization failed!");
    return;
  }
  
  // 打印SD卡初始化成功的消息
  Serial.println("initialization done.");

  // 写入测试文件"/test.txt"
  WriteFile("/test.txt", "myfirstmessage");
  
  // 读取测试文件"/test.txt"的内容
  ReadFile("/test.txt");
  //打印读取到的内容
  Serial.println("Reading from test.txt");

}

void loop()
{

}