sdcard.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. from micropython import const
  17. import time
  18. _CMD_TIMEOUT = const(100)
  19. _R1_IDLE_STATE = const(1 << 0)
  20. #R1_ERASE_RESET = const(1 << 1)
  21. _R1_ILLEGAL_COMMAND = const(1 << 2)
  22. #R1_COM_CRC_ERROR = const(1 << 3)
  23. #R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
  24. #R1_ADDRESS_ERROR = const(1 << 5)
  25. #R1_PARAMETER_ERROR = const(1 << 6)
  26. _TOKEN_CMD25 = const(0xfc)
  27. _TOKEN_STOP_TRAN = const(0xfd)
  28. _TOKEN_DATA = const(0xfe)
  29. class SDCard:
  30. def __init__(self, spi, cs):
  31. self.spi = spi
  32. self.cs = cs
  33. self.cmdbuf = bytearray(6)
  34. self.dummybuf = bytearray(512)
  35. self.tokenbuf = bytearray(1)
  36. for i in range(512):
  37. self.dummybuf[i] = 0xff
  38. self.dummybuf_memoryview = memoryview(self.dummybuf)
  39. # initialise the card
  40. self.init_card()
  41. def init_spi(self, baudrate):
  42. try:
  43. master = self.spi.MASTER
  44. except AttributeError:
  45. # on ESP8266
  46. self.spi.init(baudrate=baudrate, phase=0, polarity=0)
  47. else:
  48. # on pyboard
  49. self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
  50. def init_card(self):
  51. # init CS pin
  52. self.cs.init(self.cs.OUT, value=1)
  53. # init SPI bus; use low data rate for initialisation
  54. self.init_spi(100000)
  55. # clock card at least 100 cycles with cs high
  56. for i in range(16):
  57. self.spi.write(b'\xff')
  58. # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
  59. for _ in range(5):
  60. if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
  61. break
  62. else:
  63. raise OSError("no SD card")
  64. # CMD8: determine card version
  65. r = self.cmd(8, 0x01aa, 0x87, 4)
  66. if r == _R1_IDLE_STATE:
  67. self.init_card_v2()
  68. elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
  69. self.init_card_v1()
  70. else:
  71. raise OSError("couldn't determine SD card version")
  72. # get the number of sectors
  73. # CMD9: response R2 (R1 byte + 16-byte block read)
  74. if self.cmd(9, 0, 0, 0, False) != 0:
  75. raise OSError("no response from SD card")
  76. csd = bytearray(16)
  77. self.readinto(csd)
  78. if csd[0] & 0xc0 == 0x40: # CSD version 2.0
  79. self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
  80. elif csd[0] & 0xc0 == 0x00: # CSD version 1.0 (old, <=2GB)
  81. c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4
  82. c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7
  83. self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2))
  84. else:
  85. raise OSError("SD card CSD format not supported")
  86. #print('sectors', self.sectors)
  87. # CMD16: set block length to 512 bytes
  88. if self.cmd(16, 512, 0) != 0:
  89. raise OSError("can't set 512 block size")
  90. # set to high data rate now that it's initialised
  91. self.init_spi(1320000)
  92. def init_card_v1(self):
  93. for i in range(_CMD_TIMEOUT):
  94. self.cmd(55, 0, 0)
  95. if self.cmd(41, 0, 0) == 0:
  96. self.cdv = 512
  97. #print("[SDCard] v1 card")
  98. return
  99. raise OSError("timeout waiting for v1 card")
  100. def init_card_v2(self):
  101. for i in range(_CMD_TIMEOUT):
  102. time.sleep_ms(50)
  103. self.cmd(58, 0, 0, 4)
  104. self.cmd(55, 0, 0)
  105. if self.cmd(41, 0x40000000, 0) == 0:
  106. self.cmd(58, 0, 0, 4)
  107. self.cdv = 1
  108. #print("[SDCard] v2 card")
  109. return
  110. raise OSError("timeout waiting for v2 card")
  111. def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
  112. self.cs(0)
  113. # create and send the command
  114. buf = self.cmdbuf
  115. buf[0] = 0x40 | cmd
  116. buf[1] = arg >> 24
  117. buf[2] = arg >> 16
  118. buf[3] = arg >> 8
  119. buf[4] = arg
  120. buf[5] = crc
  121. self.spi.write(buf)
  122. if skip1:
  123. self.spi.readinto(self.tokenbuf, 0xff)
  124. # wait for the response (response[7] == 0)
  125. for i in range(_CMD_TIMEOUT):
  126. self.spi.readinto(self.tokenbuf, 0xff)
  127. response = self.tokenbuf[0]
  128. if not (response & 0x80):
  129. # this could be a big-endian integer that we are getting here
  130. for j in range(final):
  131. self.spi.write(b'\xff')
  132. if release:
  133. self.cs(1)
  134. self.spi.write(b'\xff')
  135. return response
  136. # timeout
  137. self.cs(1)
  138. self.spi.write(b'\xff')
  139. return -1
  140. def readinto(self, buf):
  141. self.cs(0)
  142. # read until start byte (0xff)
  143. while True:
  144. self.spi.readinto(self.tokenbuf, 0xff)
  145. if self.tokenbuf[0] == _TOKEN_DATA:
  146. break
  147. # read data
  148. mv = self.dummybuf_memoryview
  149. if len(buf) != len(mv):
  150. mv = mv[:len(buf)]
  151. self.spi.write_readinto(mv, buf)
  152. # read checksum
  153. self.spi.write(b'\xff')
  154. self.spi.write(b'\xff')
  155. self.cs(1)
  156. self.spi.write(b'\xff')
  157. def write(self, token, buf):
  158. self.cs(0)
  159. # send: start of block, data, checksum
  160. self.spi.read(1, token)
  161. self.spi.write(buf)
  162. self.spi.write(b'\xff')
  163. self.spi.write(b'\xff')
  164. # check the response
  165. if (self.spi.read(1, 0xff)[0] & 0x1f) != 0x05:
  166. self.cs(1)
  167. self.spi.write(b'\xff')
  168. return
  169. # wait for write to finish
  170. while self.spi.read(1, 0xff)[0] == 0:
  171. pass
  172. self.cs(1)
  173. self.spi.write(b'\xff')
  174. def write_token(self, token):
  175. self.cs(0)
  176. self.spi.read(1, token)
  177. self.spi.write(b'\xff')
  178. # wait for write to finish
  179. while self.spi.read(1, 0xff)[0] == 0x00:
  180. pass
  181. self.cs(1)
  182. self.spi.write(b'\xff')
  183. def readblocks(self, block_num, buf):
  184. nblocks = len(buf) // 512
  185. assert nblocks and not len(buf) % 512, 'Buffer length is invalid'
  186. if nblocks == 1:
  187. # CMD17: set read address for single block
  188. if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
  189. # release the card
  190. self.cs(1)
  191. raise OSError(5) # EIO
  192. # receive the data and release card
  193. self.readinto(buf)
  194. else:
  195. # CMD18: set read address for multiple blocks
  196. if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
  197. # release the card
  198. self.cs(1)
  199. raise OSError(5) # EIO
  200. offset = 0
  201. mv = memoryview(buf)
  202. while nblocks:
  203. # receive the data and release card
  204. self.readinto(mv[offset : offset + 512])
  205. offset += 512
  206. nblocks -= 1
  207. if self.cmd(12, 0, 0xff, skip1=True):
  208. raise OSError(5) # EIO
  209. def writeblocks(self, block_num, buf):
  210. nblocks, err = divmod(len(buf), 512)
  211. assert nblocks and not err, 'Buffer length is invalid'
  212. if nblocks == 1:
  213. # CMD24: set write address for single block
  214. if self.cmd(24, block_num * self.cdv, 0) != 0:
  215. raise OSError(5) # EIO
  216. # send the data
  217. self.write(_TOKEN_DATA, buf)
  218. else:
  219. # CMD25: set write address for first block
  220. if self.cmd(25, block_num * self.cdv, 0) != 0:
  221. raise OSError(5) # EIO
  222. # send the data
  223. offset = 0
  224. mv = memoryview(buf)
  225. while nblocks:
  226. self.write(_TOKEN_CMD25, mv[offset : offset + 512])
  227. offset += 512
  228. nblocks -= 1
  229. self.write_token(_TOKEN_STOP_TRAN)
  230. def ioctl(self, op, arg):
  231. print('ioctl', op, arg)
  232. if op == 4: # get number of blocks
  233. return self.sectors