乐鑫ESP32系列开发板MicroPython编程5: 使用MicroPython读写TF卡文件(the same as SD卡读写)

在ESP32-S3开发板上使用MicroPython连接TF(TransFlash,也称为microSD)卡是一个常见的需求,可以用来存储和读取大量数据。ESP32S3和C3都可以使用MicroPython连TF卡读写文件。

提前学习和准备

所需硬件

  1. ESP32-S3开发板 (约25元)
  2. MicroSD卡  容量适中即可
  3. MicroSD卡适配器模块(约3元)
  4. 连接线(USB线用于供电和编程,母对母杜邦线)

所需软件

  1. MicroPython固件‌(适用于ESP32-S3)
  2. 调试开发环境‌(如Arduino、Putty、Screen等),只要能连上调试开发板即可。

学习Micropython文档

参考:SDCard 类 – 安全数字存储卡 — MicroPython中文 1.17 文档 

参考:Quick reference for the ESP32 — MicroPython latest documentation

学习ESP32 SD/TF卡连接管脚:

手册上显示的默认(和首选)引脚分配如下(估计这是ESP32的,而不是C3和S3版本的):

Slot

0

1

2

3

Signal

Pin

Pin

Pin

Pin

sck

6

14

18

14

cmd

11

15

cs

5

15

miso

19

12

mosi

23

13

D0

7

2

D1

8

4

D2

9

12

D3

10

13

D4

16

D5

17

D6

5

D7

18

 默认0号是板载Flash用的,我们不能用。

这是ESP32的,所以还需要自己找C3和S3的接口,可以通过找参考代码,看看比人用的什么端口。

MicroPython提供的读写SD卡函数

支持的参数的确切子集因平台而异。

class machine.SDCard(slot=1width=1cd=Nonewp=Nonesck=Nonemiso=Nonemosi=Nonecs=Nonefreq=20000000)

此类使用专用 SD/MMC 接口硬件或通过 SPI 通道提供对 SD 或 MMC 存储卡的访问。该类实现了由 定义的块协议uos.AbstractBlockDev。这使得 SD 卡的安装变得如此简单:

uos.mount(machine.SDCard(), "/sd")

构造函数采用以下参数:

  • slot选择要使用的可用接口。不设置此项将选择默认界面。

  • width选择 SD/MMC 接口的总线宽度。

  • cd可用于指定卡检测引脚。

  • wp可用于指定写保护引脚。

  • sck可用于指定 SPI 时钟引脚。

  • miso可用于指定 SPI miso 引脚。

  • mosi可用于指定 SPI mosi 引脚。

  • cs可用于指定 SPI 片选引脚。

  • freq以 Hz 为单位选择 SD/MMC 接口频率(仅在 ESP32 上支持)。

不过这个函数跟开发板具体型号绑定,ESP32S3没有试成功。 

C代码参考

ESP32C3

