Micropython学习交流群 学习QQ群:786510434 提供多种固件下载和学习交流。
Micropython-扇贝物联 QQ群:31324057 扇贝物联是一个让你与智能设备沟通更方便的物联网云平台
Micropython学习交流群 学习QQ群:468985481 学习交流ESP8266、ESP32、ESP8285、wifi模块开发交流、物联网。
Micropython老哥俩的IT农场分享QQ群:929132891 为喜欢科创制作的小白们分享一些自制的计算机软硬件免费公益课程,由两位多年从事IT研发的中年大叔发起。
一、目的
这一节我们学习如何使用我们的ESP32开发板来通过SPI接口控制Micro SD卡TF卡模块。
二、环境
ESP32 + SPI接口控制Micro SD卡TF卡模块 + Thonny IDE + 几根杜邦线
接线方法:
Soft SPI接线说明
# 接线说明:
# MISO -> GPTO13
# MOSI -> GPIO12
# SCK -> GPIO 14
# CS -> GPIO27
SPI通讯
1.GND-for the ground pins.
2.VCC-for the supply voltage.
3.MISO-for the SPI Master Input Slave Output pin.
4.MOSI-for the SPI Master Output Slave Input pin.
5.SCK-for the SPI Serial Clock pin.
6.CS-for the SPI Chip Select pin.
gnd -用于接地插脚。
vcc- 电源电压。
miso-用于SPI主输入从输出引脚。
mosi -用于SPI主输出从输入引脚。
sck -用于SPI串行时钟引脚。
cs -用于SPI芯片选择引脚。
————————————————
Micropython ESP32对TF卡容量可支持128GB,本人没有对此做过验证,目前手上没有这么大容量的卡,但是需要注意的是,对某些未知的TF不支持,不管容量多少就是识别不到。另外需要注意的是,目前只支持 FAT/FAT32格式的卡.。
128GB容量TF卡相关验证请看:
四、演示效果
sdcard模块可以在MicroPython源码中找到。
MicroPython源码:https://github.com/micropython/micropython
————————————————
sdcard.py
""" MicroPython driver for SD cards using SPI bus. Requires an SPI bus and a CS pin. Provides readblocks and writeblocks methods so the device can be mounted as a filesystem. Example usage on pyboard: import pyb, sdcard, os sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5) pyb.mount(sd, '/sd2') os.listdir('/') Example usage on ESP8266: import machine, sdcard, os sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15)) os.mount(sd, '/sd') os.listdir('/') """ from micropython import const import time _CMD_TIMEOUT = const(100) _R1_IDLE_STATE = const(1 << 0) # R1_ERASE_RESET = const(1 << 1) _R1_ILLEGAL_COMMAND = const(1 << 2) # R1_COM_CRC_ERROR = const(1 << 3) # R1_ERASE_SEQUENCE_ERROR = const(1 << 4) # R1_ADDRESS_ERROR = const(1 << 5) # R1_PARAMETER_ERROR = const(1 << 6) _TOKEN_CMD25 = const(0xFC) _TOKEN_STOP_TRAN = const(0xFD) _TOKEN_DATA = const(0xFE) class SDCard: def __init__(self, spi, cs, baudrate=1320000): self.spi = spi self.cs = cs self.cmdbuf = bytearray(6) self.dummybuf = bytearray(512) self.tokenbuf = bytearray(1) for i in range(512): self.dummybuf[i] = 0xFF self.dummybuf_memoryview = memoryview(self.dummybuf) # initialise the card self.init_card(baudrate) def init_spi(self, baudrate): try: master = self.spi.MASTER except AttributeError: # on ESP8266 self.spi.init(baudrate=baudrate, phase=0, polarity=0) else: # on pyboard self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) def init_card(self, baudrate): # init CS pin self.cs.init(self.cs.OUT, value=1) # init SPI bus; use low data rate for initialisation self.init_spi(100000) # clock card at least 100 cycles with cs high for i in range(16): self.spi.write(b"\xff") # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) for _ in range(5): if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE: break else: raise OSError("no SD card") # CMD8: determine card version r = self.cmd(8, 0x01AA, 0x87, 4) if r == _R1_IDLE_STATE: self.init_card_v2() elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): self.init_card_v1() else: raise OSError("couldn't determine SD card version") # get the number of sectors # CMD9: response R2 (R1 byte + 16-byte block read) if self.cmd(9, 0, 0, 0, False) != 0: raise OSError("no response from SD card") csd = bytearray(16) self.readinto(csd) if csd[0] & 0xC0 == 0x40: # CSD version 2.0 self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB) c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6 c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7 read_bl_len = csd[5] & 0b1111 capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len) self.sectors = capacity // 512 else: raise OSError("SD card CSD format not supported") # print('sectors', self.sectors) # CMD16: set block length to 512 bytes if self.cmd(16, 512, 0) != 0: raise OSError("can't set 512 block size") # set to high data rate now that it's initialised self.init_spi(baudrate) def init_card_v1(self): for i in range(_CMD_TIMEOUT): self.cmd(55, 0, 0) if self.cmd(41, 0, 0) == 0: # SDSC card, uses byte addressing in read/write/erase commands self.cdv = 512 # print("[SDCard] v1 card") return raise OSError("timeout waiting for v1 card") def init_card_v2(self): for i in range(_CMD_TIMEOUT): time.sleep_ms(50) self.cmd(58, 0, 0, 4) self.cmd(55, 0, 0) if self.cmd(41, 0x40000000, 0) == 0: self.cmd(58, 0, 0, -4) # 4-byte response, negative means keep the first byte ocr = self.tokenbuf[0] # get first byte of response, which is OCR if not ocr & 0x40: # SDSC card, uses byte addressing in read/write/erase commands self.cdv = 512 else: # SDHC/SDXC card, uses block addressing in read/write/erase commands self.cdv = 1 # print("[SDCard] v2 card") return raise OSError("timeout waiting for v2 card") def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): self.cs(0) # create and send the command buf = self.cmdbuf buf[0] = 0x40 | cmd buf[1] = arg >> 24 buf[2] = arg >> 16 buf[3] = arg >> 8 buf[4] = arg buf[5] = crc self.spi.write(buf) if skip1: self.spi.readinto(self.tokenbuf, 0xFF) # wait for the response (response[7] == 0) for i in range(_CMD_TIMEOUT): self.spi.readinto(self.tokenbuf, 0xFF) response = self.tokenbuf[0] if not (response & 0x80): # this could be a big-endian integer that we are getting here # if final<0 then store the first byte to tokenbuf and discard the rest if final < 0: self.spi.readinto(self.tokenbuf, 0xFF) final = -1 - final for j in range(final): self.spi.write(b"\xff") if release: self.cs(1) self.spi.write(b"\xff") return response # timeout self.cs(1) self.spi.write(b"\xff") return -1 def readinto(self, buf): self.cs(0) # read until start byte (0xff) for i in range(_CMD_TIMEOUT): self.spi.readinto(self.tokenbuf, 0xFF) if self.tokenbuf[0] == _TOKEN_DATA: break time.sleep_ms(1) else: self.cs(1) raise OSError("timeout waiting for response") # read data mv = self.dummybuf_memoryview if len(buf) != len(mv): mv = mv[: len(buf)] self.spi.write_readinto(mv, buf) # read checksum self.spi.write(b"\xff") self.spi.write(b"\xff") self.cs(1) self.spi.write(b"\xff") def write(self, token, buf): self.cs(0) # send: start of block, data, checksum self.spi.read(1, token) self.spi.write(buf) self.spi.write(b"\xff") self.spi.write(b"\xff") # check the response if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05: self.cs(1) self.spi.write(b"\xff") return # wait for write to finish while self.spi.read(1, 0xFF)[0] == 0: pass self.cs(1) self.spi.write(b"\xff") def write_token(self, token): self.cs(0) self.spi.read(1, token) self.spi.write(b"\xff") # wait for write to finish while self.spi.read(1, 0xFF)[0] == 0x00: pass self.cs(1) self.spi.write(b"\xff") def readblocks(self, block_num, buf): nblocks = len(buf) // 512 assert nblocks and not len(buf) % 512, "Buffer length is invalid" if nblocks == 1: # CMD17: set read address for single block if self.cmd(17, block_num * self.cdv, 0, release=False) != 0: # release the card self.cs(1) raise OSError(5) # EIO # receive the data and release card self.readinto(buf) else: # CMD18: set read address for multiple blocks if self.cmd(18, block_num * self.cdv, 0, release=False) != 0: # release the card self.cs(1) raise OSError(5) # EIO offset = 0 mv = memoryview(buf) while nblocks: # receive the data and release card self.readinto(mv[offset : offset + 512]) offset += 512 nblocks -= 1 if self.cmd(12, 0, 0xFF, skip1=True): raise OSError(5) # EIO def writeblocks(self, block_num, buf): nblocks, err = divmod(len(buf), 512) assert nblocks and not err, "Buffer length is invalid" if nblocks == 1: # CMD24: set write address for single block if self.cmd(24, block_num * self.cdv, 0) != 0: raise OSError(5) # EIO # send the data self.write(_TOKEN_DATA, buf) else: # CMD25: set write address for first block if self.cmd(25, block_num * self.cdv, 0) != 0: raise OSError(5) # EIO # send the data offset = 0 mv = memoryview(buf) while nblocks: self.write(_TOKEN_CMD25, mv[offset : offset + 512]) offset += 512 nblocks -= 1 self.write_token(_TOKEN_STOP_TRAN) def ioctl(self, op, arg): if op == 4: # get number of blocks return self.sectors if op == 5: # get block size in bytes return 512
测试代码1,test1.py。运行代码前,需要先将sdcard模块保存到MicroPython设备当中。
import os from machine import Pin, SoftSPI from sdcard import SDCard # 接线说明: # MISO -> GPTO13 # MOSI -> GPIO12 # SCK -> GPIO 14 # CS -> GPIO27 spisd=SoftSPI(-1, miso=Pin(13), mosi=Pin(12), sck=Pin(14)) sd=SDCard(spisd, Pin(27)) print('Root directory:{}'.format(os.listdir())) vfs=os.VfsFat(sd) os.mount(vfs,'/sd') print('Root directory:{}'.format(os.listdir())) os.chdir('sd') print('SD Card contains:{}'.format(os.listdir()))
测试代码2,test2.py。添加对SD卡容量信息读取
import os from machine import Pin, SoftSPI from sdcard import SDCard # 接线说明: # MISO -> GPTO13 # MOSI -> GPIO12 # SCK -> GPIO 14 # CS -> GPIO27 spisd=SoftSPI(-1, miso=Pin(13), mosi=Pin(12), sck=Pin(14)) sd=SDCard(spisd, Pin(27)) print('Root directory:{}'.format(os.listdir())) vfs=os.VfsFat(sd) os.mount(vfs,'/sd') r = os.statvfs('/sd') print('SD capacity: {} B / {} M'.format(r[0] * r[2], r[0] * r[2]/1024/1024)) print('free space: {} B / {} M'.format(r[0] * r[3], r[0] * r[3]/1024/1024)) print('Root directory:{}'.format(os.listdir())) os.chdir('sd') print('SD Card contains:{}'.format(os.listdir()))
代码(SPI即硬SPI通知)
这几个信号线的接线位置更换如下:
sck=Pin(17),
mosi=Pin(23),
miso=Pin(19)
SD_CS= Pin(5)
引脚接线说明
使用的是VSPI总线
SPI MOSI MISO CLK CS
VSPI 23 19 18 5
————————————————
测试代码3
import machine, sdcard, os from machine import SPI from machine import Pin SD_CS = Pin(5) sd = sdcard.SDCard(SPI(2,sck=Pin(17), mosi=Pin(23),miso=Pin(19)), SD_CS) # 初始化⽂件系统 vfs = os.VfsFat(sd)# fat挂载卡到⽬录下 os.mount(sd,"/sd")# SD/sd dirs=os.listdir('/sd') for file in dirs: print(file)
测试代码4
import os, sdcard, machine from machine import SPI from machine import Pin def sdtest(): SD_CS = Pin(5) sd = sdcard.SDCard(SPI(2,sck=Pin(17), mosi=Pin(23),miso=Pin(19)), SD_CS) vfs = os.VfsFat(sd) os.mount(vfs, "/fc") print("Filesystem check") print(os.listdir("/fc")) line = "abcdefghijklmnopqrstuvwxyz\n" lines = line * 200 # 5400 chars short = "1234567890\n" fn = "/fc/rats.txt" print() print("Multiple block read/write") with open(fn, "w") as f: n = f.write(lines) print(n, "bytes written") n = f.write(short) print(n, "bytes written") n = f.write(lines) print(n, "bytes written") with open(fn, "r") as f: result1 = f.read() print(len(result1), "bytes read") fn = "/fc/rats1.txt" print() print("Single block read/write") with open(fn, "w") as f: n = f.write(short) # one block print(n, "bytes written") with open(fn, "r") as f: result2 = f.read() print(len(result2), "bytes read") os.umount("/fc") print() print("Verifying data read back") success = True if result1 == "".join((lines, short, lines)): print("Large file Pass") else: print("Large file Fail") success = False if result2 == short: print("Small file Pass") else: print("Small file Fail") success = False print() print("Tests", "passed" if success else "failed") if __name__ == '__main__': sdtest()
版权声明:本文为CSDN博主「魔都飘雪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhusongziye/article/details/128278381
Copyright © 2014 ESP56.com All Rights Reserved
执行时间: 0.0081081390380859 seconds