Skip to content

Commit 2808a9c

Browse files
committed
Sync with prototype device filesystem
1 parent 73de790 commit 2808a9c

File tree

9 files changed

+351
-5
lines changed

9 files changed

+351
-5
lines changed

App/assets/nosd.pi

232 Bytes
Binary file not shown.

App/assets/unused/Bold.2f

699 Bytes
Binary file not shown.
1.04 KB
Binary file not shown.
1.08 KB
Binary file not shown.

App/assets/unused/Tiny.2f

292 Bytes
Binary file not shown.

App/assets/unused/Vermin.2f

1.12 KB
Binary file not shown.

App/playscreen.2ink

21.1 KB
Binary file not shown.

App/tests/sdcard.py

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
"""
2+
MicroPython driver for SD cards using SPI bus.
3+
4+
Requires an SPI bus and a CS pin. Provides readblocks and writeblocks
5+
methods so the device can be mounted as a filesystem.
6+
7+
Example usage on pyboard:
8+
9+
import pyb, sdcard, os
10+
sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
11+
pyb.mount(sd, '/sd2')
12+
os.listdir('/')
13+
14+
Example usage on ESP8266:
15+
16+
import machine, sdcard, os
17+
sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))
18+
os.mount(sd, '/sd')
19+
os.listdir('/')
20+
21+
"""
22+
23+
from micropython import const
24+
import time
25+
26+
27+
_CMD_TIMEOUT = const(100)
28+
29+
_R1_IDLE_STATE = const(1 << 0)
30+
# R1_ERASE_RESET = const(1 << 1)
31+
_R1_ILLEGAL_COMMAND = const(1 << 2)
32+
# R1_COM_CRC_ERROR = const(1 << 3)
33+
# R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
34+
# R1_ADDRESS_ERROR = const(1 << 5)
35+
# R1_PARAMETER_ERROR = const(1 << 6)
36+
_TOKEN_CMD25 = const(0xFC)
37+
_TOKEN_STOP_TRAN = const(0xFD)
38+
_TOKEN_DATA = const(0xFE)
39+
40+
41+
class SDCard:
42+
def __init__(self, spi, cs, baudrate=1320000):
43+
self.spi = spi
44+
self.cs = cs
45+
46+
self.cmdbuf = bytearray(6)
47+
self.dummybuf = bytearray(512)
48+
self.tokenbuf = bytearray(1)
49+
for i in range(512):
50+
self.dummybuf[i] = 0xFF
51+
self.dummybuf_memoryview = memoryview(self.dummybuf)
52+
53+
# initialise the card
54+
self.init_card(baudrate)
55+
56+
def init_spi(self, baudrate):
57+
try:
58+
master = self.spi.MASTER
59+
except AttributeError:
60+
# on ESP8266
61+
self.spi.init(baudrate=baudrate, phase=0, polarity=0)
62+
else:
63+
# on pyboard
64+
self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
65+
66+
def init_card(self, baudrate):
67+
# init CS pin
68+
self.cs.init(self.cs.OUT, value=1)
69+
70+
# init SPI bus; use low data rate for initialisation
71+
self.init_spi(100000)
72+
73+
# clock card at least 100 cycles with cs high
74+
for i in range(16):
75+
self.spi.write(b"\xff")
76+
77+
# CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
78+
for _ in range(5):
79+
if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
80+
break
81+
else:
82+
raise OSError("no SD card")
83+
84+
# CMD8: determine card version
85+
r = self.cmd(8, 0x01AA, 0x87, 4)
86+
if r == _R1_IDLE_STATE:
87+
self.init_card_v2()
88+
elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
89+
self.init_card_v1()
90+
else:
91+
raise OSError("couldn't determine SD card version")
92+
93+
# get the number of sectors
94+
# CMD9: response R2 (R1 byte + 16-byte block read)
95+
if self.cmd(9, 0, 0, 0, False) != 0:
96+
raise OSError("no response from SD card")
97+
csd = bytearray(16)
98+
self.readinto(csd)
99+
if csd[0] & 0xC0 == 0x40: # CSD version 2.0
100+
self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
101+
elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB)
102+
c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6
103+
c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7
104+
read_bl_len = csd[5] & 0b1111
105+
capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len)
106+
self.sectors = capacity // 512
107+
else:
108+
raise OSError("SD card CSD format not supported")
109+
# print('sectors', self.sectors)
110+
111+
# CMD16: set block length to 512 bytes
112+
if self.cmd(16, 512, 0) != 0:
113+
raise OSError("can't set 512 block size")
114+
115+
# set to high data rate now that it's initialised
116+
self.init_spi(baudrate)
117+
118+
def init_card_v1(self):
119+
for i in range(_CMD_TIMEOUT):
120+
time.sleep_ms(50)
121+
self.cmd(55, 0, 0)
122+
if self.cmd(41, 0, 0) == 0:
123+
# SDSC card, uses byte addressing in read/write/erase commands
124+
self.cdv = 512
125+
# print("[SDCard] v1 card")
126+
return
127+
raise OSError("timeout waiting for v1 card")
128+
129+
def init_card_v2(self):
130+
for i in range(_CMD_TIMEOUT):
131+
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
136+
ocr = self.tokenbuf[0] # get first byte of response, which is OCR
137+
if not ocr & 0x40:
138+
# SDSC card, uses byte addressing in read/write/erase commands
139+
self.cdv = 512
140+
else:
141+
# SDHC/SDXC card, uses block addressing in read/write/erase commands
142+
self.cdv = 1
143+
# print("[SDCard] v2 card")
144+
return
145+
raise OSError("timeout waiting for v2 card")
146+
147+
def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
148+
self.cs(0)
149+
150+
# create and send the command
151+
buf = self.cmdbuf
152+
buf[0] = 0x40 | cmd
153+
buf[1] = arg >> 24
154+
buf[2] = arg >> 16
155+
buf[3] = arg >> 8
156+
buf[4] = arg
157+
buf[5] = crc
158+
self.spi.write(buf)
159+
160+
if skip1:
161+
self.spi.readinto(self.tokenbuf, 0xFF)
162+
163+
# wait for the response (response[7] == 0)
164+
for i in range(_CMD_TIMEOUT):
165+
self.spi.readinto(self.tokenbuf, 0xFF)
166+
response = self.tokenbuf[0]
167+
if not (response & 0x80):
168+
# this could be a big-endian integer that we are getting here
169+
# if final<0 then store the first byte to tokenbuf and discard the rest
170+
if final < 0:
171+
self.spi.readinto(self.tokenbuf, 0xFF)
172+
final = -1 - final
173+
for j in range(final):
174+
self.spi.write(b"\xff")
175+
if release:
176+
self.cs(1)
177+
self.spi.write(b"\xff")
178+
return response
179+
180+
# timeout
181+
self.cs(1)
182+
self.spi.write(b"\xff")
183+
return -1
184+
185+
def readinto(self, buf):
186+
self.cs(0)
187+
188+
# read until start byte (0xff)
189+
for i in range(_CMD_TIMEOUT):
190+
self.spi.readinto(self.tokenbuf, 0xFF)
191+
if self.tokenbuf[0] == _TOKEN_DATA:
192+
break
193+
time.sleep_ms(1)
194+
else:
195+
self.cs(1)
196+
raise OSError("timeout waiting for response")
197+
198+
# read data
199+
mv = self.dummybuf_memoryview
200+
if len(buf) != len(mv):
201+
mv = mv[: len(buf)]
202+
self.spi.write_readinto(mv, buf)
203+
204+
# read checksum
205+
self.spi.write(b"\xff")
206+
self.spi.write(b"\xff")
207+
208+
self.cs(1)
209+
self.spi.write(b"\xff")
210+
211+
def write(self, token, buf):
212+
self.cs(0)
213+
214+
# send: start of block, data, checksum
215+
self.spi.read(1, token)
216+
self.spi.write(buf)
217+
self.spi.write(b"\xff")
218+
self.spi.write(b"\xff")
219+
220+
# check the response
221+
if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:
222+
self.cs(1)
223+
self.spi.write(b"\xff")
224+
return
225+
226+
# wait for write to finish
227+
while self.spi.read(1, 0xFF)[0] == 0:
228+
pass
229+
230+
self.cs(1)
231+
self.spi.write(b"\xff")
232+
233+
def write_token(self, token):
234+
self.cs(0)
235+
self.spi.read(1, token)
236+
self.spi.write(b"\xff")
237+
# wait for write to finish
238+
while self.spi.read(1, 0xFF)[0] == 0x00:
239+
pass
240+
241+
self.cs(1)
242+
self.spi.write(b"\xff")
243+
244+
def readblocks(self, block_num, buf):
245+
# workaround for shared bus, required for (at least) some Kingston
246+
# devices, ensure MOSI is high before starting transaction
247+
self.spi.write(b"\xff")
248+
249+
nblocks = len(buf) // 512
250+
assert nblocks and not len(buf) % 512, "Buffer length is invalid"
251+
if nblocks == 1:
252+
# CMD17: set read address for single block
253+
if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
254+
# release the card
255+
self.cs(1)
256+
raise OSError(5) # EIO
257+
# receive the data and release card
258+
self.readinto(buf)
259+
else:
260+
# CMD18: set read address for multiple blocks
261+
if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
262+
# release the card
263+
self.cs(1)
264+
raise OSError(5) # EIO
265+
offset = 0
266+
mv = memoryview(buf)
267+
while nblocks:
268+
# receive the data and release card
269+
self.readinto(mv[offset : offset + 512])
270+
offset += 512
271+
nblocks -= 1
272+
if self.cmd(12, 0, 0xFF, skip1=True):
273+
raise OSError(5) # EIO
274+
275+
def writeblocks(self, block_num, buf):
276+
# workaround for shared bus, required for (at least) some Kingston
277+
# devices, ensure MOSI is high before starting transaction
278+
self.spi.write(b"\xff")
279+
280+
nblocks, err = divmod(len(buf), 512)
281+
assert nblocks and not err, "Buffer length is invalid"
282+
if nblocks == 1:
283+
# CMD24: set write address for single block
284+
if self.cmd(24, block_num * self.cdv, 0) != 0:
285+
raise OSError(5) # EIO
286+
287+
# send the data
288+
self.write(_TOKEN_DATA, buf)
289+
else:
290+
# CMD25: set write address for first block
291+
if self.cmd(25, block_num * self.cdv, 0) != 0:
292+
raise OSError(5) # EIO
293+
# send the data
294+
offset = 0
295+
mv = memoryview(buf)
296+
while nblocks:
297+
self.write(_TOKEN_CMD25, mv[offset : offset + 512])
298+
offset += 512
299+
nblocks -= 1
300+
self.write_token(_TOKEN_STOP_TRAN)
301+
302+
def ioctl(self, op, arg):
303+
if op == 4: # get number of blocks
304+
return self.sectors
305+
if op == 5: # get block size in bytes
306+
return 512

