/ 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