Advertisement
HoleForYou

sd card module for 8266

Sep 30th, 2021
225
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.72 KB | None | 0 0
  1. """
  2. MicroPython driver for SD cards using SPI bus.
  3. Requires an SPI bus and a CS pin.  Provides readblocks and writeblocks
  4. methods so the device can be mounted as a filesystem.
  5. Example usage on pyboard:
  6.    import pyb, sdcard, os
  7.    sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
  8.    pyb.mount(sd, '/sd2')
  9.    os.listdir('/')
  10. Example usage on ESP8266:
  11.    import machine, sdcard, os
  12.    sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))
  13.    os.mount(sd, '/sd')
  14.    os.listdir('/')
  15. """
  16.  
  17. from micropython import const
  18. import time
  19.  
  20.  
  21. _CMD_TIMEOUT = const(100)
  22.  
  23. _R1_IDLE_STATE = const(1 << 0)
  24. # R1_ERASE_RESET = const(1 << 1)
  25. _R1_ILLEGAL_COMMAND = const(1 << 2)
  26. # R1_COM_CRC_ERROR = const(1 << 3)
  27. # R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
  28. # R1_ADDRESS_ERROR = const(1 << 5)
  29. # R1_PARAMETER_ERROR = const(1 << 6)
  30. _TOKEN_CMD25 = const(0xFC)
  31. _TOKEN_STOP_TRAN = const(0xFD)
  32. _TOKEN_DATA = const(0xFE)
  33.  
  34.  
  35. class SDCard:
  36.     def __init__(self, spi, cs):
  37.         self.spi = spi
  38.         self.cs = cs
  39.  
  40.         self.cmdbuf = bytearray(6)
  41.         self.dummybuf = bytearray(512)
  42.         self.tokenbuf = bytearray(1)
  43.         for i in range(512):
  44.             self.dummybuf[i] = 0xFF
  45.         self.dummybuf_memoryview = memoryview(self.dummybuf)
  46.  
  47.         # initialise the card
  48.         self.init_card()
  49.  
  50.     def init_spi(self, baudrate):
  51.         try:
  52.             master = self.spi.MASTER
  53.         except AttributeError:
  54.             # on ESP8266
  55.             self.spi.init(baudrate=baudrate, phase=0, polarity=0)
  56.         else:
  57.             # on pyboard
  58.             self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
  59.  
  60.     def init_card(self):
  61.         # init CS pin
  62.         self.cs.init(self.cs.OUT, value=1)
  63.  
  64.         # init SPI bus; use low data rate for initialisation
  65.         self.init_spi(100000)
  66.  
  67.         # clock card at least 100 cycles with cs high
  68.         for i in range(16):
  69.             self.spi.write(b"\xff")
  70.  
  71.         # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
  72.         for _ in range(5):
  73.             if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
  74.                 break
  75.         else:
  76.             raise OSError("no SD card")
  77.  
  78.         # CMD8: determine card version
  79.         r = self.cmd(8, 0x01AA, 0x87, 4)
  80.         if r == _R1_IDLE_STATE:
  81.             self.init_card_v2()
  82.         elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
  83.             self.init_card_v1()
  84.         else:
  85.             raise OSError("couldn't determine SD card version")
  86.  
  87.         # get the number of sectors
  88.         # CMD9: response R2 (R1 byte + 16-byte block read)
  89.         if self.cmd(9, 0, 0, 0, False) != 0:
  90.             raise OSError("no response from SD card")
  91.         csd = bytearray(16)
  92.         self.readinto(csd)
  93.         if csd[0] & 0xC0 == 0x40:  # CSD version 2.0
  94.             self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
  95.         elif csd[0] & 0xC0 == 0x00:  # CSD version 1.0 (old, <=2GB)
  96.             c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4
  97.             c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7
  98.             self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2))
  99.         else:
  100.             raise OSError("SD card CSD format not supported")
  101.         # print('sectors', self.sectors)
  102.  
  103.         # CMD16: set block length to 512 bytes
  104.         if self.cmd(16, 512, 0) != 0:
  105.             raise OSError("can't set 512 block size")
  106.  
  107.         # set to high data rate now that it's initialised
  108.         self.init_spi(1320000)
  109.  
  110.     def init_card_v1(self):
  111.         for i in range(_CMD_TIMEOUT):
  112.             self.cmd(55, 0, 0)
  113.             if self.cmd(41, 0, 0) == 0:
  114.                 self.cdv = 512
  115.                 # print("[SDCard] v1 card")
  116.                 return
  117.         raise OSError("timeout waiting for v1 card")
  118.  
  119.     def init_card_v2(self):
  120.         for i in range(_CMD_TIMEOUT):
  121.             time.sleep_ms(50)
  122.             self.cmd(58, 0, 0, 4)
  123.             self.cmd(55, 0, 0)
  124.             if self.cmd(41, 0x40000000, 0) == 0:
  125.                 self.cmd(58, 0, 0, 4)
  126.                 self.cdv = 1
  127.                 # print("[SDCard] v2 card")
  128.                 return
  129.         raise OSError("timeout waiting for v2 card")
  130.  
  131.     def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
  132.         self.cs(0)
  133.  
  134.         # create and send the command
  135.         buf = self.cmdbuf
  136.         buf[0] = 0x40 | cmd
  137.         buf[1] = arg >> 24
  138.         buf[2] = arg >> 16
  139.         buf[3] = arg >> 8
  140.         buf[4] = arg
  141.         buf[5] = crc
  142.         self.spi.write(buf)
  143.  
  144.         if skip1:
  145.             self.spi.readinto(self.tokenbuf, 0xFF)
  146.  
  147.         # wait for the response (response[7] == 0)
  148.         for i in range(_CMD_TIMEOUT):
  149.             self.spi.readinto(self.tokenbuf, 0xFF)
  150.             response = self.tokenbuf[0]
  151.             if not (response & 0x80):
  152.                 # this could be a big-endian integer that we are getting here
  153.                 for j in range(final):
  154.                     self.spi.write(b"\xff")
  155.                 if release:
  156.                     self.cs(1)
  157.                     self.spi.write(b"\xff")
  158.                 return response
  159.  
  160.         # timeout
  161.         self.cs(1)
  162.         self.spi.write(b"\xff")
  163.         return -1
  164.  
  165.     def readinto(self, buf):
  166.         self.cs(0)
  167.  
  168.         # read until start byte (0xff)
  169.         for i in range(_CMD_TIMEOUT):
  170.             self.spi.readinto(self.tokenbuf, 0xFF)
  171.             if self.tokenbuf[0] == _TOKEN_DATA:
  172.                 break
  173.             time.sleep_ms(1)
  174.         else:
  175.             self.cs(1)
  176.             raise OSError("timeout waiting for response")
  177.  
  178.         # read data
  179.         mv = self.dummybuf_memoryview
  180.         if len(buf) != len(mv):
  181.             mv = mv[: len(buf)]
  182.         self.spi.write_readinto(mv, buf)
  183.  
  184.         # read checksum
  185.         self.spi.write(b"\xff")
  186.         self.spi.write(b"\xff")
  187.  
  188.         self.cs(1)
  189.         self.spi.write(b"\xff")
  190.  
  191.     def write(self, token, buf):
  192.         self.cs(0)
  193.  
  194.         # send: start of block, data, checksum
  195.         self.spi.read(1, token)
  196.         self.spi.write(buf)
  197.         self.spi.write(b"\xff")
  198.         self.spi.write(b"\xff")
  199.  
  200.         # check the response
  201.         if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:
  202.             self.cs(1)
  203.             self.spi.write(b"\xff")
  204.             return
  205.  
  206.         # wait for write to finish
  207.         while self.spi.read(1, 0xFF)[0] == 0:
  208.             pass
  209.  
  210.         self.cs(1)
  211.         self.spi.write(b"\xff")
  212.  
  213.     def write_token(self, token):
  214.         self.cs(0)
  215.         self.spi.read(1, token)
  216.         self.spi.write(b"\xff")
  217.         # wait for write to finish
  218.         while self.spi.read(1, 0xFF)[0] == 0x00:
  219.             pass
  220.  
  221.         self.cs(1)
  222.         self.spi.write(b"\xff")
  223.  
  224.     def readblocks(self, block_num, buf):
  225.         nblocks = len(buf) // 512
  226.         assert nblocks and not len(buf) % 512, "Buffer length is invalid"
  227.         if nblocks == 1:
  228.             # CMD17: set read address for single block
  229.             if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
  230.                 # release the card
  231.                 self.cs(1)
  232.                 raise OSError(5)  # EIO
  233.             # receive the data and release card
  234.             self.readinto(buf)
  235.         else:
  236.             # CMD18: set read address for multiple blocks
  237.             if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
  238.                 # release the card
  239.                 self.cs(1)
  240.                 raise OSError(5)  # EIO
  241.             offset = 0
  242.             mv = memoryview(buf)
  243.             while nblocks:
  244.                 # receive the data and release card
  245.                 self.readinto(mv[offset : offset + 512])
  246.                 offset += 512
  247.                 nblocks -= 1
  248.             if self.cmd(12, 0, 0xFF, skip1=True):
  249.                 raise OSError(5)  # EIO
  250.  
  251.     def writeblocks(self, block_num, buf):
  252.         nblocks, err = divmod(len(buf), 512)
  253.         assert nblocks and not err, "Buffer length is invalid"
  254.         if nblocks == 1:
  255.             # CMD24: set write address for single block
  256.             if self.cmd(24, block_num * self.cdv, 0) != 0:
  257.                 raise OSError(5)  # EIO
  258.  
  259.             # send the data
  260.             self.write(_TOKEN_DATA, buf)
  261.         else:
  262.             # CMD25: set write address for first block
  263.             if self.cmd(25, block_num * self.cdv, 0) != 0:
  264.                 raise OSError(5)  # EIO
  265.             # send the data
  266.             offset = 0
  267.             mv = memoryview(buf)
  268.             while nblocks:
  269.                 self.write(_TOKEN_CMD25, mv[offset : offset + 512])
  270.                 offset += 512
  271.                 nblocks -= 1
  272.             self.write_token(_TOKEN_STOP_TRAN)
  273.  
  274.     def ioctl(self, op, arg):
  275.         if op == 4:  # get number of blocks
  276.             return self.sectors
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement