2 MicroPython driver for SD cards using SPI bus.
4 Requires an SPI bus and a CS pin. Provides readblocks and writeblocks
5 methods so the device can be mounted as a filesystem.
7 Example usage on pyboard:
10 sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
14 Example usage on ESP8266:
16 import machine, sdcard, os
17 sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))
23 from micropython
import const
27 _CMD_TIMEOUT
= const(100)
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)
42 def __init__(self
, spi
, cs
, baudrate
=1320000):
46 self
.cmdbuf
= bytearray(6)
47 self
.dummybuf
= bytearray(512)
48 self
.tokenbuf
= bytearray(1)
50 self
.dummybuf
[i
] = 0xFF
51 self
.dummybuf_memoryview
= memoryview(self
.dummybuf
)
54 self
.init_card(baudrate
)
56 def init_spi(self
, baudrate
):
58 master
= self
.spi
.MASTER
59 except AttributeError:
61 self
.spi
.init(baudrate
=baudrate
, phase
=0, polarity
=0)
64 self
.spi
.init(master
, baudrate
=baudrate
, phase
=0, polarity
=0)
66 def init_card(self
, baudrate
):
68 self
.cs
.init(self
.cs
.OUT
, value
=1)
70 # init SPI bus; use low data rate for initialisation
73 # clock card at least 100 cycles with cs high
75 self
.spi
.write(b
"\xff")
77 # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
79 if self
.cmd(0, 0, 0x95) == _R1_IDLE_STATE
:
82 raise OSError("no SD card")
84 # CMD8: determine card version
85 r
= self
.cmd(8, 0x01AA, 0x87, 4)
86 if r
== _R1_IDLE_STATE
:
88 elif r
== (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND
):
91 raise OSError("couldn't determine SD card version")
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")
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
108 raise OSError("SD card CSD format not supported")
109 # print('sectors', self.sectors)
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")
115 # set to high data rate now that it's initialised
116 self
.init_spi(baudrate
)
118 def init_card_v1(self
):
119 for i
in range(_CMD_TIMEOUT
):
122 if self
.cmd(41, 0, 0) == 0:
123 # SDSC card, uses byte addressing in read/write/erase commands
125 # print("[SDCard] v1 card")
127 raise OSError("timeout waiting for v1 card")
129 def init_card_v2(self
):
130 for i
in range(_CMD_TIMEOUT
):
132 self
.cmd(58, 0, 0, 4)
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
138 # SDSC card, uses byte addressing in read/write/erase commands
141 # SDHC/SDXC card, uses block addressing in read/write/erase commands
143 # print("[SDCard] v2 card")
145 raise OSError("timeout waiting for v2 card")
147 def cmd(self
, cmd
, arg
, crc
, final
=0, release
=True, skip1
=False):
150 # create and send the command
161 self
.spi
.readinto(self
.tokenbuf
, 0xFF)
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
171 self
.spi
.readinto(self
.tokenbuf
, 0xFF)
173 for j
in range(final
):
174 self
.spi
.write(b
"\xff")
177 self
.spi
.write(b
"\xff")
182 self
.spi
.write(b
"\xff")
185 def readinto(self
, buf
):
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
:
196 raise OSError("timeout waiting for response")
199 mv
= self
.dummybuf_memoryview
200 if len(buf
) != len(mv
):
202 self
.spi
.write_readinto(mv
, buf
)
205 self
.spi
.write(b
"\xff")
206 self
.spi
.write(b
"\xff")
209 self
.spi
.write(b
"\xff")
211 def write(self
, token
, buf
):
214 # send: start of block, data, checksum
215 self
.spi
.read(1, token
)
217 self
.spi
.write(b
"\xff")
218 self
.spi
.write(b
"\xff")
221 if (self
.spi
.read(1, 0xFF)[0] & 0x1F) != 0x05:
223 self
.spi
.write(b
"\xff")
226 # wait for write to finish
227 while self
.spi
.read(1, 0xFF)[0] == 0:
231 self
.spi
.write(b
"\xff")
233 def write_token(self
, token
):
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:
242 self
.spi
.write(b
"\xff")
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")
249 nblocks
= len(buf
) // 512
250 assert nblocks
and not len(buf
) % 512, "Buffer length is invalid"
252 # CMD17: set read address for single block
253 if self
.cmd(17, block_num
* self
.cdv
, 0, release
=False) != 0:
256 raise OSError(5) # EIO
257 # receive the data and release card
260 # CMD18: set read address for multiple blocks
261 if self
.cmd(18, block_num
* self
.cdv
, 0, release
=False) != 0:
264 raise OSError(5) # EIO
268 # receive the data and release card
269 self
.readinto(mv
[offset
: offset
+ 512])
272 if self
.cmd(12, 0, 0xFF, skip1
=True):
273 raise OSError(5) # EIO
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")
280 nblocks
, err
= divmod(len(buf
), 512)
281 assert nblocks
and not err
, "Buffer length is invalid"
283 # CMD24: set write address for single block
284 if self
.cmd(24, block_num
* self
.cdv
, 0) != 0:
285 raise OSError(5) # EIO
288 self
.write(_TOKEN_DATA
, buf
)
290 # CMD25: set write address for first block
291 if self
.cmd(25, block_num
* self
.cdv
, 0) != 0:
292 raise OSError(5) # EIO
297 self
.write(_TOKEN_CMD25
, mv
[offset
: offset
+ 512])
300 self
.write_token(_TOKEN_STOP_TRAN
)
302 def ioctl(self
, op
, arg
):
303 if op
== 4: # get number of blocks
305 if op
== 5: # get block size in bytes
309 __version__
= '0.1.0'