Skip to content

Commit 1570eaf

Browse files
committed
drivers: Add SD card driver, controlled via SPI bus.
1 parent 7690b13 commit 1570eaf

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

drivers/sdcard/sdcard.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
"""
2+
Micro Python 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:
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+
"""
15+
16+
import pyb
17+
18+
class SDCard:
19+
CMD_TIMEOUT = const(100)
20+
21+
R1_IDLE_STATE = const(1 << 0)
22+
#R1_ERASE_RESET = const(1 << 1)
23+
R1_ILLEGAL_COMMAND = const(1 << 2)
24+
#R1_COM_CRC_ERROR = const(1 << 3)
25+
#R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
26+
#R1_ADDRESS_ERROR = const(1 << 5)
27+
#R1_PARAMETER_ERROR = const(1 << 6)
28+
29+
def __init__(self, spi, cs):
30+
self.spi = spi
31+
self.cs = cs
32+
33+
self.cmdbuf = bytearray(6)
34+
self.dummybuf = bytearray(512)
35+
for i in range(512):
36+
self.dummybuf[i] = 0xff
37+
self.dummybuf_memoryview = memoryview(self.dummybuf)
38+
39+
# initialise the card
40+
self.init_card()
41+
42+
def init_card(self):
43+
# init CS pin
44+
self.cs.high()
45+
self.cs.init(self.cs.OUT_PP)
46+
47+
# init SPI bus; use low data rate for initialisation
48+
self.spi.init(self.spi.MASTER, baudrate=100000, phase=0, polarity=0)
49+
50+
# clock card at least 100 cycles with cs high
51+
for i in range(16):
52+
self.spi.send(0xff)
53+
54+
# CMD0: init card; should return R1_IDLE_STATE (allow 2 attempts)
55+
if self.cmd(0, 0, 0x95) != R1_IDLE_STATE:
56+
if self.cmd(0, 0, 0x95) != R1_IDLE_STATE:
57+
raise OSError("no SD card")
58+
59+
# CMD8: determine card version
60+
r = self.cmd(8, 0x01aa, 0x87, 4)
61+
if r == R1_IDLE_STATE:
62+
self.init_card_v2()
63+
elif r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND):
64+
self.init_card_v1()
65+
else:
66+
raise OSError("couldn't determine SD card version")
67+
68+
# get the number of sectors
69+
# CMD9: response R2 (R1 byte + 16-byte block read)
70+
if self.cmd(9, 0, 0, 0, False) != 0:
71+
raise OSError("no response from SD card")
72+
csd = bytearray(16)
73+
self.readinto(csd)
74+
if csd[0] & 0xc0 != 0x40:
75+
raise OSError("SD card CSD format not supported")
76+
self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 2014
77+
#print('sectors', self.sectors)
78+
79+
# CMD16: set block length to 512 bytes
80+
if self.cmd(16, 512, 0) != 0:
81+
raise OSError("can't set 512 block size")
82+
83+
# set to high data rate now that it's initialised
84+
self.spi.init(self.spi.MASTER, baudrate=1320000, phase=0, polarity=0)
85+
86+
def init_card_v1(self):
87+
for i in range(CMD_TIMEOUT):
88+
self.cmd(55, 0, 0)
89+
if self.cmd(41, 0, 0) == 0:
90+
self.cdv = 512
91+
#print("[SDCard] v1 card")
92+
return
93+
raise OSError("timeout waiting for v1 card")
94+
95+
def init_card_v2(self):
96+
for i in range(CMD_TIMEOUT):
97+
pyb.delay(50)
98+
self.cmd(58, 0, 0, 4)
99+
self.cmd(55, 0, 0)
100+
if self.cmd(41, 0x40000000, 0) == 0:
101+
self.cmd(58, 0, 0, 4)
102+
self.cdv = 1
103+
#print("[SDCard] v2 card")
104+
return
105+
raise OSError("timeout waiting for v2 card")
106+
107+
def cmd(self, cmd, arg, crc, final=0, release=True):
108+
self.cs.low()
109+
110+
# create and send the command
111+
buf = self.cmdbuf
112+
buf[0] = 0x40 | cmd
113+
buf[1] = arg >> 24
114+
buf[2] = arg >> 16
115+
buf[3] = arg >> 8
116+
buf[4] = arg
117+
buf[5] = crc
118+
self.spi.send(buf)
119+
120+
# wait for the repsonse (response[7] == 0)
121+
for i in range(CMD_TIMEOUT):
122+
response = self.spi.send_recv(0xff)[0]
123+
if not (response & 0x80):
124+
# this could be a big-endian integer that we are getting here
125+
for j in range(final):
126+
self.spi.send(0xff)
127+
if release:
128+
self.cs.high()
129+
self.spi.send(0xff)
130+
return response
131+
132+
# timeout
133+
self.cs.high()
134+
self.spi.send(0xff)
135+
return -1
136+
137+
def readinto(self, buf):
138+
self.cs.low()
139+
140+
# read until start byte (0xff)
141+
while self.spi.send_recv(0xff)[0] != 0xfe:
142+
pass
143+
144+
# read data
145+
mv = self.dummybuf_memoryview[:len(buf)]
146+
self.spi.send_recv(mv, recv=buf)
147+
148+
# read checksum
149+
self.spi.send(0xff)
150+
self.spi.send(0xff)
151+
152+
self.cs.high()
153+
self.spi.send(0xff)
154+
155+
def write(self, buf):
156+
self.cs.low()
157+
158+
# send: start of block, data, checksum
159+
self.spi.send(0xfe)
160+
self.spi.send(buf)
161+
self.spi.send(0xff)
162+
self.spi.send(0xff)
163+
164+
# check the response
165+
if (self.spi.send_recv(0xff)[0] & 0x1f) != 0x05:
166+
self.cs.high()
167+
self.spi.send(0xff)
168+
return
169+
170+
# wait for write to finish
171+
while self.spi.send_recv(0xff)[0] == 0:
172+
pass
173+
174+
self.cs.high()
175+
self.spi.send(0xff)
176+
177+
def count(self):
178+
return self.sectors
179+
180+
def readblocks(self, block_num, buf):
181+
# TODO support multiple block reads
182+
assert len(buf) == 512
183+
184+
# CMD17: set read address for single block
185+
if self.cmd(17, block_num * self.cdv, 0) != 0:
186+
return 1
187+
188+
# receive the data
189+
self.readinto(buf)
190+
return 0
191+
192+
def writeblocks(self, block_num, buf):
193+
# TODO support multiple block writes
194+
assert len(buf) == 512
195+
196+
# CMD24: set write address for single block
197+
if self.cmd(24, block_num * self.cdv, 0) != 0:
198+
return 1
199+
200+
# send the data
201+
self.write(buf)
202+
return 0

0 commit comments

Comments
 (0)