关键词:
【MicroPython ESP32】通过sdcard模块软SPI读取SD卡实例
- 相关篇《【MicroPython ESP32】通过sdcard模块读取SD卡实例》
- 本实验基于
Thonny
平台开发。esp32固件版本MicroPython v1.19.1 on 2022-06-18; ESP32 module with ESP32
Micro SD
卡模块(TF卡读写卡器 SPI 带电平转换芯片)
sdcard
模块
sdcard
模块可以在MicroPython
源码中找到。
"""
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
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芯片选择引脚。
- esp32 Dev kit V1
- 本实例需要引入
sdcard
模块
运行代码前,需要先将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()))
micropythonesp32通过ntptime模块获取ntp获取网络时间(代码片段)
【MicroPythonESP32】通过ntptime模块获取NTP获取网络时间本示例基于Thonny平台开发。实例代码importtime,ntptime,networkdefwifi_main():globalinifwifi=network.WLAN(network.STA_IF)#WIFI模式ifnotwifi.isconnected():print('wifiing... 查看详情
micropythonesp32入网和udp数据收发通讯示例(代码片段)
【MicroPythonESP32】入网和udp数据收发通讯示例相关篇《【MicroPythonESP32】入网和udp数据接收通讯示例》本示例基于Thonny平台开发。本实验利用ESP32,通过microPython编程实现入网和通过udp网络通讯功能实现,远程控制板载led亮... 查看详情
micropythonesp32入网和udp数据接收通讯示例(代码片段)
【MicroPythonESP32】入网和udp数据接收通讯示例本示例基于Thonny平台开发。本实验利用ESP32,通过microPython编程实现入网和通过udp网络通讯功能实现,远程控制板载led亮灭。功能演示所需工具NetAssist网络调试助手:http://www... 查看详情
micropythonesp32触摸传感器使用示例(代码片段)
【MicroPythonESP32】触摸传感器使用示例本示例基于Thonny平台开发触摸传感器ESP32可提供多达10个触摸GPIO。这10个触摸GPIO为0,2,4,12,13,14,15,27,32,33frommachineimportTouchPad触摸功能函数属于machine模块内的TouchPad类。通过Shell调试窗口查询:... 查看详情
micropythonesp32外部中断使用示例(代码片段)
【MicroPythonESP32】外部中断使用示例✨本案例基于Thonny平台开发。✨📌ESP32外部中断相关函数在machine模块中-🌿通过Thonny平台,Shell调试窗口查询相关函数:>>>importmachine>>>help(machine)📖中断相关函... 查看详情
micropythonesp32扫描i2c设备地址(代码片段)
【MicroPythonESP32】扫描I2C设备地址本示例基于Thonny平台开发。通过I2C扫描获取挂载在外设上的设备地址,已方便管理和了解I2C设备是否正常运行状态。有关Micropythonesp32i2c外设功能介绍可以参考官方说明文档:http://docs.microp... 查看详情
micropythonesp32httpget方法获取网络时间+oled显示(代码片段)
【MicroPythonESP32】HttpGet方法获取网络时间+OLed显示效果演示本示例基于Thonny平台开发。通过Http请求Get方法获取有关时间戳数据,然后通过将json数据解析,和时间换算,最终得到网络时间。这个和NTP直接获取时间有... 查看详情
micropythonesp32入网和tcp数据发送通讯示例(代码片段)
【MicroPythonESP32】入网和TCP数据发送通讯示例本示例基于Thonny平台开发。通过TCP数据发送到指定的服务端,相当于数据实时进行上报。业务代码需要自行添加和完善。演示功能模块简介入网部分#入网部分defwlan_connect(ssid,password... 查看详情
micropythonesp32ssd1306模块基于gb2312字库驱动0.96“i2c屏幕汉字显示示例(代码片段)
【MicroPythonESP32】ssd1306模块基于GB2312字库驱动0.96“I2C屏幕汉字显示示例本示例基于Thonny平台开发。本篇参考和借鉴《ssd1306OLED显示屏-MicroPython-ESP32-中文显示-利用GB2312字库(非手动取模)》文章的资源实现。本示例中文内... 查看详情
micropythonesp32udp和tcp数据收发通讯综合实例(代码片段)
【MicroPythonESP32】UDP和TCP数据收发通讯综合实例本示例基于Thonny平台开发。所需工具网络调试助手UDP和TCP4种通讯模式MicroPythonESP32设备作为TCP客户端MicroPythonESP32设备作为TCP服务端MicroPythonESP32设备作为UDP客户端MicroPythonESP32设备作为... 查看详情
micropythonesp32machine.pin类函数以及参数详解(代码片段)
【MicroPythonESP32】machine.Pin类函数以及参数详解✨本案例基于Thonny平台开发。✨🔖通过Shell调试窗口查询Pin类的的相关帮助信息frommachineimportPin📍value–不带参数为获取状态,带参数为赋值📍off–赋值0📍on–赋... 查看详情
micropythonesp32读取dht11温湿度传感器数据(代码片段)
MicroPythonESP32读取DHT11温湿度传感器数据DHT11温湿度传感器接线说明ESP32-----DHT113.3V-----VCCGND-----GNDGPIO22-----DOUT本示例基于Thonny平台开发所引入的模块,如果没有按照,可以通过pipinstallxxxx命令在命令提示窗中进行安装,或... 查看详情
micropythonesp32超低功耗协处理器(ulp):睡眠模式示例详解(代码片段)
【MicroPythonESP32】硬件低功耗:睡眠模式详解✨本案例基于Thonny平台开发。✨📖esp32硬件低功耗知识介绍📗超低功耗协处理器(ULP)超低功耗协处理器(ULPCoprocessor)是一种功耗极低的协处理器设备,可在主系统级芯片(S... 查看详情
micropythonesp32手动入网配置调试过程(代码片段)
MicroPythonESP32手动入网配置调试过程本实验基于Thonny平台Shell调试窗口入网过程MicroPythonv1.19.1on2022-06-18;ESP32modulewithESP32Type"help()"formoreinformation.>>>importnetwork>>>wlan=network.WLAN 查看详情
micropythonesp32/8266ap模式下自定义gpio状态显示+主动按键控制(代码片段)
【Micropythonesp32/8266】AP模式下自定义GPIO状态显示+主动按键控制✨本示例基于Thonny开发。✨📍验证对象:esp8266,在esp32上需要修改其自定义GPIO引脚即可。🌻网页控制演示🎯功能说明⛳通过micropython设备... 查看详情
micropythonesp32利用中断控制电机正反转示例(代码片段)
【MicroPythonESP32】利用中断控制电机正反转示例✨本案例基于Thonny平台开发。✨📍相关篇《【MicroPythonESP32】外部中断使用示例》📌开发板采用的esp32Dev引脚功能图📝利用中断控制电机正反转控制示例importmachinesw1=ma... 查看详情
micropythonesp32cpu频率调整示例演示(代码片段)
MicroPythonESP32CPU频率调整示例演示本示例基于thonny平台。当运行的任务比较少,而且不是很紧急的任务时,可以主动降低esp32主频,从而来降低一部分功耗。esp32默认的主频是160MHz。示例演示代码frommachineimportfreq,Pinfromut... 查看详情
micropythonesp32读取esp32内部霍尔传感器数据(代码片段)
【MicroPythonESP32】读取esp32内部霍尔传感器数据本示例基于Thonny平台开发读取的霍尔传感器数据引入的模块importesp32#读取esp32内部霍尔传感器必须引入的模块fromtimeimportsleep#用于延时frommachineimportPin#引脚功能控制读取霍尔传感器数... 查看详情