Skip to content

Commit 2bd758f

Browse files
peterhinchdpgeorge
authored andcommitted
drivers/sdcard: Add support for multi-block read/write; add SD test.
1 parent 67e8108 commit 2bd758f

File tree

2 files changed

+125
-20
lines changed

2 files changed

+125
-20
lines changed

drivers/sdcard/sdcard.py

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class SDCard:
2525
#R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
2626
#R1_ADDRESS_ERROR = const(1 << 5)
2727
#R1_PARAMETER_ERROR = const(1 << 6)
28+
TOKEN_CMD25 = const(0xfc)
29+
TOKEN_STOP_TRAN = const(0xfd)
30+
TOKEN_DATA = const(0xfe)
2831

2932
def __init__(self, spi, cs):
3033
self.spi = spi
@@ -136,6 +139,18 @@ def cmd(self, cmd, arg, crc, final=0, release=True):
136139
self.spi.send(0xff)
137140
return -1
138141

142+
def cmd_nodata(self, cmd):
143+
self.spi.send(cmd)
144+
self.spi.send_recv(0xff) # ignore stuff byte
145+
for _ in range(CMD_TIMEOUT):
146+
if self.spi.send_recv(0xff)[0] == 0xff:
147+
self.cs.high()
148+
self.spi.send(0xff)
149+
return 0 # OK
150+
self.cs.high()
151+
self.spi.send(0xff)
152+
return 1 # timeout
153+
139154
def readinto(self, buf):
140155
self.cs.low()
141156

@@ -154,11 +169,11 @@ def readinto(self, buf):
154169
self.cs.high()
155170
self.spi.send(0xff)
156171

157-
def write(self, buf):
172+
def write(self, token, buf):
158173
self.cs.low()
159174

160175
# send: start of block, data, checksum
161-
self.spi.send(0xfe)
176+
self.spi.send(token)
162177
self.spi.send(buf)
163178
self.spi.send(0xff)
164179
self.spi.send(0xff)
@@ -176,29 +191,62 @@ def write(self, buf):
176191
self.cs.high()
177192
self.spi.send(0xff)
178193

194+
def write_token(self, token):
195+
self.cs.low()
196+
self.spi.send(token)
197+
self.spi.send(0xff)
198+
# wait for write to finish
199+
while self.spi.send_recv(0xff)[0] == 0:
200+
pass
201+
202+
self.cs.high()
203+
self.spi.send(0xff)
204+
179205
def count(self):
180206
return self.sectors
181207

182208
def readblocks(self, block_num, buf):
183-
# TODO support multiple block reads
184-
assert len(buf) == 512
185-
186-
# CMD17: set read address for single block
187-
if self.cmd(17, block_num * self.cdv, 0) != 0:
188-
return 1
189-
190-
# receive the data
191-
self.readinto(buf)
209+
nblocks, err = divmod(len(buf), 512)
210+
assert nblocks and not err, 'Buffer length is invalid'
211+
if nblocks == 1:
212+
# CMD17: set read address for single block
213+
if self.cmd(17, block_num * self.cdv, 0) != 0:
214+
return 1
215+
# receive the data
216+
self.readinto(buf)
217+
else:
218+
# CMD18: set read address for multiple blocks
219+
if self.cmd(18, block_num * self.cdv, 0) != 0:
220+
return 1
221+
offset = 0
222+
mv = memoryview(buf)
223+
while nblocks:
224+
self.readinto(mv[offset : offset + 512])
225+
offset += 512
226+
nblocks -= 1
227+
return self.cmd_nodata(12)
192228
return 0
193229

194230
def writeblocks(self, block_num, buf):
195-
# TODO support multiple block writes
196-
assert len(buf) == 512
197-
198-
# CMD24: set write address for single block
199-
if self.cmd(24, block_num * self.cdv, 0) != 0:
200-
return 1
201-
202-
# send the data
203-
self.write(buf)
231+
nblocks, err = divmod(len(buf), 512)
232+
assert nblocks and not err, 'Buffer length is invalid'
233+
if nblocks == 1:
234+
# CMD24: set write address for single block
235+
if self.cmd(24, block_num * self.cdv, 0) != 0:
236+
return 1
237+
238+
# send the data
239+
self.write(TOKEN_DATA, buf)
240+
else:
241+
# CMD25: set write address for first block
242+
if self.cmd(25, block_num * self.cdv, 0) != 0:
243+
return 1
244+
# send the data
245+
offset = 0
246+
mv = memoryview(buf)
247+
while nblocks:
248+
self.write(TOKEN_CMD25, mv[offset : offset + 512])
249+
offset += 512
250+
nblocks -= 1
251+
self.write_token(TOKEN_STOP_TRAN)
204252
return 0

drivers/sdcard/sdtest.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Test for sdcard block protocol
2+
# Peter hinch 30th Jan 2016
3+
import os, sdcard, pyb
4+
5+
def sdtest():
6+
sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X21) # Compatible with PCB
7+
pyb.mount(sd, '/fc')
8+
print('Filesystem check')
9+
print(os.listdir('/fc'))
10+
11+
line = 'abcdefghijklmnopqrstuvwxyz\n'
12+
lines = line * 200 # 5400 chars
13+
short = '1234567890\n'
14+
15+
fn = '/fc/rats.txt'
16+
print()
17+
print('Multiple block read/write')
18+
with open(fn,'w') as f:
19+
n = f.write(lines)
20+
print(n, 'bytes written')
21+
n = f.write(short)
22+
print(n, 'bytes written')
23+
n = f.write(lines)
24+
print(n, 'bytes written')
25+
26+
with open(fn,'r') as f:
27+
result1 = f.read()
28+
print(len(result1), 'bytes read')
29+
30+
fn = '/fc/rats1.txt'
31+
print()
32+
print('Single block read/write')
33+
with open(fn,'w') as f:
34+
n = f.write(short) # one block
35+
print(n, 'bytes written')
36+
37+
with open(fn,'r') as f:
38+
result2 = f.read()
39+
print(len(result2), 'bytes read')
40+
41+
pyb.mount(None, '/fc')
42+
43+
print()
44+
print('Verifying data read back')
45+
success = True
46+
if result1 == ''.join((lines, short, lines)):
47+
print('Large file Pass')
48+
else:
49+
print('Large file Fail')
50+
success = False
51+
if result2 == short:
52+
print('Small file Pass')
53+
else:
54+
print('Small file Fail')
55+
success = False
56+
print()
57+
print('Tests', 'passed' if success else 'failed')

0 commit comments

Comments
 (0)