drivers/sdcard: Port the SDCard driver to new machine API.
With backwards compatibility for pyboard.esp8266-idle-ticks
parent
49406b0ac6
commit
ce1c786297
|
@ -4,31 +4,41 @@ Micro Python driver for SD cards using SPI bus.
|
||||||
Requires an SPI bus and a CS pin. Provides readblocks and writeblocks
|
Requires an SPI bus and a CS pin. Provides readblocks and writeblocks
|
||||||
methods so the device can be mounted as a filesystem.
|
methods so the device can be mounted as a filesystem.
|
||||||
|
|
||||||
Example usage:
|
Example usage on pyboard:
|
||||||
|
|
||||||
import pyb, sdcard, os
|
import pyb, sdcard, os
|
||||||
sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
|
sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
|
||||||
pyb.mount(sd, '/sd2')
|
pyb.mount(sd, '/sd2')
|
||||||
os.listdir('/')
|
os.listdir('/')
|
||||||
|
|
||||||
|
Example usage on ESP8266:
|
||||||
|
|
||||||
|
import machine, sdcard, os
|
||||||
|
sd = sdcard.SDCard(machine.SPI(0), machine.Pin(15))
|
||||||
|
os.umount()
|
||||||
|
os.VfsFat(sd, "")
|
||||||
|
os.listdir()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pyb
|
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:
|
class SDCard:
|
||||||
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)
|
|
||||||
|
|
||||||
def __init__(self, spi, cs):
|
def __init__(self, spi, cs):
|
||||||
self.spi = spi
|
self.spi = spi
|
||||||
self.cs = cs
|
self.cs = cs
|
||||||
|
@ -42,30 +52,39 @@ class SDCard:
|
||||||
# initialise the card
|
# initialise the card
|
||||||
self.init_card()
|
self.init_card()
|
||||||
|
|
||||||
|
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):
|
def init_card(self):
|
||||||
# init CS pin
|
# init CS pin
|
||||||
self.cs.high()
|
self.cs.init(self.cs.OUT, value=1)
|
||||||
self.cs.init(self.cs.OUT_PP)
|
|
||||||
|
|
||||||
# init SPI bus; use low data rate for initialisation
|
# init SPI bus; use low data rate for initialisation
|
||||||
self.spi.init(self.spi.MASTER, baudrate=100000, phase=0, polarity=0)
|
self.init_spi(100000)
|
||||||
|
|
||||||
# clock card at least 100 cycles with cs high
|
# clock card at least 100 cycles with cs high
|
||||||
for i in range(16):
|
for i in range(16):
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
|
|
||||||
# CMD0: init card; should return R1_IDLE_STATE (allow 5 attempts)
|
# CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
if self.cmd(0, 0, 0x95) == R1_IDLE_STATE:
|
if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise OSError("no SD card")
|
raise OSError("no SD card")
|
||||||
|
|
||||||
# CMD8: determine card version
|
# CMD8: determine card version
|
||||||
r = self.cmd(8, 0x01aa, 0x87, 4)
|
r = self.cmd(8, 0x01aa, 0x87, 4)
|
||||||
if r == R1_IDLE_STATE:
|
if r == _R1_IDLE_STATE:
|
||||||
self.init_card_v2()
|
self.init_card_v2()
|
||||||
elif r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND):
|
elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
|
||||||
self.init_card_v1()
|
self.init_card_v1()
|
||||||
else:
|
else:
|
||||||
raise OSError("couldn't determine SD card version")
|
raise OSError("couldn't determine SD card version")
|
||||||
|
@ -86,10 +105,10 @@ class SDCard:
|
||||||
raise OSError("can't set 512 block size")
|
raise OSError("can't set 512 block size")
|
||||||
|
|
||||||
# set to high data rate now that it's initialised
|
# set to high data rate now that it's initialised
|
||||||
self.spi.init(self.spi.MASTER, baudrate=1320000, phase=0, polarity=0)
|
self.init_spi(1320000)
|
||||||
|
|
||||||
def init_card_v1(self):
|
def init_card_v1(self):
|
||||||
for i in range(CMD_TIMEOUT):
|
for i in range(_CMD_TIMEOUT):
|
||||||
self.cmd(55, 0, 0)
|
self.cmd(55, 0, 0)
|
||||||
if self.cmd(41, 0, 0) == 0:
|
if self.cmd(41, 0, 0) == 0:
|
||||||
self.cdv = 512
|
self.cdv = 512
|
||||||
|
@ -98,8 +117,8 @@ class SDCard:
|
||||||
raise OSError("timeout waiting for v1 card")
|
raise OSError("timeout waiting for v1 card")
|
||||||
|
|
||||||
def init_card_v2(self):
|
def init_card_v2(self):
|
||||||
for i in range(CMD_TIMEOUT):
|
for i in range(_CMD_TIMEOUT):
|
||||||
pyb.delay(50)
|
time.sleep_ms(50)
|
||||||
self.cmd(58, 0, 0, 4)
|
self.cmd(58, 0, 0, 4)
|
||||||
self.cmd(55, 0, 0)
|
self.cmd(55, 0, 0)
|
||||||
if self.cmd(41, 0x40000000, 0) == 0:
|
if self.cmd(41, 0x40000000, 0) == 0:
|
||||||
|
@ -120,87 +139,87 @@ class SDCard:
|
||||||
buf[3] = arg >> 8
|
buf[3] = arg >> 8
|
||||||
buf[4] = arg
|
buf[4] = arg
|
||||||
buf[5] = crc
|
buf[5] = crc
|
||||||
self.spi.send(buf)
|
self.spi.write(buf)
|
||||||
|
|
||||||
# wait for the repsonse (response[7] == 0)
|
# wait for the repsonse (response[7] == 0)
|
||||||
for i in range(CMD_TIMEOUT):
|
for i in range(_CMD_TIMEOUT):
|
||||||
response = self.spi.send_recv(0xff)[0]
|
response = self.spi.read(1, 0xff)[0]
|
||||||
if not (response & 0x80):
|
if not (response & 0x80):
|
||||||
# this could be a big-endian integer that we are getting here
|
# this could be a big-endian integer that we are getting here
|
||||||
for j in range(final):
|
for j in range(final):
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
if release:
|
if release:
|
||||||
self.cs.high()
|
self.cs.high()
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# timeout
|
# timeout
|
||||||
self.cs.high()
|
self.cs.high()
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def cmd_nodata(self, cmd):
|
def cmd_nodata(self, cmd):
|
||||||
self.spi.send(cmd)
|
self.spi.write(cmd)
|
||||||
self.spi.send_recv(0xff) # ignore stuff byte
|
self.spi.read(1, 0xff) # ignore stuff byte
|
||||||
for _ in range(CMD_TIMEOUT):
|
for _ in range(_CMD_TIMEOUT):
|
||||||
if self.spi.send_recv(0xff)[0] == 0xff:
|
if self.spi.read(1, 0xff)[0] == 0xff:
|
||||||
self.cs.high()
|
self.cs.high()
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
return 0 # OK
|
return 0 # OK
|
||||||
self.cs.high()
|
self.cs.high()
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
return 1 # timeout
|
return 1 # timeout
|
||||||
|
|
||||||
def readinto(self, buf):
|
def readinto(self, buf):
|
||||||
self.cs.low()
|
self.cs.low()
|
||||||
|
|
||||||
# read until start byte (0xff)
|
# read until start byte (0xff)
|
||||||
while self.spi.send_recv(0xff)[0] != 0xfe:
|
while self.spi.read(1, 0xff)[0] != 0xfe:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# read data
|
# read data
|
||||||
mv = self.dummybuf_memoryview[:len(buf)]
|
mv = self.dummybuf_memoryview[:len(buf)]
|
||||||
self.spi.send_recv(mv, recv=buf)
|
self.spi.write_readinto(mv, buf)
|
||||||
|
|
||||||
# read checksum
|
# read checksum
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
|
|
||||||
self.cs.high()
|
self.cs.high()
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
|
|
||||||
def write(self, token, buf):
|
def write(self, token, buf):
|
||||||
self.cs.low()
|
self.cs.low()
|
||||||
|
|
||||||
# send: start of block, data, checksum
|
# send: start of block, data, checksum
|
||||||
self.spi.send(token)
|
self.spi.read(1, token)
|
||||||
self.spi.send(buf)
|
self.spi.write(buf)
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
|
|
||||||
# check the response
|
# check the response
|
||||||
if (self.spi.send_recv(0xff)[0] & 0x1f) != 0x05:
|
if (self.spi.read(1, 0xff)[0] & 0x1f) != 0x05:
|
||||||
self.cs.high()
|
self.cs.high()
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
return
|
return
|
||||||
|
|
||||||
# wait for write to finish
|
# wait for write to finish
|
||||||
while self.spi.send_recv(0xff)[0] == 0:
|
while self.spi.read(1, 0xff)[0] == 0:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.cs.high()
|
self.cs.high()
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
|
|
||||||
def write_token(self, token):
|
def write_token(self, token):
|
||||||
self.cs.low()
|
self.cs.low()
|
||||||
self.spi.send(token)
|
self.spi.read(1, token)
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
# wait for write to finish
|
# wait for write to finish
|
||||||
while self.spi.send_recv(0xff)[0] == 0:
|
while self.spi.read(1, 0xff)[0] == 0x00:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.cs.high()
|
self.cs.high()
|
||||||
self.spi.send(0xff)
|
self.spi.write(b'\xff')
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
return self.sectors
|
return self.sectors
|
||||||
|
@ -224,7 +243,7 @@ class SDCard:
|
||||||
self.readinto(mv[offset : offset + 512])
|
self.readinto(mv[offset : offset + 512])
|
||||||
offset += 512
|
offset += 512
|
||||||
nblocks -= 1
|
nblocks -= 1
|
||||||
return self.cmd_nodata(12)
|
return self.cmd_nodata(b'\x0c') # cmd 12
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def writeblocks(self, block_num, buf):
|
def writeblocks(self, block_num, buf):
|
||||||
|
@ -236,7 +255,7 @@ class SDCard:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# send the data
|
# send the data
|
||||||
self.write(TOKEN_DATA, buf)
|
self.write(_TOKEN_DATA, buf)
|
||||||
else:
|
else:
|
||||||
# CMD25: set write address for first block
|
# CMD25: set write address for first block
|
||||||
if self.cmd(25, block_num * self.cdv, 0) != 0:
|
if self.cmd(25, block_num * self.cdv, 0) != 0:
|
||||||
|
@ -245,8 +264,8 @@ class SDCard:
|
||||||
offset = 0
|
offset = 0
|
||||||
mv = memoryview(buf)
|
mv = memoryview(buf)
|
||||||
while nblocks:
|
while nblocks:
|
||||||
self.write(TOKEN_CMD25, mv[offset : offset + 512])
|
self.write(_TOKEN_CMD25, mv[offset : offset + 512])
|
||||||
offset += 512
|
offset += 512
|
||||||
nblocks -= 1
|
nblocks -= 1
|
||||||
self.write_token(TOKEN_STOP_TRAN)
|
self.write_token(_TOKEN_STOP_TRAN)
|
||||||
return 0
|
return 0
|
||||||
|
|
Loading…
Reference in New Issue