/ adafruit_sdcard.py
adafruit_sdcard.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2014-2016 Damien George, Peter Hinch and Radomir Dopieralski 4 # Copyright (c) 2017 Scott Shawcroft for Adafruit Industries 5 # 6 # Permission is hereby granted, free of charge, to any person obtaining a copy 7 # of this software and associated documentation files (the "Software"), to deal 8 # in the Software without restriction, including without limitation the rights 9 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 # copies of the Software, and to permit persons to whom the Software is 11 # furnished to do so, subject to the following conditions: 12 # 13 # The above copyright notice and this permission notice shall be included in 14 # all copies or substantial portions of the Software. 15 # 16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 # THE SOFTWARE. 23 """ 24 `adafruit_sdcard` - SD card over SPI driver 25 ==================================================== 26 27 CircuitPython driver for SD cards using SPI bus. 28 29 Requires an SPI bus and a CS pin. Provides readblocks and writeblocks 30 methods so the device can be mounted as a filesystem. 31 32 * Author(s): Scott Shawcroft 33 34 Implementation Notes 35 -------------------- 36 37 **Hardware:** 38 39 * Adafruit `MicroSD card breakout board+ 40 <https://www.adafruit.com/product/254>`_ (Product ID: 254) 41 42 * Adafruit `Assembled Data Logging shield for Arduino 43 <https://www.adafruit.com/product/1141>`_ (Product ID: 1141) 44 45 * Adafruit `Feather M0 Adalogger 46 <https://www.adafruit.com/product/2796>`_ (Product ID: 2796) 47 48 * Adalogger `FeatherWing - RTC + SD Add-on For All Feather Boards 49 <https://www.adafruit.com/product/2922>`_ (Product ID: 2922) 50 51 **Software and Dependencies:** 52 53 * Adafruit CircuitPython firmware for the ESP8622 and M0-based boards: 54 https://github.com/adafruit/circuitpython/releases 55 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 56 """ 57 58 import time 59 from micropython import const 60 from adafruit_bus_device import spi_device 61 62 __version__ = "0.0.0-auto.0" 63 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SD.git" 64 65 _CMD_TIMEOUT = const(200) 66 67 _R1_IDLE_STATE = const(1 << 0) 68 # R1_ERASE_RESET = const(1 << 1) 69 _R1_ILLEGAL_COMMAND = const(1 << 2) 70 # R1_COM_CRC_ERROR = const(1 << 3) 71 # R1_ERASE_SEQUENCE_ERROR = const(1 << 4) 72 # R1_ADDRESS_ERROR = const(1 << 5) 73 # R1_PARAMETER_ERROR = const(1 << 6) 74 _TOKEN_CMD25 = const(0xFC) 75 _TOKEN_STOP_TRAN = const(0xFD) 76 _TOKEN_DATA = const(0xFE) 77 78 # pylint: disable-msg=superfluous-parens 79 class SDCard: 80 """Controls an SD card over SPI. 81 82 :param ~busio.SPI spi: The SPI bus 83 :param ~digitalio.DigitalInOut cs: The chip select connected to the card 84 :param int baudrate: The SPI data rate to use after card setup 85 86 Example usage: 87 88 .. code-block:: python 89 90 import busio 91 import storage 92 import adafruit_sdcard 93 import os 94 import board 95 96 spi = busio.SPI(SCK, MOSI, MISO) 97 sd = adafruit_sdcard.SDCard(spi, board.SD_CS) 98 vfs = storage.VfsFat(sdcard) 99 storage.mount(vfs, '/sd') 100 os.listdir('/') 101 102 """ 103 104 def __init__(self, spi, cs, baudrate=1320000): 105 # This is the init baudrate. 106 # We create a second device with the target baudrate after card initialization. 107 self._spi = spi_device.SPIDevice(spi, cs, baudrate=250000, extra_clocks=8) 108 109 self._cmdbuf = bytearray(6) 110 self._single_byte = bytearray(1) 111 112 # Card is byte addressing, set to 1 if addresses are per block 113 self._cdv = 512 114 115 # initialise the card and switch to high speed 116 self._init_card(baudrate) 117 118 def _clock_card(self, cycles=8): 119 """ 120 Clock the bus a minimum of `cycles` with the chip select high. 121 122 :param int cycles: The minimum number of clock cycles to cycle the bus. 123 """ 124 while not self._spi.spi.try_lock(): 125 pass 126 self._spi.spi.configure(baudrate=self._spi.baudrate) 127 self._spi.chip_select.value = True 128 129 self._single_byte[0] = 0xFF 130 for _ in range(cycles // 8 + 1): 131 self._spi.spi.write(self._single_byte) 132 self._spi.spi.unlock() 133 134 def _init_card(self, baudrate): 135 """Initialize the card in SPI mode.""" 136 # clock card at least cycles with cs high 137 self._clock_card(80) 138 139 with self._spi as card: 140 # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) 141 for _ in range(5): 142 if self._cmd(card, 0, 0, 0x95) == _R1_IDLE_STATE: 143 break 144 else: 145 raise OSError("no SD card") 146 147 # CMD8: determine card version 148 rb7 = bytearray(4) 149 r = self._cmd(card, 8, 0x01AA, 0x87, rb7, data_block=False) 150 if r == _R1_IDLE_STATE: 151 self._init_card_v2(card) 152 elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): 153 self._init_card_v1(card) 154 else: 155 raise OSError("couldn't determine SD card version") 156 157 # get the number of sectors 158 # CMD9: response R2 (R1 byte + 16-byte block read) 159 csd = bytearray(16) 160 if self._cmd(card, 9, 0, 0xAF, response_buf=csd) != 0: 161 raise OSError("no response from SD card") 162 # self.readinto(csd) 163 csd_version = (csd[0] & 0xC0) >> 6 164 if csd_version >= 2: 165 raise OSError("SD card CSD format not supported") 166 167 if csd_version == 1: 168 self._sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 169 else: 170 block_length = 2 ** (csd[5] & 0xF) 171 c_size = ((csd[6] & 0x3) << 10) | (csd[7] << 2) | ((csd[8] & 0xC) >> 6) 172 mult = 2 ** (((csd[9] & 0x3) << 1 | (csd[10] & 0x80) >> 7) + 2) 173 self._sectors = block_length // 512 * mult * (c_size + 1) 174 175 # CMD16: set block length to 512 bytes 176 if self._cmd(card, 16, 512, 0x15) != 0: 177 raise OSError("can't set 512 block size") 178 179 # set to high data rate now that it's initialised 180 self._spi = spi_device.SPIDevice( 181 self._spi.spi, self._spi.chip_select, baudrate=baudrate, extra_clocks=8 182 ) 183 184 def _init_card_v1(self, card): 185 """Initialize v1 SDCards which use byte addressing.""" 186 for _ in range(_CMD_TIMEOUT): 187 self._cmd(card, 55, 0, 0) 188 if self._cmd(card, 41, 0, 0) == 0: 189 # print("[SDCard] v1 card") 190 return 191 raise OSError("timeout waiting for v1 card") 192 193 def _init_card_v2(self, card): 194 """Initialize v2 SDCards which use 512-byte block addressing.""" 195 ocr = bytearray(4) 196 for _ in range(_CMD_TIMEOUT): 197 time.sleep(0.050) 198 self._cmd(card, 58, 0, 0xFD, response_buf=ocr, data_block=False) 199 self._cmd(card, 55, 0, 0x65) 200 # On non-longint builds, we cannot use 0x40000000 directly as the arg 201 # so break it into bytes, which are interpreted by self._cmd(). 202 if self._cmd(card, 41, b"\x40\x00\x00\x00", 0x77) == 0: 203 self._cmd(card, 58, 0, 0xFD, response_buf=ocr, data_block=False) 204 205 # Check for block addressing 206 if (ocr[0] & 0x40) != 0: 207 self._cdv = 1 208 # print("[SDCard] v2 card") 209 return 210 raise OSError("timeout waiting for v2 card") 211 212 def _wait_for_ready(self, card, timeout=0.3): 213 """ 214 Wait for the card to clock out 0xff to indicate its ready. 215 216 :param busio.SPI card: The locked SPI bus. 217 :param float timeout: Maximum time to wait in seconds. 218 """ 219 start_time = time.monotonic() 220 self._single_byte[0] = 0x00 221 while time.monotonic() - start_time < timeout and self._single_byte[0] != 0xFF: 222 card.readinto(self._single_byte, write_value=0xFF) 223 224 # pylint: disable-msg=too-many-arguments 225 # pylint: disable=no-member 226 # no-member disable should be reconsidered when it can be tested 227 def _cmd( 228 self, card, cmd, arg=0, crc=0, response_buf=None, data_block=True, wait=True 229 ): 230 """ 231 Issue a command to the card and read an optional data response. 232 233 :param busio.SPI card: The locked SPI bus. 234 :param int cmd: The command number. 235 :param int|buf(4) arg: The command argument 236 :param int crc: The crc to allow the card to verify the command and argument. 237 :param bytearray response_buf: Buffer to read a data block response into. 238 :param bool data_block: True if the response data is in a data block. 239 """ 240 # create and send the command 241 buf = self._cmdbuf 242 buf[0] = 0x40 | cmd 243 if isinstance(arg, int): 244 buf[1] = (arg >> 24) & 0xFF 245 buf[2] = (arg >> 16) & 0xFF 246 buf[3] = (arg >> 8) & 0xFF 247 buf[4] = arg & 0xFF 248 elif len(arg) == 4: 249 # arg can be a 4-byte buf 250 buf[1:5] = arg 251 else: 252 raise ValueError() 253 254 if crc == 0: 255 buf[5] = calculate_crc(buf[:-1]) 256 else: 257 buf[5] = crc 258 259 if wait: 260 self._wait_for_ready(card) 261 262 card.write(buf) 263 264 # wait for the response (response[7] == 0) 265 for _ in range(_CMD_TIMEOUT): 266 card.readinto(buf, end=1, write_value=0xFF) 267 if not (buf[0] & 0x80): 268 if response_buf: 269 if data_block: 270 # Wait for the start block byte 271 buf[1] = 0xFF 272 while buf[1] != 0xFE: 273 card.readinto(buf, start=1, end=2, write_value=0xFF) 274 card.readinto(response_buf, write_value=0xFF) 275 if data_block: 276 # Read the checksum 277 card.readinto(buf, start=1, end=3, write_value=0xFF) 278 return buf[0] 279 return -1 280 281 # pylint: enable-msg=too-many-arguments 282 283 # pylint: disable-msg=too-many-arguments 284 def _block_cmd(self, card, cmd, block, crc, response_buf=None): 285 """ 286 Issue a command to the card with a block argument. 287 288 :param busio.SPI card: The locked SPI bus. 289 :param int cmd: The command number. 290 :param int block: The relevant block. 291 :param int crc: The crc to allow the card to verify the command and argument. 292 """ 293 if self._cdv == 1: 294 return self._cmd(card, cmd, block, crc, response_buf=response_buf) 295 296 # create and send the command 297 buf = self._cmdbuf 298 buf[0] = 0x40 | cmd 299 # We address by byte because cdv is 512. Instead of multiplying, shift 300 # the data to the correct spot so that we don't risk creating a long 301 # int. 302 buf[1] = (block >> 15) & 0xFF 303 buf[2] = (block >> 7) & 0xFF 304 buf[3] = (block << 1) & 0xFF 305 buf[4] = 0 306 307 if crc == 0: 308 buf[5] = calculate_crc(buf[:-1]) 309 else: 310 buf[5] = crc 311 312 result = -1 313 self._wait_for_ready(card) 314 315 card.write(buf) 316 317 # wait for the response (response[7] == 0) 318 for _ in range(_CMD_TIMEOUT): 319 card.readinto(buf, end=1, write_value=0xFF) 320 if not (buf[0] & 0x80): 321 result = buf[0] 322 break 323 324 # pylint: disable=singleton-comparison 325 # Disable should be removed when refactor can be tested. 326 if response_buf != None and result == 0: 327 self._readinto(card, response_buf) 328 329 return result 330 331 # pylint: enable-msg=too-many-arguments 332 333 def _cmd_nodata(self, card, cmd, response=0xFF): 334 """ 335 Issue a command to the card with no argument. 336 337 :param busio.SPI card: The locked SPI bus. 338 :param int cmd: The command number. 339 """ 340 buf = self._cmdbuf 341 buf[0] = cmd 342 buf[1] = 0xFF 343 344 card.write(buf, end=2) 345 for _ in range(_CMD_TIMEOUT): 346 card.readinto(buf, end=1, write_value=0xFF) 347 if buf[0] == response: 348 return 0 # OK 349 return 1 # timeout 350 351 def _readinto(self, card, buf, start=0, end=None): 352 """ 353 Read a data block into buf. 354 355 :param busio.SPI card: The locked SPI bus. 356 :param bytearray buf: The buffer to write into 357 :param int start: The first index to write data at 358 :param int end: The index after the last byte to write to. 359 """ 360 if end is None: 361 end = len(buf) 362 363 # read until start byte (0xfe) 364 buf[start] = 0xFF # busy 365 while buf[start] != 0xFE: 366 card.readinto(buf, start=start, end=start + 1, write_value=0xFF) 367 368 card.readinto(buf, start=start, end=end, write_value=0xFF) 369 370 # read checksum and throw it away 371 card.readinto(self._cmdbuf, end=2, write_value=0xFF) 372 373 # pylint: disable-msg=too-many-arguments 374 def _write(self, card, token, buf, start=0, end=None): 375 """ 376 Write a data block to the card. 377 378 :param busio.SPI card: The locked SPI bus. 379 :param int token: The start token 380 :param bytearray buf: The buffer to write from 381 :param int start: The first index to read data from 382 :param int end: The index after the last byte to read from. 383 """ 384 cmd = self._cmdbuf 385 if end is None: 386 end = len(buf) 387 388 self._wait_for_ready(card) 389 390 # send: start of block, data, checksum 391 cmd[0] = token 392 card.write(cmd, end=1) 393 card.write(buf, start=start, end=end) 394 cmd[0] = 0xFF 395 cmd[1] = 0xFF 396 card.write(cmd, end=2) 397 398 # check the response 399 # pylint: disable=no-else-return 400 # Disable should be removed when refactor can be tested 401 for _ in range(_CMD_TIMEOUT): 402 card.readinto(cmd, end=1, write_value=0xFF) 403 if not (cmd[0] & 0x80): 404 if (cmd[0] & 0x1F) != 0x05: 405 return -1 406 else: 407 break 408 409 # wait for write to finish 410 card.readinto(cmd, end=1, write_value=0xFF) 411 while cmd[0] == 0: 412 card.readinto(cmd, end=1, write_value=0xFF) 413 414 return 0 # worked 415 416 # pylint: enable-msg=too-many-arguments 417 418 def count(self): 419 """ 420 Returns the total number of sectors. 421 422 :return: The number of 512-byte blocks 423 :rtype: int 424 """ 425 return self._sectors 426 427 def readblocks(self, start_block, buf): 428 """ 429 Read one or more blocks from the card 430 431 :param int start_block: The block to start reading from 432 :param bytearray buf: The buffer to write into. Length must be multiple of 512. 433 """ 434 nblocks, err = divmod(len(buf), 512) 435 assert nblocks and not err, "Buffer length is invalid" 436 with self._spi as card: 437 if nblocks == 1: 438 # CMD17: set read address for single block 439 # We use _block_cmd to read our data so that the chip select line 440 # isn't toggled between the command, response and data. 441 if self._block_cmd(card, 17, start_block, 0, response_buf=buf) != 0: 442 return 1 443 else: 444 # CMD18: set read address for multiple blocks 445 if self._block_cmd(card, 18, start_block, 0) != 0: 446 return 1 447 offset = 0 448 while nblocks: 449 self._readinto(card, buf, start=offset, end=(offset + 512)) 450 offset += 512 451 nblocks -= 1 452 ret = self._cmd(card, 12, 0, 0x61, wait=False) 453 # return first status 0 or last before card ready (0xff) 454 while ret != 0: 455 card.readinto(self._single_byte, write_value=0xFF) 456 if self._single_byte[0] & 0x80: 457 return ret 458 ret = self._single_byte[0] 459 return 0 460 461 def writeblocks(self, start_block, buf): 462 """ 463 Write one or more blocks to the card 464 465 :param int start_block: The block to start writing to 466 :param bytearray buf: The buffer to write into. Length must be multiple of 512. 467 """ 468 nblocks, err = divmod(len(buf), 512) 469 assert nblocks and not err, "Buffer length is invalid" 470 with self._spi as card: 471 if nblocks == 1: 472 # CMD24: set write address for single block 473 if self._block_cmd(card, 24, start_block, 0) != 0: 474 return 1 475 476 # send the data 477 self._write(card, _TOKEN_DATA, buf) 478 else: 479 # CMD25: set write address for first block 480 if self._block_cmd(card, 25, start_block, 0) != 0: 481 return 1 482 # send the data 483 offset = 0 484 while nblocks: 485 self._write( 486 card, _TOKEN_CMD25, buf, start=offset, end=(offset + 512) 487 ) 488 offset += 512 489 nblocks -= 1 490 self._cmd_nodata(card, _TOKEN_STOP_TRAN, 0x0) 491 return 0 492 493 494 def _calculate_crc_table(): 495 """Precompute the table used in calculate_crc.""" 496 # Code converted from https://github.com/hazelnusse/crc7/blob/master/crc7.cc by devoh747 497 # With permission from Dale Lukas Peterson <hazelnusse@gmail.com> 498 # 8/6/2019 499 500 crc_table = bytearray(256) 501 crc_poly = const(0x89) # the value of our CRC-7 polynomial 502 503 # generate a table value for all 256 possible byte values 504 for i in range(256): 505 if i & 0x80: 506 crc_table[i] = i ^ crc_poly 507 else: 508 crc_table[i] = i 509 for _ in range(1, 8): 510 crc_table[i] = crc_table[i] << 1 511 if crc_table[i] & 0x80: 512 crc_table[i] = crc_table[i] ^ crc_poly 513 return crc_table 514 515 516 CRC_TABLE = _calculate_crc_table() 517 518 519 def calculate_crc(message): 520 """ 521 Calculate the CRC of message[0:5], using a precomputed table in CRC_TABLE. 522 :param bytearray message: Where each index is a byte 523 """ 524 525 crc = 0 526 # All messages in _cmd are 5 bytes including the cmd.. The 6th byte is the crc value. 527 for i in range(0, 5): 528 crc = CRC_TABLE[(crc << 1) ^ message[i]] 529 530 return (crc << 1) | 1