Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    External SD Card

    Scheduled Pinned Locked Moved MicroPython
    sd card
    22 Posts 6 Posters 8.8k Views 6 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • J Offline
      jmarcelino
      last edited by

      You need to connect :

      P4 -> SD_CMD
      P8 -> SD_DATA
      P23 -> SD_CLK

      If you can post what module you are using we could maybe be more helpful

      E 1 Reply Last reply Reply Quote 2
      • M Offline
        Manju2391
        last edited by

        This post is deleted!
        1 Reply Last reply Reply Quote 0
        • M Offline
          Manju2391
          last edited by

          @manju2391 said in External SD Card:

          I'm using Catalex Micro SD Card adapter which uses SPI pins.
          https://www.google.com.au/search?q=catalex+micro+sd+card+adapter+datasheet&rlz=1C1CHBF_en-GBAU760AU760&tbm=isch&source=iu&pf=m&ictx=1&fir=DHR2Q6L_pvc-M%253A%252CO21LzES_2VLsTM%252C&usg=__XDM0G6olWPNV09RPlFPnxzGgHyo%3D&sa=X&ved=0ahUKEwj3_puTi6HXAhXIFJQKHdpNASMQ9QEIOTAC#imgrc=SAdHIXZFJd1dvM:

          L 1 Reply Last reply Reply Quote 0
          • L Offline
            livius @Manju2391
            last edited by

            @manju2391
            As we talking privately you do not need this module at all
            only direct connection of SD card or simple adapter (without any electronic elements).

            But if you really need to use some SPI based module (I will see need for this only when you need more then one SD card) then try this
            https://github.com/pycom/pycom-micropython-sigfox/tree/master/drivers/sdcard

            think about people first, then computers

            M 1 Reply Last reply Reply Quote 0
            • M Offline
              Manju2391 @livius
              last edited by

              @livius
              Sorry for the late reply was out of town. I still couldn't figure out how spi works on pycom chip. I'm just confused about the pins .

              1 Reply Last reply Reply Quote 0
              • M Offline
                Manju2391
                last edited by

                I got it working with SDIO bus but is it possible over shared SPI bus?

                L 1 Reply Last reply Reply Quote 0
                • L Offline
                  livius @Manju2391
                  last edited by livius

                  @manju2391
                  If you look below you find link

                  But if you really need to use some SPI based module (I will see need for this only when you need more then one SD card) then try this
                  https://github.com/pycom/pycom-micropython-sigfox/tree/master/drivers/sdcard

                  but i haven't used it and cannot say more about it.

                  think about people first, then computers

                  M 1 Reply Last reply Reply Quote 0
                  • M Offline
                    Manju2391 @livius
                    last edited by

                    This post is deleted!
                    1 Reply Last reply Reply Quote 0
                    • M Offline
                      Manju2391
                      last edited by

                      @livius
                      Yes I did import the library and when tried to test it but i'm getting this error

                      import machine, sdcard, os
                      from machine import SPI
                      from machine import Pin

                      spi = SPI(0, mode=SPI.MASTER, baudrate=2000000, polarity=0, phase=0, pins=('P19','P20','P21'))
                      CS = Pin('P10', mode=Pin.OUT, pull=Pin.PULL_UP)
                      sd = sdcard.SDCard(machine.SPI(0), machine.Pin('P22'))

                      Traceback (most recent call last):
                      File "<stdin>", line 9, in <module>
                      File "/flash/lib/sdcard.py", line 54, in init
                      File "/flash/lib/sdcard.py", line 79, in init_card
                      File "/flash/lib/sdcard.py", line 133, in cmd
                      AttributeError: 'Pin' object has no attribute 'low'

                      MicroPython v1.8.6-839-g536c958c on 2017-11-15; LoPy with ESP32
                      Type "help()" for more information.

                      L 1 Reply Last reply Reply Quote 0
                      • L Offline
                        livius @Manju2391
                        last edited by

                        @manju2391
                        you must addapt it to pycom sources
                        instead low() write value(0)

                        think about people first, then computers

                        robert-hhR 1 Reply Last reply Reply Quote 0
                        • robert-hhR Offline
                          robert-hh @livius
                          last edited by

                          @livius And value(1) instead of high().
                          That was a confusing change in the API, which broke a lot of code.

                          M 1 Reply Last reply Reply Quote 0
                          • M Offline
                            Manju2391 @robert-hh
                            last edited by

                            @robert-hh @livius
                            I did change the values according to pycom sources but still its not liking it. I'm getting this error
                            File "/flash/sd.py", line 9
                            File "/flash/lib/sdcard.py", line 54, in_init_
                            File "/flash/lib/sdcard.py", line 79, in init_card
                            File "/flash/lib/sdcard.py", line 147, in cmd
                            Typeerror: extra positional arguments given

                            robert-hhR 1 Reply Last reply Reply Quote 0
                            • robert-hhR Offline
                              robert-hh @Manju2391
                              last edited by

                              @manju2391 It seems that this library is a little bit outdated. I know that I used this on an esp8266, and it worked. And the version on the micropython.org site seems to be a little bit more recent. (https://github.com/micropython/micropython/tree/master/drivers/sdcard). I can check, but not before the evening, which is in about 8 hours.

                              M 1 Reply Last reply Reply Quote 0
                              • M Offline
                                Manju2391 @robert-hh
                                last edited by

                                @robert-hh
                                Is there any possibility to interface the SD card over shared SPI bus without using any
                                libraries? I have been getting my head around the SPI module from last week and I haven't got it yet. Not a problem @robert-hh you can let me know how it goes after testing

                                robert-hhR 1 Reply Last reply Reply Quote 0
                                • robert-hhR Offline
                                  robert-hh @Manju2391
                                  last edited by robert-hh

                                  @manju2391 I tried that driver, and I got it to the point where it showed some activity. The code is below. The sdcard SPI is connected to:
                                  P9 (SS), P10(Clk), P11(MOSI) and P12 (MISO). It is started with:

                                  import sdcard, machine, os
                                  
                                  sd = sdcard.SDCard(machine.SPI(0), machine.Pin("P9"))
                                  os.mount(sd, "/card")
                                  

                                  But it is very unstable, runs at low speed only, and data read from the card contains the byte sequence "\x7f\x04" here and there. And sometimes lopy simply crashed. So it's nothing really usable at the moment.
                                  I tried that with two different SD card adapters. One with embedded bus drivers, which is like the one you have, and one which is simply wiring. The latter runs well when connected to the genuine SD card pins of the xxPy, using the built-in SD card module.

                                  """
                                  MicroPython driver for SD cards using SPI bus.
                                  
                                  Requires an SPI bus and a CS pin.  Provides readblocks and writeblocks
                                  methods so the device can be mounted as a filesystem.
                                  
                                  Example usage on xxPy:
                                  
                                      import machine, sdcard, os
                                      sd = sdcard.SDCard(machine.SPI(0), machine.Pin("P9"))
                                      os.mount(sd, '/sd2')
                                      os.listdir('/sd2')
                                  
                                  
                                  """
                                  
                                  from micropython import const
                                  import time
                                  
                                  
                                  _CMD_TIMEOUT = const(100)
                                  
                                  _R1_IDLE_STATE = const(1 << 0)
                                  #R1_ERASE_RESET = const(1 << 1)
                                  _R1_ILLEGAL_COMMAND = const(1 << 2)
                                  #R1_COM_CRC_ERROR = const(1 << 3)
                                  #R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
                                  #R1_ADDRESS_ERROR = const(1 << 5)
                                  #R1_PARAMETER_ERROR = const(1 << 6)
                                  _TOKEN_CMD25 = const(0xfc)
                                  _TOKEN_STOP_TRAN = const(0xfd)
                                  _TOKEN_DATA = const(0xfe)
                                  
                                  
                                  class SDCard:
                                      def __init__(self, spi, cs):
                                          self.spi = spi
                                          self.cs = cs
                                  
                                          self.cmdbuf = bytearray(6)
                                          self.dummybuf = bytearray(512)
                                          for i in range(512):
                                              self.dummybuf[i] = 0xff
                                          self.dummybuf_memoryview = memoryview(self.dummybuf)
                                  
                                          # initialise the card
                                          self.init_card()
                                  
                                      def init_spi(self, baudrate):
                                          master = self.spi.MASTER
                                          self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
                                  
                                      def init_card(self):
                                          # init CS pin
                                          self.cs.init(self.cs.OUT, value=1)
                                  
                                          # init SPI bus; use low data rate for initialisation
                                          self.init_spi(100000)
                                  
                                          # clock card at least 100 cycles with cs high
                                          for i in range(16):
                                              self.spi.write(b'\xff')
                                  
                                          # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
                                          for _ in range(5):
                                              if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
                                                  break
                                          else:
                                              raise OSError("no SD card")
                                  
                                          # CMD8: determine card version
                                          r = self.cmd(8, 0x01aa, 0x87, 4)
                                          if r == _R1_IDLE_STATE:
                                              self.init_card_v2()
                                          elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
                                              self.init_card_v1()
                                          else:
                                              raise OSError("couldn't determine SD card version")
                                  
                                          # get the number of sectors
                                          # CMD9: response R2 (R1 byte + 16-byte block read)
                                          if self.cmd(9, 0, 0, 0, False) != 0:
                                              raise OSError("no response from SD card")
                                          csd = bytearray(16)
                                          self.readinto(csd)
                                          if csd[0] & 0xc0 != 0x40:
                                              raise OSError("SD card CSD format not supported")
                                          self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 2014
                                          #print('sectors', self.sectors)
                                  
                                          # CMD16: set block length to 512 bytes
                                          if self.cmd(16, 512, 0) != 0:
                                              raise OSError("can't set 512 block size")
                                  
                                          # set to high data rate now that it's initialised
                                          #self.init_spi(100000)
                                          #self.init_spi(40000000)
                                  
                                      def init_card_v1(self):
                                          for i in range(_CMD_TIMEOUT):
                                              self.cmd(55, 0, 0)
                                              if self.cmd(41, 0, 0) == 0:
                                                  self.cdv = 512
                                                  #print("[SDCard] v1 card")
                                                  return
                                          raise OSError("timeout waiting for v1 card")
                                  
                                      def init_card_v2(self):
                                          for i in range(_CMD_TIMEOUT):
                                              time.sleep_ms(50)
                                              self.cmd(58, 0, 0, 4)
                                              self.cmd(55, 0, 0)
                                              if self.cmd(41, 0x40000000, 0) == 0:
                                                  self.cmd(58, 0, 0, 4)
                                                  self.cdv = 1
                                                  #print("[SDCard] v2 card")
                                                  return
                                          raise OSError("timeout waiting for v2 card")
                                  
                                      def cmd(self, cmd, arg, crc, final=0, release=True):
                                          self.cs(0)
                                  
                                          # create and send the command
                                          buf = self.cmdbuf
                                          buf[0] = 0x40 | cmd
                                          buf[1] = arg >> 24
                                          buf[2] = arg >> 16
                                          buf[3] = arg >> 8
                                          buf[4] = arg
                                          buf[5] = crc
                                          self.spi.write(buf)
                                  
                                          # wait for the response (response[7] == 0)
                                          for i in range(_CMD_TIMEOUT):
                                              response = self.spi.read(1, write=0xff)[0]
                                              if not (response & 0x80):
                                                  # this could be a big-endian integer that we are getting here
                                                  for j in range(final):
                                                      self.spi.write(b'\xff')
                                                  if release:
                                                      self.cs(1)
                                                      self.spi.write(b'\xff')
                                                  return response
                                  
                                          # timeout
                                          self.cs(1)
                                          self.spi.write(b'\xff')
                                          return -1
                                  
                                      def cmd_nodata(self, cmd):
                                          self.spi.write(cmd)
                                          self.spi.read(1, write=0xff) # ignore stuff byte
                                          for _ in range(_CMD_TIMEOUT):
                                              if self.spi.read(1, write=0xff)[0] == 0xff:
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                                  return 0    # OK
                                          self.cs(1)
                                          self.spi.write(b'\xff')
                                          return 1 # timeout
                                  
                                      def readinto(self, buf):
                                          self.cs(0)
                                  
                                          # read until start byte (0xff)
                                          while self.spi.read(1, write=0xff)[0] != 0xfe:
                                              pass
                                  
                                          # read data
                                          mv = self.dummybuf_memoryview[:len(buf)]
                                          self.spi.write_readinto(mv, buf)
                                  
                                          # read checksum
                                          self.spi.write(b'\xff')
                                          self.spi.write(b'\xff')
                                  
                                          self.cs(1)
                                          self.spi.write(b'\xff')
                                  
                                      def write(self, token, buf):
                                          self.cs(0)
                                  
                                          # send: start of block, data, checksum
                                          self.spi.read(1, token)
                                          self.spi.write(buf)
                                          self.spi.write(b'\xff')
                                          self.spi.write(b'\xff')
                                  
                                          # check the response
                                          if (self.spi.read(1, 0xff)[0] & 0x1f) != 0x05:
                                              self.cs(1)
                                              self.spi.write(b'\xff')
                                              return
                                  
                                          # wait for write to finish
                                          while self.spi.read(1, 0xff)[0] == 0:
                                              pass
                                  
                                          self.cs(1)
                                          self.spi.write(b'\xff')
                                  
                                      def write_token(self, token):
                                          self.cs(0)
                                          self.spi.read(1, token)
                                          self.spi.write(b'\xff')
                                          # wait for write to finish
                                          while self.spi.read(1, 0xff)[0] == 0x00:
                                              pass
                                  
                                          self.cs(1)
                                          self.spi.write(b'\xff')
                                  
                                      def count(self):
                                          return self.sectors
                                  
                                      def readblocks(self, block_num, buf):
                                          nblocks, err = divmod(len(buf), 512)
                                          assert nblocks and not err, 'Buffer length is invalid'
                                          if nblocks == 1:
                                              # CMD17: set read address for single block
                                              if self.cmd(17, block_num * self.cdv, 0) != 0:
                                                  return 1
                                              # receive the data
                                              self.readinto(buf)
                                          else:
                                              # CMD18: set read address for multiple blocks
                                              if self.cmd(18, block_num * self.cdv, 0) != 0:
                                                  return 1
                                              offset = 0
                                              mv = memoryview(buf)
                                              while nblocks:
                                                  self.readinto(mv[offset : offset + 512])
                                                  offset += 512
                                                  nblocks -= 1
                                              return self.cmd_nodata(b'\x0c') # cmd 12
                                          return 0
                                  
                                      def writeblocks(self, block_num, buf):
                                          nblocks, err = divmod(len(buf), 512)
                                          assert nblocks and not err, 'Buffer length is invalid'
                                          if nblocks == 1:
                                              # CMD24: set write address for single block
                                              if self.cmd(24, block_num * self.cdv, 0) != 0:
                                                  return 1
                                  
                                              # send the data
                                              self.write(_TOKEN_DATA, buf)
                                          else:
                                              # CMD25: set write address for first block
                                              if self.cmd(25, block_num * self.cdv, 0) != 0:
                                                  return 1
                                              # send the data
                                              offset = 0
                                              mv = memoryview(buf)
                                              while nblocks:
                                                  self.write(_TOKEN_CMD25, mv[offset : offset + 512])
                                                  offset += 512
                                                  nblocks -= 1
                                              self.write_token(_TOKEN_STOP_TRAN)
                                          return 0
                                  
                                  M N 2 Replies Last reply Reply Quote 1
                                  • M Offline
                                    Manju2391 @robert-hh
                                    last edited by

                                    @robert-hh
                                    Thanks for your reply robert and I'll try working on it today and will post if anything crashes up and I have one more doubt regarding the WLAN access point. I have created an access point and I want to connect to my AP whenever the boot.py loads up. I can connect as a station but not as an access point i tried using this syntax but whenever it boots up it's looking for wlan AP but couldn't find it and goes to deep sleep mode. My network is up and it runs when I run it externally but not during boot up.

                                    from network import WLAN

                                    wlan.init(mode=WLAN.STA_AP,ssid='AP')
                                    wlan.init(ssid='AP',auth=(WLAN.WPA2,'xxxx'))
                                    print(wlan.ifconfig())

                                    Is the above syntax right?

                                    robert-hhR 1 Reply Last reply Reply Quote 0
                                    • robert-hhR Offline
                                      robert-hh @Manju2391
                                      last edited by

                                      @manju2391 About WLAN: It could look like:

                                      from network import WLAN
                                      
                                      wlan = WLAN(mode=WLAN.STA_AP)
                                      wlan.connect(ssid='AP',auth=(WLAN.WPA2, 'xxxx'))
                                      # from here on it's optional. It just takes a few moments to connect
                                      for _ in range (200):
                                          if wlan.isconnected():
                                              print(wlan.ifconfig())
                                              break
                                          else:
                                              time.sleep_ms(50)
                                      else:
                                          print("Could not connect")
                                      
                                      M 1 Reply Last reply Reply Quote 0
                                      • M Offline
                                        Manju2391 @robert-hh
                                        last edited by

                                        @robert-hh

                                        I tried using SPI bus it is very slow. I had designed the pcb with spi pins but looks like it is not best so I’m just using the SDIO pins and changed the pcb. About the WLAN when I define wlan initially it is asking me for ap ssid at first I get an error saying ap ssid not given. I get the IP address when run locally and it connects to FileZilla but when it boots for the STA_AP it doesn’t print the IP address nor it connects to WLAN. It will be in idle state. I don’t know why it’s not connecting. I’m wondering should we create a station first and then an AP?

                                        1 Reply Last reply Reply Quote 0
                                        • N Offline
                                          nathanh @robert-hh
                                          last edited by nathanh

                                          @robert-hh said in External SD Card:

                                          """
                                          MicroPython driver for SD cards using SPI bus.
                                          
                                          Requires an SPI bus and a CS pin.  Provides readblocks and writeblocks
                                          methods so the device can be mounted as a filesystem.
                                          
                                          Example usage on xxPy:
                                          
                                              import machine, sdcard, os
                                              sd = sdcard.SDCard(machine.SPI(0), machine.Pin("P9"))
                                              os.mount(sd, '/sd2')
                                              os.listdir('/sd2')
                                          
                                          
                                          """
                                          
                                          from micropython import const
                                          import time
                                          
                                          
                                          _CMD_TIMEOUT = const(100)
                                          
                                          _R1_IDLE_STATE = const(1 << 0)
                                          #R1_ERASE_RESET = const(1 << 1)
                                          _R1_ILLEGAL_COMMAND = const(1 << 2)
                                          #R1_COM_CRC_ERROR = const(1 << 3)
                                          #R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
                                          #R1_ADDRESS_ERROR = const(1 << 5)
                                          #R1_PARAMETER_ERROR = const(1 << 6)
                                          _TOKEN_CMD25 = const(0xfc)
                                          _TOKEN_STOP_TRAN = const(0xfd)
                                          _TOKEN_DATA = const(0xfe)
                                          
                                          
                                          class SDCard:
                                              def __init__(self, spi, cs):
                                                  self.spi = spi
                                                  self.cs = cs
                                          
                                                  self.cmdbuf = bytearray(6)
                                                  self.dummybuf = bytearray(512)
                                                  for i in range(512):
                                                      self.dummybuf[i] = 0xff
                                                  self.dummybuf_memoryview = memoryview(self.dummybuf)
                                          
                                                  # initialise the card
                                                  self.init_card()
                                          
                                              def init_spi(self, baudrate):
                                                  master = self.spi.MASTER
                                                  self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
                                          
                                              def init_card(self):
                                                  # init CS pin
                                                  self.cs.init(self.cs.OUT, value=1)
                                          
                                                  # init SPI bus; use low data rate for initialisation
                                                  self.init_spi(100000)
                                          
                                                  # clock card at least 100 cycles with cs high
                                                  for i in range(16):
                                                      self.spi.write(b'\xff')
                                          
                                                  # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
                                                  for _ in range(5):
                                                      if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
                                                          break
                                                  else:
                                                      raise OSError("no SD card")
                                          
                                                  # CMD8: determine card version
                                                  r = self.cmd(8, 0x01aa, 0x87, 4)
                                                  if r == _R1_IDLE_STATE:
                                                      self.init_card_v2()
                                                  elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
                                                      self.init_card_v1()
                                                  else:
                                                      raise OSError("couldn't determine SD card version")
                                          
                                                  # get the number of sectors
                                                  # CMD9: response R2 (R1 byte + 16-byte block read)
                                                  if self.cmd(9, 0, 0, 0, False) != 0:
                                                      raise OSError("no response from SD card")
                                                  csd = bytearray(16)
                                                  self.readinto(csd)
                                                  if csd[0] & 0xc0 != 0x40:
                                                      raise OSError("SD card CSD format not supported")
                                                  self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 2014
                                                  #print('sectors', self.sectors)
                                          
                                                  # CMD16: set block length to 512 bytes
                                                  if self.cmd(16, 512, 0) != 0:
                                                      raise OSError("can't set 512 block size")
                                          
                                                  # set to high data rate now that it's initialised
                                                  #self.init_spi(100000)
                                                  #self.init_spi(40000000)
                                          
                                              def init_card_v1(self):
                                                  for i in range(_CMD_TIMEOUT):
                                                      self.cmd(55, 0, 0)
                                                      if self.cmd(41, 0, 0) == 0:
                                                          self.cdv = 512
                                                          #print("[SDCard] v1 card")
                                                          return
                                                  raise OSError("timeout waiting for v1 card")
                                          
                                              def init_card_v2(self):
                                                  for i in range(_CMD_TIMEOUT):
                                                      time.sleep_ms(50)
                                                      self.cmd(58, 0, 0, 4)
                                                      self.cmd(55, 0, 0)
                                                      if self.cmd(41, 0x40000000, 0) == 0:
                                                          self.cmd(58, 0, 0, 4)
                                                          self.cdv = 1
                                                          #print("[SDCard] v2 card")
                                                          return
                                                  raise OSError("timeout waiting for v2 card")
                                          
                                              def cmd(self, cmd, arg, crc, final=0, release=True):
                                                  self.cs(0)
                                          
                                                  # create and send the command
                                                  buf = self.cmdbuf
                                                  buf[0] = 0x40 | cmd
                                                  buf[1] = arg >> 24
                                                  buf[2] = arg >> 16
                                                  buf[3] = arg >> 8
                                                  buf[4] = arg
                                                  buf[5] = crc
                                                  self.spi.write(buf)
                                          
                                                  # wait for the response (response[7] == 0)
                                                  for i in range(_CMD_TIMEOUT):
                                                      response = self.spi.read(1, write=0xff)[0]
                                                      if not (response & 0x80):
                                                          # this could be a big-endian integer that we are getting here
                                                          for j in range(final):
                                                              self.spi.write(b'\xff')
                                                          if release:
                                                              self.cs(1)
                                                              self.spi.write(b'\xff')
                                                          return response
                                          
                                                  # timeout
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                                  return -1
                                          
                                              def cmd_nodata(self, cmd):
                                                  self.spi.write(cmd)
                                                  self.spi.read(1, write=0xff) # ignore stuff byte
                                                  for _ in range(_CMD_TIMEOUT):
                                                      if self.spi.read(1, write=0xff)[0] == 0xff:
                                                          self.cs(1)
                                                          self.spi.write(b'\xff')
                                                          return 0    # OK
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                                  return 1 # timeout
                                          
                                              def readinto(self, buf):
                                                  self.cs(0)
                                          
                                                  # read until start byte (0xff)
                                                  while self.spi.read(1, write=0xff)[0] != 0xfe:
                                                      pass
                                          
                                                  # read data
                                                  mv = self.dummybuf_memoryview[:len(buf)]
                                                  self.spi.write_readinto(mv, buf)
                                          
                                                  # read checksum
                                                  self.spi.write(b'\xff')
                                                  self.spi.write(b'\xff')
                                          
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                          
                                              def write(self, token, buf):
                                                  self.cs(0)
                                          
                                                  # send: start of block, data, checksum
                                                  self.spi.read(1, token)
                                                  self.spi.write(buf)
                                                  self.spi.write(b'\xff')
                                                  self.spi.write(b'\xff')
                                          
                                                  # check the response
                                                  if (self.spi.read(1, 0xff)[0] & 0x1f) != 0x05:
                                                      self.cs(1)
                                                      self.spi.write(b'\xff')
                                                      return
                                          
                                                  # wait for write to finish
                                                  while self.spi.read(1, 0xff)[0] == 0:
                                                      pass
                                          
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                          
                                              def write_token(self, token):
                                                  self.cs(0)
                                                  self.spi.read(1, token)
                                                  self.spi.write(b'\xff')
                                                  # wait for write to finish
                                                  while self.spi.read(1, 0xff)[0] == 0x00:
                                                      pass
                                          
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                          
                                              def count(self):
                                                  return self.sectors
                                          
                                              def readblocks(self, block_num, buf):
                                                  nblocks, err = divmod(len(buf), 512)
                                                  assert nblocks and not err, 'Buffer length is invalid'
                                                  if nblocks == 1:
                                                      # CMD17: set read address for single block
                                                      if self.cmd(17, block_num * self.cdv, 0) != 0:
                                                          return 1
                                                      # receive the data
                                                      self.readinto(buf)
                                                  else:
                                                      # CMD18: set read address for multiple blocks
                                                      if self.cmd(18, block_num * self.cdv, 0) != 0:
                                                          return 1
                                                      offset = 0
                                                      mv = memoryview(buf)
                                                      while nblocks:
                                                          self.readinto(mv[offset : offset + 512])
                                                          offset += 512
                                                          nblocks -= 1
                                                      return self.cmd_nodata(b'\x0c') # cmd 12
                                                  return 0
                                          
                                              def writeblocks(self, block_num, buf):
                                                  nblocks, err = divmod(len(buf), 512)
                                                  assert nblocks and not err, 'Buffer length is invalid'
                                                  if nblocks == 1:
                                                      # CMD24: set write address for single block
                                                      if self.cmd(24, block_num * self.cdv, 0) != 0:
                                                          return 1
                                          
                                                      # send the data
                                                      self.write(_TOKEN_DATA, buf)
                                                  else:
                                                      # CMD25: set write address for first block
                                                      if self.cmd(25, block_num * self.cdv, 0) != 0:
                                                          return 1
                                                      # send the data
                                                      offset = 0
                                                      mv = memoryview(buf)
                                                      while nblocks:
                                                          self.write(_TOKEN_CMD25, mv[offset : offset + 512])
                                                          offset += 512
                                                          nblocks -= 1
                                                      self.write_token(_TOKEN_STOP_TRAN)
                                                  return 0
                                          

                                          I know this is a bit old but I just got an SD card module (https://www.sparkfun.com/products/13743) and was trying to get it working with my FiPy. I was running into a lot of issues but was able to make some headway using the library you pasted @robert-hh and thought I would share my results in case other people wanted to get it working.

                                          However, I did have some errors with the library and had to make the following changes:

                                          • The pins you used are not the default pins for the SPI init call. So I created my own SPI instance with the pin allocation you had in the post (since I already had them like that) and passed that instance into the SDCard constructor rather than machine.SPI(0). I also removed the additional spi init calls.

                                          • I had to set the SPI baudrate to 1000000 instead of 100000

                                          • I had to declare the CS pin as an instance outside of the library and pass it in similar to the SPI above.

                                          • The command to check idle state at the start of the library self.cmd(0, 0, 0x95) would never return a value of 1. I don't know where the reference of these commands were but testing the other commands manually all returned values as expected. So I commented out the entire block that checks for the SD card or raises the 'no SD card' error. I found that I still need to run this command regardless, I get the same set of (4?) different values that repeat each time it is called. They never equal 1 but I let the code continue and command out the error block. Not calling the 0x95 command will not allow the next set of command to work correctly.

                                          Doing the above I have gotten my external SD card board working fine with the FiPy. For reference below are my library and how I call it.

                                          """
                                          MicroPython driver for SD cards using SPI bus.
                                          
                                          Requires an SPI bus and a CS pin.  Provides readblocks and writeblocks
                                          methods so the device can be mounted as a filesystem.
                                          
                                          Example usage on xxPy:
                                          
                                              import machine, sdcard, os
                                              sd = sdcard.SDCard(machine.SPI(0), machine.Pin("P9"))
                                              os.mount(sd, '/sd2')
                                              os.listdir('/sd2')
                                          
                                          
                                          """
                                          
                                          from micropython import const
                                          import time
                                          
                                          
                                          _CMD_TIMEOUT = const(100)
                                          
                                          _R1_IDLE_STATE = const(1 << 0)
                                          #R1_ERASE_RESET = const(1 << 1)
                                          _R1_ILLEGAL_COMMAND = const(1 << 2)
                                          #R1_COM_CRC_ERROR = const(1 << 3)
                                          #R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
                                          #R1_ADDRESS_ERROR = const(1 << 5)
                                          #R1_PARAMETER_ERROR = const(1 << 6)
                                          _TOKEN_CMD25 = const(0xfc)
                                          _TOKEN_STOP_TRAN = const(0xfd)
                                          _TOKEN_DATA = const(0xfe)
                                          
                                          
                                          class SDCard:
                                              def __init__(self, spi, cs):
                                                  self.spi = spi
                                                  self.cs = cs
                                          
                                                  self.cmdbuf = bytearray(6)
                                                  self.dummybuf = bytearray(512)
                                                  for i in range(512):
                                                      self.dummybuf[i] = 0xff
                                                  self.dummybuf_memoryview = memoryview(self.dummybuf)
                                          
                                                  # initialise the card
                                                  self.init_card()
                                          
                                              def init_spi(self, baudrate):
                                                  master = self.spi.MASTER
                                                  self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
                                          
                                              def init_card(self):
                                                  # init CS pin        
                                                  #self.cs.init(self.cs.OUT, value=0)
                                          
                                                  # init SPI bus; use low data rate for initialisation
                                                  #self.init_spi(100000)
                                          
                                                  # clock card at least 100 cycles with cs high
                                                  for i in range(16):
                                                      self.spi.write(b'\xff')
                                          
                                                  # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
                                                  
                                                  for _ in range(5):
                                                      if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
                                                          break
                                                  '''
                                                  else:
                                                      raise OSError("no SD card")
                                                  '''
                                          
                                                  # CMD8: determine card version
                                                  r = self.cmd(8, 0x01aa, 0x87, 4)
                                                  if r == _R1_IDLE_STATE:
                                                      self.init_card_v2()
                                                  elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
                                                      self.init_card_v1()
                                                  else:
                                                      raise OSError("couldn't determine SD card version")
                                          
                                                  # get the number of sectors
                                                  # CMD9: response R2 (R1 byte + 16-byte block read)
                                                  if self.cmd(9, 0, 0, 0, False) != 0:
                                                      raise OSError("no response from SD card")
                                                  csd = bytearray(16)
                                                  self.readinto(csd)
                                                  if csd[0] & 0xc0 != 0x40:
                                                      raise OSError("SD card CSD format not supported")
                                                  self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 2014
                                                  #print('sectors', self.sectors)
                                          
                                                  # CMD16: set block length to 512 bytes
                                                  if self.cmd(16, 512, 0) != 0:
                                                      raise OSError("can't set 512 block size")
                                          
                                                  # set to high data rate now that it's initialised
                                                  #self.init_spi(100000)
                                                  #self.init_spi(40000000)
                                          
                                              def init_card_v1(self):
                                                  for i in range(_CMD_TIMEOUT):
                                                      self.cmd(55, 0, 0)
                                                      if self.cmd(41, 0, 0) == 0:
                                                          self.cdv = 512
                                                          #print("[SDCard] v1 card")
                                                          return
                                                  raise OSError("timeout waiting for v1 card")
                                          
                                              def init_card_v2(self):
                                                  for i in range(_CMD_TIMEOUT):
                                                      time.sleep_ms(50)
                                                      self.cmd(58, 0, 0, 4)
                                                      self.cmd(55, 0, 0)
                                                      if self.cmd(41, 0x40000000, 0) == 0:
                                                          self.cmd(58, 0, 0, 4)
                                                          self.cdv = 1
                                                          #print("[SDCard] v2 card")
                                                          return
                                                  raise OSError("timeout waiting for v2 card")
                                          
                                              def cmd(self, cmd, arg, crc, final=0, release=True):
                                                  self.cs(0)
                                          
                                                  # create and send the command
                                                  buf = self.cmdbuf
                                                  buf[0] = 0x40 | cmd
                                                  buf[1] = arg >> 24
                                                  buf[2] = arg >> 16
                                                  buf[3] = arg >> 8
                                                  buf[4] = arg
                                                  buf[5] = crc
                                                  self.spi.write(buf)
                                          
                                                  # wait for the response (response[7] == 0)
                                                  for i in range(_CMD_TIMEOUT):
                                                      response = self.spi.read(1, write=0xff)[0]           
                                                      if not (response & 0x80):                
                                                          # this could be a big-endian integer that we are getting here
                                                          for j in range(final):
                                                              self.spi.write(b'\xff')
                                                          if release:
                                                              self.cs(1)
                                                              self.spi.write(b'\xff')
                                                          return response
                                          
                                                  # timeout
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                                  return -1
                                          
                                              def cmd_nodata(self, cmd):
                                                  self.spi.write(cmd)
                                                  self.spi.read(1, write=0xff) # ignore stuff byte
                                                  for _ in range(_CMD_TIMEOUT):
                                                      if self.spi.read(1, write=0xff)[0] == 0xff:
                                                          self.cs(1)
                                                          self.spi.write(b'\xff')
                                                          return 0    # OK
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                                  return 1 # timeout
                                          
                                              def readinto(self, buf):
                                                  self.cs(0)
                                          
                                                  # read until start byte (0xff)
                                                  while self.spi.read(1, write=0xff)[0] != 0xfe:
                                                      pass
                                          
                                                  # read data
                                                  mv = self.dummybuf_memoryview[:len(buf)]
                                                  self.spi.write_readinto(mv, buf)
                                          
                                                  # read checksum
                                                  self.spi.write(b'\xff')
                                                  self.spi.write(b'\xff')
                                          
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                          
                                              def write(self, token, buf):
                                                  self.cs(0)
                                          
                                                  # send: start of block, data, checksum
                                                  self.spi.read(1, token)
                                                  self.spi.write(buf)
                                                  self.spi.write(b'\xff')
                                                  self.spi.write(b'\xff')
                                          
                                                  # check the response
                                                  if (self.spi.read(1, 0xff)[0] & 0x1f) != 0x05:
                                                      self.cs(1)
                                                      self.spi.write(b'\xff')
                                                      return
                                          
                                                  # wait for write to finish
                                                  while self.spi.read(1, 0xff)[0] == 0:
                                                      pass
                                          
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                          
                                              def write_token(self, token):
                                                  self.cs(0)
                                                  self.spi.read(1, token)
                                                  self.spi.write(b'\xff')
                                                  # wait for write to finish
                                                  while self.spi.read(1, 0xff)[0] == 0x00:
                                                      pass
                                          
                                                  self.cs(1)
                                                  self.spi.write(b'\xff')
                                          
                                              def count(self):
                                                  return self.sectors
                                          
                                              def readblocks(self, block_num, buf):
                                                  nblocks, err = divmod(len(buf), 512)
                                                  assert nblocks and not err, 'Buffer length is invalid'
                                                  if nblocks == 1:
                                                      # CMD17: set read address for single block
                                                      if self.cmd(17, block_num * self.cdv, 0) != 0:
                                                          return 1
                                                      # receive the data
                                                      self.readinto(buf)
                                                  else:
                                                      # CMD18: set read address for multiple blocks
                                                      if self.cmd(18, block_num * self.cdv, 0) != 0:
                                                          return 1
                                                      offset = 0
                                                      mv = memoryview(buf)
                                                      while nblocks:
                                                          self.readinto(mv[offset : offset + 512])
                                                          offset += 512
                                                          nblocks -= 1
                                                      return self.cmd_nodata(b'\x0c') # cmd 12
                                                  return 0
                                          
                                              def writeblocks(self, block_num, buf):
                                                  nblocks, err = divmod(len(buf), 512)
                                                  assert nblocks and not err, 'Buffer length is invalid'
                                                  if nblocks == 1:
                                                      # CMD24: set write address for single block
                                                      if self.cmd(24, block_num * self.cdv, 0) != 0:
                                                          return 1
                                          
                                                      # send the data
                                                      self.write(_TOKEN_DATA, buf)
                                                  else:
                                                      # CMD25: set write address for first block
                                                      if self.cmd(25, block_num * self.cdv, 0) != 0:
                                                          return 1
                                                      # send the data
                                                      offset = 0
                                                      mv = memoryview(buf)
                                                      while nblocks:
                                                          self.write(_TOKEN_CMD25, mv[offset : offset + 512])
                                                          offset += 512
                                                          nblocks -= 1
                                                      self.write_token(_TOKEN_STOP_TRAN)
                                                  return 0
                                          
                                          from machine import SPI
                                          from machine import Pin
                                          import machine, sdcard, os
                                          spi = SPI(0, mode=SPI.MASTER, baudrate=1000000, polarity=0, phase=0, pins=('P10','P11','P12'))
                                          cs = Pin('P9', Pin.OUT)
                                          sd = sdcard.SDCard(spi, cs)
                                          os.mount(sd, "/card")
                                          

                                          I could probably tidy up the library to initialise the SPI and CS properly internally but this got me going at least.

                                          1 Reply Last reply Reply Quote 1
                                          • E Offline
                                            Edward Dimmack @jmarcelino
                                            last edited by

                                            @jmarcelino Would this wiring work?

                                            0_1549934788017_aba064d6-d4e9-4383-86bc-da3e0aafb20a-image.png

                                            robert-hhR 1 Reply Last reply Reply Quote 0

                                            Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                                            Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                                            With your input, this post could be even better 💗

                                            Register Login
                                            • 1
                                            • 2
                                            • 1 / 2
                                            • First post
                                              Last post

                                            Pycom on Twitter

                                            Pycom