App/tests/sdtest.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
import os
55
import sdcard
66

7-
8-
def sdtest():
9-
spi = machine.SPI(1, sck=machine.Pin(14), mosi=machine.Pin(15), miso=machine.Pin(12), baudrate=400000, polarity=0, phase=0)
7+
def getspi():
8+
spi = machine.SPI(0, sck=machine.Pin(18), mosi=machine.Pin(19), miso=machine.Pin(16), baudrate=400000, polarity=0, phase=0)
109
spi.init() # Ensure right baudrate
11-
sd = sdcard.SDCard(spi, machine.Pin(18)) # Compatible with PCB
10+
return spi
11+
12+
def sdtest(spi):
13+
#spi = machine.SPI(0, sck=machine.Pin(18), mosi=machine.Pin(19), miso=machine.Pin(16), baudrate=400000, polarity=0, phase=0)
14+
#spi.init() # Ensure right baudrate
15+
sd = sdcard.SDCard( spi=spi, cs=machine.Pin(2), baudrate=4000000) # Compatible with PCB
1216

1317
print( sd.ioctl(4,0) )
1418
print( sd.ioctl(5,0) )
@@ -17,6 +21,8 @@ def sdtest():
1721
os.mount(vfs, "/fc")
1822
print("Filesystem check")
1923
print(os.listdir("/fc"))
24+
25+
return
2026

