Skip to content

Commit 1f9323a

Browse files
committed
sdcard: Compute CRC7 for all SPI commands.
Some SD cards (e.g. SDXC) have CRC checking permanently enabled in SPI mode. Sending commands with CRC=0x00 causes them to return ILLEGAL_COMMAND, making initialisation fail. Fix this by computing a valid CRC7 for every command using a viper function. Signed-off-by: Kwabena W. Agyeman <kwagyeman@live.com>
1 parent 6ae440a commit 1f9323a

File tree

1 file changed

+29
-17
lines changed

1 file changed

+29
-17
lines changed

micropython/drivers/storage/sdcard/sdcard.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@
3838
_TOKEN_DATA = const(0xFE)
3939

4040

41+
@micropython.viper
42+
def _crc7(buf, n: int) -> int:
43+
"""CRC7 with end bit for SD card SPI command frames (polynomial x^7+x^3+1)."""
44+
d = ptr8(buf)
45+
crc = 0
46+
for i in range(n):
47+
crc ^= d[i]
48+
for j in range(8):
49+
crc = ((crc << 1) ^ (0x12 * (crc >> 7))) & 0xFF
50+
return crc | 1
51+
52+
4153
class SDCard:
4254
def __init__(self, spi, cs, baudrate=1320000):
4355
self.spi = spi
@@ -76,13 +88,13 @@ def init_card(self, baudrate):
7688

7789
# CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
7890
for _ in range(5):
79-
if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
91+
if self.cmd(0, 0) == _R1_IDLE_STATE:
8092
break
8193
else:
8294
raise OSError("no SD card")
8395

8496
# CMD8: determine card version
85-
r = self.cmd(8, 0x01AA, 0x87, 4)
97+
r = self.cmd(8, 0x01AA, 4)
8698
if r == _R1_IDLE_STATE:
8799
self.init_card_v2()
88100
elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
@@ -92,7 +104,7 @@ def init_card(self, baudrate):
92104

93105
# get the number of sectors
94106
# CMD9: response R2 (R1 byte + 16-byte block read)
95-
if self.cmd(9, 0, 0, 0, False) != 0:
107+
if self.cmd(9, 0, 0, False) != 0:
96108
raise OSError("no response from SD card")
97109
csd = bytearray(16)
98110
self.readinto(csd)
@@ -109,7 +121,7 @@ def init_card(self, baudrate):
109121
# print('sectors', self.sectors)
110122

111123
# CMD16: set block length to 512 bytes
112-
if self.cmd(16, 512, 0) != 0:
124+
if self.cmd(16, 512) != 0:
113125
raise OSError("can't set 512 block size")
114126

115127
# set to high data rate now that it's initialised
@@ -118,8 +130,8 @@ def init_card(self, baudrate):
118130
def init_card_v1(self):
119131
for i in range(_CMD_TIMEOUT):
120132
time.sleep_ms(50)
121-
self.cmd(55, 0, 0)
122-
if self.cmd(41, 0, 0) == 0:
133+
self.cmd(55, 0)
134+
if self.cmd(41, 0) == 0:
123135
# SDSC card, uses byte addressing in read/write/erase commands
124136
self.cdv = 512
125137
# print("[SDCard] v1 card")
@@ -129,10 +141,10 @@ def init_card_v1(self):
129141
def init_card_v2(self):
130142
for i in range(_CMD_TIMEOUT):
131143
time.sleep_ms(50)
132-
self.cmd(58, 0, 0, 4)
133-
self.cmd(55, 0, 0)
134-
if self.cmd(41, 0x40000000, 0) == 0:
135-
self.cmd(58, 0, 0, -4) # 4-byte response, negative means keep the first byte
144+
self.cmd(58, 0, 4)
145+
self.cmd(55, 0)
146+
if self.cmd(41, 0x40000000) == 0:
147+
self.cmd(58, 0, -4) # 4-byte response, negative means keep the first byte
136148
ocr = self.tokenbuf[0] # get first byte of response, which is OCR
137149
if not ocr & 0x40:
138150
# SDSC card, uses byte addressing in read/write/erase commands
@@ -144,7 +156,7 @@ def init_card_v2(self):
144156
return
145157
raise OSError("timeout waiting for v2 card")
146158

147-
def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
159+
def cmd(self, cmd, arg, final=0, release=True, skip1=False):
148160
self.cs(0)
149161

150162
# create and send the command
@@ -154,7 +166,7 @@ def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
154166
buf[2] = arg >> 16
155167
buf[3] = arg >> 8
156168
buf[4] = arg
157-
buf[5] = crc
169+
buf[5] = _crc7(buf, 5)
158170
self.spi.write(buf)
159171

160172
if skip1:
@@ -250,15 +262,15 @@ def readblocks(self, block_num, buf):
250262
assert nblocks and not len(buf) % 512, "Buffer length is invalid"
251263
if nblocks == 1:
252264
# CMD17: set read address for single block
253-
if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
265+
if self.cmd(17, block_num * self.cdv, release=False) != 0:
254266
# release the card
255267
self.cs(1)
256268
raise OSError(5) # EIO
257269
# receive the data and release card
258270
self.readinto(buf)
259271
else:
260272
# CMD18: set read address for multiple blocks
261-
if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
273+
if self.cmd(18, block_num * self.cdv, release=False) != 0:
262274
# release the card
263275
self.cs(1)
264276
raise OSError(5) # EIO
@@ -269,7 +281,7 @@ def readblocks(self, block_num, buf):
269281
self.readinto(mv[offset : offset + 512])
270282
offset += 512
271283
nblocks -= 1
272-
if self.cmd(12, 0, 0xFF, skip1=True):
284+
if self.cmd(12, 0, skip1=True):
273285
raise OSError(5) # EIO
274286

275287
def writeblocks(self, block_num, buf):
@@ -281,14 +293,14 @@ def writeblocks(self, block_num, buf):
281293
assert nblocks and not err, "Buffer length is invalid"
282294
if nblocks == 1:
283295
# CMD24: set write address for single block
284-
if self.cmd(24, block_num * self.cdv, 0) != 0:
296+
if self.cmd(24, block_num * self.cdv) != 0:
285297
raise OSError(5) # EIO
286298

287299
# send the data
288300
self.write(_TOKEN_DATA, buf)
289301
else:
290302
# CMD25: set write address for first block
291-
if self.cmd(25, block_num * self.cdv, 0) != 0:
303+
if self.cmd(25, block_num * self.cdv) != 0:
292304
raise OSError(5) # EIO
293305
# send the data
294306
offset = 0

0 commit comments

Comments
 (0)