/*
 * pin 1 - not used          |  Micro SD card     |
 * pin 2 - CS (SS)           |                   /
 * pin 3 - DI (MOSI)         |                  |__
 * pin 4 - VDD (3.3V)        |                    |
 * pin 5 - SCK (SCLK)        | 8 7 6 5 4 3 2 1   /
 * pin 6 - VSS (GND)         | ▄ ▄ ▄ ▄ ▄ ▄ ▄ ▄  /
 * pin 7 - DO (MISO)         | ▀ ▀ █ ▀ █ ▀ ▀ ▀ |
 * pin 8 - not used          |_________________|
 *                             ║ ║ ║ ║ ║ ║ ║ ║
 *                     ╔═══════╝ ║ ║ ║ ║ ║ ║ ╚═════════╗
 *                     ║         ║ ║ ║ ║ ║ ╚══════╗    ║
 *                     ║   ╔═════╝ ║ ║ ║ ╚═════╗  ║    ║
 * Connections for     ║   ║   ╔═══╩═║═║═══╗   ║  ║    ║
 * full-sized          ║   ║   ║   ╔═╝ ║   ║   ║  ║    ║
 * SD card             ║   ║   ║   ║   ║   ║   ║  ║    ║
 * Pin name         |  -  DO  VSS SCK VDD VSS DI CS    -  |
 * SD pin number    |  8   7   6   5   4   3   2   1   9 /
 *                  |                                  █/
 *                  |__▍___▊___█___█___█___█___█___█___/
 *
 * Note:  The SPI pins can be manually configured by using `SPI.begin(sck, miso, mosi, cs).`
 *        Alternatively, you can change the CS pin and use the other default settings by using `SD.begin(cs)`.
 *
 * +--------------+---------+-------+----------+----------+----------+----------+----------+
 * | SPI Pin Name | ESP8266 | ESP32 | ESP32‑S2 | ESP32‑S3 | ESP32‑C3 | ESP32‑C6 | ESP32‑H2 |
 * +==============+=========+=======+==========+==========+==========+==========+==========+
 * | CS (SS)      | GPIO15  | GPIO5 | GPIO34   | GPIO10   | GPIO7    | GPIO18   | GPIO0    |
 * +--------------+---------+-------+----------+----------+----------+----------+----------+
 * | DI (MOSI)    | GPIO13  | GPIO23| GPIO35   | GPIO11   | GPIO6    | GPIO19   | GPIO25   |
 * +--------------+---------+-------+----------+----------+----------+----------+----------+
 * | DO (MISO)    | GPIO12  | GPIO19| GPIO37   | GPIO13   | GPIO5    | GPIO20   | GPIO11   |
 * +--------------+---------+-------+----------+----------+----------+----------+----------+
 * | SCK (SCLK)   | GPIO14  | GPIO18| GPIO36   | GPIO12   | GPIO4    | GPIO21   | GPIO10   |
 * +--------------+---------+-------+----------+----------+----------+----------+----------+
 *
 * For more info see file README.md in this library or on URL:
 * https://github.com/espressif/arduino-esp32/tree/master/libraries/SD
 */

#include "FS.h"
#include "SD.h"
#include "SPI.h"

/*
Uncomment and set up if you want to use custom pins for the SPI communication
#define REASSIGN_PINS
int sck = -1;
int miso = -1;
int mosi = -1;
int cs = -1;
*/

