Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions micropython/drivers/storage/sdcard/sdcard.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@
_TOKEN_DATA = const(0xFE)


def _crc7(buf, n):
crc = 0
for i in range(n):
crc ^= buf[i]
for j in range(8):
crc = ((crc << 1) ^ (0x12 * (crc >> 7))) & 0xFF
return crc | 1


class SDCard:
def __init__(self, spi, cs, baudrate=1320000):
self.spi = spi
Expand Down Expand Up @@ -76,13 +85,13 @@ def init_card(self, baudrate):

# CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
for _ in range(5):
if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
if self.cmd(0, 0) == _R1_IDLE_STATE:
break
else:
raise OSError("no SD card")

# CMD8: determine card version
r = self.cmd(8, 0x01AA, 0x87, 4)
r = self.cmd(8, 0x01AA, 4)
if r == _R1_IDLE_STATE:
self.init_card_v2()
elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
Expand All @@ -92,7 +101,7 @@ def init_card(self, baudrate):

# get the number of sectors
# CMD9: response R2 (R1 byte + 16-byte block read)
if self.cmd(9, 0, 0, 0, False) != 0:
if self.cmd(9, 0, 0, False) != 0:
raise OSError("no response from SD card")
csd = bytearray(16)
self.readinto(csd)
Expand All @@ -109,7 +118,7 @@ def init_card(self, baudrate):
# print('sectors', self.sectors)

# CMD16: set block length to 512 bytes
if self.cmd(16, 512, 0) != 0:
if self.cmd(16, 512) != 0:
raise OSError("can't set 512 block size")

# set to high data rate now that it's initialised
Expand All @@ -118,8 +127,8 @@ def init_card(self, baudrate):
def init_card_v1(self):
for i in range(_CMD_TIMEOUT):
time.sleep_ms(50)
self.cmd(55, 0, 0)
if self.cmd(41, 0, 0) == 0:
self.cmd(55, 0)
if self.cmd(41, 0) == 0:
# SDSC card, uses byte addressing in read/write/erase commands
self.cdv = 512
# print("[SDCard] v1 card")
Expand All @@ -129,10 +138,10 @@ def init_card_v1(self):
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
self.cmd(58, 0, 4)
self.cmd(55, 0)
if self.cmd(41, 0x40000000) == 0:
self.cmd(58, 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
Expand All @@ -144,7 +153,7 @@ def init_card_v2(self):
return
raise OSError("timeout waiting for v2 card")

def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
def cmd(self, cmd, arg, final=0, release=True, skip1=False):
self.cs(0)

# create and send the command
Expand All @@ -154,7 +163,7 @@ def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
buf[2] = arg >> 16
buf[3] = arg >> 8
buf[4] = arg
buf[5] = crc
buf[5] = _crc7(buf, 5)
self.spi.write(buf)

if skip1:
Expand Down Expand Up @@ -250,15 +259,15 @@ def readblocks(self, block_num, buf):
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:
if self.cmd(17, block_num * self.cdv, 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:
if self.cmd(18, block_num * self.cdv, release=False) != 0:
# release the card
self.cs(1)
raise OSError(5) # EIO
Expand All @@ -269,7 +278,7 @@ def readblocks(self, block_num, buf):
self.readinto(mv[offset : offset + 512])
offset += 512
nblocks -= 1
if self.cmd(12, 0, 0xFF, skip1=True):
if self.cmd(12, 0, skip1=True):
raise OSError(5) # EIO

def writeblocks(self, block_num, buf):
Expand All @@ -281,14 +290,14 @@ def writeblocks(self, block_num, buf):
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:
if self.cmd(24, block_num * self.cdv) != 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:
if self.cmd(25, block_num * self.cdv) != 0:
raise OSError(5) # EIO
# send the data
offset = 0
Expand Down
21 changes: 21 additions & 0 deletions micropython/drivers/storage/sdcard/sdtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Peter hinch 30th Jan 2016
import machine
import os
import time
import sdcard


Expand Down Expand Up @@ -44,6 +45,26 @@ def sdtest():
result2 = f.read()
print(len(result2), "bytes read")

fn = "/fc/speed.bin"
buf = bytearray(32768) # 32 KB buffer

print()
print("Write speed test")
t = time.ticks_ms()
with open(fn, "wb") as f:
for _ in range(32): # 1 MB total
f.write(buf)
elapsed = time.ticks_diff(time.ticks_ms(), t)
print("{} KB/s".format(32768 * 32 // elapsed))

print("Read speed test")
t = time.ticks_ms()
with open(fn, "rb") as f:
while f.readinto(buf):
pass
elapsed = time.ticks_diff(time.ticks_ms(), t)
print("{} KB/s".format(32768 * 32 // elapsed))

os.umount("/fc")

print()
Expand Down
Loading