2127
line = "abcdefghijklmnopqrstuvwxyz\n"
2228
lines = line * 200 # 5400 chars
@@ -66,4 +72,38 @@ def sdtest():
6672
print()
6773
print("Tests", "passed" if success else "failed")
6874

69-
sdtest()
75+
def discon_test(spi):
76+
import time
77+
78+
sd = sdcard.SDCard( spi=spi, cs=machine.Pin(2), baudrate=4000000)
79+
80+
print( sd.ioctl(4,0) )
81+
print( sd.ioctl(5,0) )
82+
83+
#print(sd.cmd(0, 0, 0x95))
84+
85+
vfs = os.VfsFat(sd)
86+
os.mount(vfs, "/fc")
87+
print("Filesystem check")
88+
print(os.listdir("/fc"))
89+
print(os.listdir("/fc/non-existanbt"))
90+
91+
time.sleep(5)
92+
93+
print('waking...')
94+
95+
os.umount(vfs)
96+
print(os.listdir("/fc"))
97+
98+
#print(sd.cmd(0, 0, 0x95))
99+
100+
101+
#print( sd.ioctl(4,0) )
102+
#print( sd.ioctl(5,0) )
103+
#print("Filesystem check")
104+
#print(os.listdir("/fc"))
105+
106+
107+
spi = getspi()
108+
discon_test(spi)
109+
#sdtest(spi)

0 commit comments

Comments
 (0)