300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 物联网开发笔记(60)- 使用Micropython开发ESP32开发板之SPI接口控制Micro SD卡TF卡模块

物联网开发笔记(60)- 使用Micropython开发ESP32开发板之SPI接口控制Micro SD卡TF卡模块

时间:2019-05-14 09:08:28

相关推荐

物联网开发笔记(60)- 使用Micropython开发ESP32开发板之SPI接口控制Micro SD卡TF卡模块

一、目的

这一节我们学习如何使用我们的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芯片选择引脚。

三、ESP固件版本

esp32固件版本MicroPython v1.19.1 on -06-18;

Micropython ESP32对TF卡容量可支持128GB,本人没有对此做过验证,目前手上没有这么大容量的卡,但是需要注意的是,对某些未知的TF不支持,不管容量多少就是识别不到。另外需要注意的是,目前只支持 FAT/FAT32格式的卡.。

128GB容量TF卡相关验证请看:

四、演示效果

sdcard模块可以在MicroPython源码中找到。

MicroPython源码:/micropython/micropython

"""MicroPython driver for SD cards using SPI bus.Requires an SPI bus and a CS pin. Provides readblocks and writeblocksmethods so the device can be mounted as a filesystem.Example usage on pyboard:import pyb, sdcard, ossd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)pyb.mount(sd, '/sd2')os.listdir('/')Example usage on ESP8266:import machine, sdcard, ossd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))os.mount(sd, '/sd')os.listdir('/')"""from micropython import constimport 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 = spiself.cs = csself.cmdbuf = bytearray(6)self.dummybuf = bytearray(512)self.tokenbuf = bytearray(1)for i in range(512):self.dummybuf[i] = 0xFFself.dummybuf_memoryview = memoryview(self.dummybuf)# initialise the cardself.init_card(baudrate)def init_spi(self, baudrate):try:master = self.spi.MASTERexcept AttributeError:# on ESP8266self.spi.init(baudrate=baudrate, phase=0, polarity=0)else:# on pyboardself.spi.init(master, baudrate=baudrate, phase=0, polarity=0)def init_card(self, baudrate):# init CS pinself.cs.init(self.cs.OUT, value=1)# init SPI bus; use low data rate for initialisationself.init_spi(100000)# clock card at least 100 cycles with cs highfor 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:breakelse:raise OSError("no SD card")# CMD8: determine card versionr = 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.0self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB)c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7read_bl_len = csd[5] & 0b1111capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len)self.sectors = capacity // 512else:raise OSError("SD card CSD format not supported")# print('sectors', self.sectors)# CMD16: set block length to 512 bytesif self.cmd(16, 512, 0) != 0:raise OSError("can't set 512 block size")# set to high data rate now that it's initialisedself.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 commandsself.cdv = 512# print("[SDCard] v1 card")returnraise 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 byteocr = self.tokenbuf[0] # get first byte of response, which is OCRif not ocr & 0x40:# SDSC card, uses byte addressing in read/write/erase commandsself.cdv = 512else:# SDHC/SDXC card, uses block addressing in read/write/erase commandsself.cdv = 1# print("[SDCard] v2 card")returnraise 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 commandbuf = self.cmdbufbuf[0] = 0x40 | cmdbuf[1] = arg >> 24buf[2] = arg >> 16buf[3] = arg >> 8buf[4] = argbuf[5] = crcself.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 restif final < 0:self.spi.readinto(self.tokenbuf, 0xFF)final = -1 - finalfor j in range(final):self.spi.write(b"\xff")if release:self.cs(1)self.spi.write(b"\xff")return response# timeoutself.cs(1)self.spi.write(b"\xff")return -1def 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:breaktime.sleep_ms(1)else:self.cs(1)raise OSError("timeout waiting for response")# read datamv = self.dummybuf_memoryviewif len(buf) != len(mv):mv = mv[: len(buf)]self.spi.write_readinto(mv, buf)# read checksumself.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, checksumself.spi.read(1, token)self.spi.write(buf)self.spi.write(b"\xff")self.spi.write(b"\xff")# check the responseif (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:self.cs(1)self.spi.write(b"\xff")return# wait for write to finishwhile self.spi.read(1, 0xFF)[0] == 0:passself.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 finishwhile self.spi.read(1, 0xFF)[0] == 0x00:passself.cs(1)self.spi.write(b"\xff")def readblocks(self, block_num, buf):nblocks = len(buf) // 512assert nblocks and not len(buf) % 512, "Buffer length is invalid"if nblocks == 1:# CMD17: set read address for single blockif self.cmd(17, block_num * self.cdv, 0, release=False) != 0:# release the cardself.cs(1)raise OSError(5) # EIO# receive the data and release cardself.readinto(buf)else:# CMD18: set read address for multiple blocksif self.cmd(18, block_num * self.cdv, 0, release=False) != 0:# release the cardself.cs(1)raise OSError(5) # EIOoffset = 0mv = memoryview(buf)while nblocks:# receive the data and release cardself.readinto(mv[offset : offset + 512])offset += 512nblocks -= 1if self.cmd(12, 0, 0xFF, skip1=True):raise OSError(5) # EIOdef 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 blockif self.cmd(24, block_num * self.cdv, 0) != 0:raise OSError(5) # EIO# send the dataself.write(_TOKEN_DATA, buf)else:# CMD25: set write address for first blockif self.cmd(25, block_num * self.cdv, 0) != 0:raise OSError(5) # EIO# send the dataoffset = 0mv = memoryview(buf)while nblocks:self.write(_TOKEN_CMD25, mv[offset : offset + 512])offset += 512nblocks -= 1self.write_token(_TOKEN_STOP_TRAN)def ioctl(self, op, arg):if op == 4: # get number of blocksreturn self.sectorsif op == 5: # get block size in bytesreturn 512

五、代码(SoftSPI即软SPI通知)

测试代码1,test1.py。运行代码前,需要先将sdcard模块保存到MicroPython设备当中。

import osfrom machine import Pin, SoftSPIfrom sdcard import SDCard# 接线说明:# MISO -> GPTO13# MOSI -> GPIO12# SCK -> GPIO 14# CS -> GPIO27spisd=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 osfrom machine import Pin, SoftSPIfrom sdcard import SDCard# 接线说明:# MISO -> GPTO13# MOSI -> GPIO12# SCK -> GPIO 14# CS -> GPIO27spisd=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总线

测试代码3,test3.py:

import machine, sdcard, osfrom machine import SPIfrom machine import PinSD_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/sddirs=os.listdir('/sd')for file in dirs: print(file)

测试代码4,test4.py:

import os, sdcard, machinefrom machine import SPIfrom machine import Pindef 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 charsshort = "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 blockprint(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 = Trueif result1 == "".join((lines, short, lines)):print("Large file Pass")else:print("Large file Fail")success = Falseif result2 == short:print("Small file Pass")else:print("Small file Fail")success = Falseprint()print("Tests", "passed" if success else "failed")if __name__ == '__main__':sdtest()

五、购买

某宝链接如下:

/item.htm?spm=a1z09.2.0.0.6cde2e8dg7IWOL&id=617159768229&_u=bp01rchc8d5

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。