void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if (!root) {
    Serial.println("Failed to open directory");
    return;
  }
  if (!root.isDirectory()) {
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while (file) {
    if (file.isDirectory()) {
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if (levels) {
        listDir(fs, file.path(), levels - 1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
    file = root.openNextFile();
  }
}

void createDir(fs::FS &fs, const char *path) {
  Serial.printf("Creating Dir: %s\n", path);
  if (fs.mkdir(path)) {
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

void removeDir(fs::FS &fs, const char *path) {
  Serial.printf("Removing Dir: %s\n", path);
  if (fs.rmdir(path)) {
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

void readFile(fs::FS &fs, const char *path) {
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if (!file) {
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while (file.available()) {
    Serial.write(file.read());
  }
  file.close();
}

void writeFile(fs::FS &fs, const char *path, const char *message) {
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char *path, const char *message) {
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if (!file) {
    Serial.println("Failed to open file for appending");
    return;
  }
  if (file.print(message)) {
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

void renameFile(fs::FS &fs, const char *path1, const char *path2) {
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

void deleteFile(fs::FS &fs, const char *path) {
  Serial.printf("Deleting file: %s\n", path);
  if (fs.remove(path)) {
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

void testFileIO(fs::FS &fs, const char *path) {
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if (file) {
    len = file.size();
    size_t flen = len;
    start = millis();
    while (len) {
      size_t toRead = len;
      if (toRead > 512) {
        toRead = 512;
      }
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read for %lu ms\n", flen, end);
    file.close();
  } else {
    Serial.println("Failed to open file for reading");
  }

  file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file for writing");
    return;
  }

  size_t i;
  start = millis();
  for (i = 0; i < 2048; i++) {
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written for %lu ms\n", 2048 * 512, end);
  file.close();
}

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(10);
  }

#ifdef REASSIGN_PINS
  SPI.begin(sck, miso, mosi, cs);
  if (!SD.begin(cs)) {
#else
  if (!SD.begin()) {
#endif
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();

  if (cardType == CARD_NONE) {
    Serial.println("No SD card attached");
    return;
  }

  Serial.print("SD Card Type: ");
  if (cardType == CARD_MMC) {
    Serial.println("MMC");
  } else if (cardType == CARD_SD) {
    Serial.println("SDSC");
  } else if (cardType == CARD_SDHC) {
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);

  listDir(SD, "/", 0);
  createDir(SD, "/mydir");
  listDir(SD, "/", 0);
  removeDir(SD, "/mydir");
  listDir(SD, "/", 2);
  writeFile(SD, "/hello.txt", "Hello ");
  appendFile(SD, "/hello.txt", "World!\n");
  readFile(SD, "/hello.txt");
  deleteFile(SD, "/foo.txt");
  renameFile(SD, "/hello.txt", "/foo.txt");
  readFile(SD, "/foo.txt");
  testFileIO(SD, "/test.txt");
  Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}

void loop() {}

根据这份C代码,可以看到开发板与SD卡模块连通的线序图:

也就是默认连线

ESP32C3使用:7、        6、        5、        4

ESP32S3使用:10、      11、        13、     12

对应sd卡模块:cs         mosi         clk         miso 

这样终于拿到接线图了。

python代码参考

import machine, os

# Slot 2 uses pins sck=18, cs=5, miso=19, mosi=23
sd = machine.SDCard(slot=2)
os.mount(sd, '/sd')  # mount

os.listdir('/sd')    # list directory contents

os.umount('/sd')     # eject

但是发现ESP32C3开发板没有machine.SDCard。而ESP32S3开发板有这个函数,但是使用这句也不对:sd = machine.SDCard(slot=2) 。 主要是这个slot=2是写死的,但是我的布线连接方式没法保证跟例子代码里一样啊。况且例子代码对应的是ESP32,而不是ESP32S3开发板。所以这段代码没法用,除非以后增加对ESP32S3开发板的支持(个人理解,不知道是否正确,请大家指点)。

咨询百度AI探索版

发现百度搜索有了个“探索版”按钮,发现这个探索版真不错,回答问题很全面。

问:ESP32s3 怎么用micropython 读取sd卡

回答:

在ESP32-S3上使用MicroPython读取SD卡,你需要先确保硬件连接正确,并且已经安装了支持SD卡的MicroPython固件。以下是一个基本的步骤指南,包括硬件连接、固件安装和代码示例。

硬件连接

  1. 准备硬件‌:

    • ESP32-S3开发板
    • MicroSD卡模块(如带有SPI接口的SD卡模块)
    • 连接线
  2. 连接SD卡模块到ESP32-S3‌:

    • 将SD卡模块的MISO连接到ESP32-S3的MISO(通常是GPIO12)
    • 将SD卡模块的MOSI连接到ESP32-S3的MOSI(通常是GPIO13)
    • 将SD卡模块的SCK连接到ESP32-S3的SCK(通常是GPIO14)
    • 将SD卡模块的CS(Chip Select)连接到ESP32-S3的一个可用GPIO(如GPIO15)
    • 接好电源(3.3V)和地线(GND)

安装MicroPython固件

  1. 前往MicroPython官网下载适用于ESP32的固件。
  2. 使用工具(如esptool.py)将固件刷入ESP32-S3开发板。
 
esptool.py --chip esp32-s3 --port /dev/ttyUSB0 erase_flash
esptool.py --chip esp32-s3 --port /dev/ttyUSB0 write_flash -z 0x0 path_to_firmware.bin

编写MicroPython代码

  1. 使用串行终端或IDE(如Thonny)连接到ESP32-S3。
  2. 编写以下代码来初始化SD卡并读取其内容:
 
import machine
import os
import sdcard

# 初始化SPI总线
spi = machine.SPI(2,
                  baudrate=1000000,
                  polarity=0,
                  phase=0,
                  sck=machine.Pin(14),
                  mosi=machine.Pin(13),
                  miso=machine.Pin(12))

# 初始化SD卡模块
sd = sdcard.SDCard(spi, machine.Pin(15))

# 挂载文件系统
vfs = os.VfsFat(sd)
os.mount(vfs, "/sd")

# 读取SD卡内容
print("Files on SD card:")
for file in os.listdir("/sd"):
    print(file)

# 读取某个文件的内容
with open("/sd/yourfile.txt", "r") as file:
    content = file.read()
    print(content)

# 卸载文件系统
os.umount("/sd")

注意事项

  1. 引脚配置‌:确保你使用的引脚与你的硬件连接一致。如果你使用的是不同的引脚,请相应地修改代码。
  2. 模块选择‌:本文假设你使用的是带有SPI接口的SD卡模块。如果你使用的是其他类型的模块(如SDIO),请查阅相关文档进行配置。
  3. 文件系统‌:确保SD卡上已经有文件系统(如FAT32),并且包含你要读取的文件。

通过这些步骤,你应该能够在ESP32-S3上使用MicroPython成功读取SD卡的内容。如果遇到问题,请检查硬件连接、固件版本和代码配置是否正确。

实践

在使用ESP32C3的时候,发现没有machine.SDCard,于是转而使用了ESP32S3开发板。其实后来才知道,不需要再使用machine.SDCard,只要使用sdcard.SDCard即可,当然sdcard这个软件包需要用mip来安装。

连线

根据C语言代码,我们知道了线序:

ESP32S3使用:10、      11、        13、     12

对应sd卡模块:cs         mosi         clk         miso 

根据这份线序图,上面那段Python代码的连线部分应该进行相应改动,最终为:

spi = machine.SPI(2,
                  baudrate=1000000,
                  polarity=0,
                  phase=0,
                  sck=machine.Pin(13),
                  mosi=machine.Pin(11),
                  miso=machine.Pin(12))

# 初始化SD卡模块
sd = sdcard.SDCard(spi, machine.Pin(10))

前期需要处理事项:

1 打开开发板wifi

import wifi

这样wifi就打开了。wifi.py文件是通过webrepl传送上来的,内容:

import network
import time
nic = network.WLAN(network.STA_IF)
nic.active(True)

if not nic.isconnected():
    nic.connect("showmemoney", "xxxx")
    print("Waiting for h...")
    while not nic.isconnected():
        time.sleep(1)
 
 
print(nic.ipconfig("addr4"))

 直接执行上面内容即可。也可以打开webrepl,然后把wifi.py文件上传到开发板,这样每次只要执行import wifi即可打开WiFi。打开webrepl使用命令:

import webrepl
webrepl.start()

2 安装sdcard软件包

wifi启动后,就可以上网安装sdcard软件包了,命令:

import mip
mip.install("sdcard")

安装完成sdcard软件包,就可以运行下面的代码了:

最终MicroPython读写SD/TF卡代码:

import machine
import os
import sdcard

# 初始化SPI总线
spi = machine.SPI(2,
                  baudrate=1000000,
                  polarity=0,
                  phase=0,
                  sck=machine.Pin(13),
                  mosi=machine.Pin(11),
                  miso=machine.Pin(12))

# 初始化SD卡模块
sd = sdcard.SDCard(spi, machine.Pin(10))

# 挂载文件系统
vfs = os.VfsFat(sd)
os.mount(vfs, "/sd")

# 写文件
with open("/sd/test.txt", "w") as file:
    file.write("hello ")
    file.write("world!")

# 读取SD卡内容
print("Files on SD card:")
for file in os.listdir("/sd"):
    print(file)

# 读取某个文件的内容
with open("/sd/test.txt", "r") as file:
    content = file.read()
    print(content)

# 卸载文件系统
os.umount("/sd")

运行输出:

>>> with open("/sd/test.txt", "w") as file:
...     file.write("hello")
...     file.write("world!")
...
5
6
>>> # SD
>>> print("Files on SD card:")
Files on SD card:
>>> for file in os.listdir("/sd"):
...     print(file)
...
test.txt
>>> #
>>> with open("/sd/test.txt", "r") as file:
...     content = file.read()
...     print(content)
...
helloworld!
>>> #
>>> os.umount("/sd")

 非常棒,读取sd卡就这么简单啊!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值