1""" 2MicroPython driver for SD cards using SPI bus. 3 4Requires an SPI bus and a CS pin. Provides readblocks and writeblocks 5methods so the device can be mounted as a filesystem. 6 7Example 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 14Example 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 23from micropython import const 24import 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 41class SDCard: 42 def __init__(self, spi, cs): 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() 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): 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 | csd[7] << 2 | (csd[8] & 0b11000000) << 4 103 c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7 104 self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2)) 105 else: 106 raise OSError("SD card CSD format not supported") 107 # print('sectors', self.sectors) 108 109 # CMD16: set block length to 512 bytes 110 if self.cmd(16, 512, 0) != 0: 111 raise OSError("can't set 512 block size") 112 113 # set to high data rate now that it's initialised 114 self.init_spi(1320000) 115 116 def init_card_v1(self): 117 for i in range(_CMD_TIMEOUT): 118 self.cmd(55, 0, 0) 119 if self.cmd(41, 0, 0) == 0: 120 self.cdv = 512 121 # print("[SDCard] v1 card") 122 return 123 raise OSError("timeout waiting for v1 card") 124 125 def init_card_v2(self): 126 for i in range(_CMD_TIMEOUT): 127 time.sleep_ms(50) 128 self.cmd(58, 0, 0, 4) 129 self.cmd(55, 0, 0) 130 if self.cmd(41, 0x40000000, 0) == 0: 131 self.cmd(58, 0, 0, 4) 132 self.cdv = 1 133 # print("[SDCard] v2 card") 134 return 135 raise OSError("timeout waiting for v2 card") 136 137 def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): 138 self.cs(0) 139 140 # create and send the command 141 buf = self.cmdbuf 142 buf[0] = 0x40 | cmd 143 buf[1] = arg >> 24 144 buf[2] = arg >> 16 145 buf[3] = arg >> 8 146 buf[4] = arg 147 buf[5] = crc 148 self.spi.write(buf) 149 150 if skip1: 151 self.spi.readinto(self.tokenbuf, 0xFF) 152 153 # wait for the response (response[7] == 0) 154 for i in range(_CMD_TIMEOUT): 155 self.spi.readinto(self.tokenbuf, 0xFF) 156 response = self.tokenbuf[0] 157 if not (response & 0x80): 158 # this could be a big-endian integer that we are getting here 159 for j in range(final): 160 self.spi.write(b"\xff") 161 if release: 162 self.cs(1) 163 self.spi.write(b"\xff") 164 return response 165 166 # timeout 167 self.cs(1) 168 self.spi.write(b"\xff") 169 return -1 170 171 def readinto(self, buf): 172 self.cs(0) 173 174 # read until start byte (0xff) 175 for i in range(_CMD_TIMEOUT): 176 self.spi.readinto(self.tokenbuf, 0xFF) 177 if self.tokenbuf[0] == _TOKEN_DATA: 178 break 179 time.sleep_ms(1) 180 else: 181 self.cs(1) 182 raise OSError("timeout waiting for response") 183 184 # read data 185 mv = self.dummybuf_memoryview 186 if len(buf) != len(mv): 187 mv = mv[: len(buf)] 188 self.spi.write_readinto(mv, buf) 189 190 # read checksum 191 self.spi.write(b"\xff") 192 self.spi.write(b"\xff") 193 194 self.cs(1) 195 self.spi.write(b"\xff") 196 197 def write(self, token, buf): 198 self.cs(0) 199 200 # send: start of block, data, checksum 201 self.spi.read(1, token) 202 self.spi.write(buf) 203 self.spi.write(b"\xff") 204 self.spi.write(b"\xff") 205 206 # check the response 207 if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05: 208 self.cs(1) 209 self.spi.write(b"\xff") 210 return 211 212 # wait for write to finish 213 while self.spi.read(1, 0xFF)[0] == 0: 214 pass 215 216 self.cs(1) 217 self.spi.write(b"\xff") 218 219 def write_token(self, token): 220 self.cs(0) 221 self.spi.read(1, token) 222 self.spi.write(b"\xff") 223 # wait for write to finish 224 while self.spi.read(1, 0xFF)[0] == 0x00: 225 pass 226 227 self.cs(1) 228 self.spi.write(b"\xff") 229 230 def readblocks(self, block_num, buf): 231 nblocks = len(buf) // 512 232 assert nblocks and not len(buf) % 512, "Buffer length is invalid" 233 if nblocks == 1: 234 # CMD17: set read address for single block 235 if self.cmd(17, block_num * self.cdv, 0, release=False) != 0: 236 # release the card 237 self.cs(1) 238 raise OSError(5) # EIO 239 # receive the data and release card 240 self.readinto(buf) 241 else: 242 # CMD18: set read address for multiple blocks 243 if self.cmd(18, block_num * self.cdv, 0, release=False) != 0: 244 # release the card 245 self.cs(1) 246 raise OSError(5) # EIO 247 offset = 0 248 mv = memoryview(buf) 249 while nblocks: 250 # receive the data and release card 251 self.readinto(mv[offset : offset + 512]) 252 offset += 512 253 nblocks -= 1 254 if self.cmd(12, 0, 0xFF, skip1=True): 255 raise OSError(5) # EIO 256 257 def writeblocks(self, block_num, buf): 258 nblocks, err = divmod(len(buf), 512) 259 assert nblocks and not err, "Buffer length is invalid" 260 if nblocks == 1: 261 # CMD24: set write address for single block 262 if self.cmd(24, block_num * self.cdv, 0) != 0: 263 raise OSError(5) # EIO 264 265 # send the data 266 self.write(_TOKEN_DATA, buf) 267 else: 268 # CMD25: set write address for first block 269 if self.cmd(25, block_num * self.cdv, 0) != 0: 270 raise OSError(5) # EIO 271 # send the data 272 offset = 0 273 mv = memoryview(buf) 274 while nblocks: 275 self.write(_TOKEN_CMD25, mv[offset : offset + 512]) 276 offset += 512 277 nblocks -= 1 278 self.write_token(_TOKEN_STOP_TRAN) 279 280 def ioctl(self, op, arg): 281 if op == 4: # get number of blocks 282 return self.sectors 283