changeset 0:765d0ffbb57b

Initial commit
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Mon, 22 Aug 2016 11:55:12 +0200
parents
children 4b538731a207
files sdcard.py
diffstat 1 files changed, 262 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sdcard.py	Mon Aug 22 11:55:12 2016 +0200
@@ -0,0 +1,262 @@
+"""
+Micro Python 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:
+
+    import pyb, sdcard, os
+    sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
+    pyb.mount(sd, '/sd2')
+    os.listdir('/')
+
+"""
+
+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 = b'\xfc'
+_TOKEN_STOP_TRAN = b'\xfd'
+_TOKEN_DATA = b'\xfe'
+
+
+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_card(self):
+        # init CS pin
+        self.cs.init(self.cs.OUT, 1)
+
+        # init SPI bus; use low data rate for initialisation
+        self.spi.init(baudrate=100000, phase=0, polarity=0)
+
+        # 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.spi.init(baudrate=1320000, phase=0, polarity=0)
+
+    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.low()
+
+        # 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 repsonse (response[7] == 0)
+        for i in range(_CMD_TIMEOUT):
+            response = self.spi.read(1, 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.high()
+                    self.spi.write(b'\xff')
+                return response
+
+        # timeout
+        self.cs.high()
+        self.spi.write(b'\xff')
+        return -1
+
+    def cmd_nodata(self, cmd):
+        self.spi.write(bytearray([cmd]))
+        self.spi.read(1, 0xff) # ignore stuff byte
+        for _ in range(_CMD_TIMEOUT):
+            if self.spi.read(1, 0xff)[0] == 0xff:
+                self.cs.high()
+                self.spi.write(b'\xff')
+                return 0    # OK
+        self.cs.high()
+        self.spi.write(b'\xff')
+        return 1 # timeout
+
+    def readinto(self, buf):
+        self.cs.low()
+
+        # read until start byte (0xff)
+        while self.spi.read(1, 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.high()
+        self.spi.write(b'\xff')
+
+    def write(self, token, buf):
+        self.cs.low()
+
+        # send: start of block, data, checksum
+        self.spi.write(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.high()
+            self.spi.write(b'\xff')
+            return
+
+        # wait for write to finish
+        while self.spi.read(1, 0xff)[0] == 0:
+            pass
+
+        self.cs.high()
+        self.spi.write(b'\xff')
+
+    def write_token(self, token):
+        self.cs.low()
+        self.spi.write(token)
+        self.spi.write(b'\xff')
+        # wait for write to finish
+        while self.spi.read(1, 0xff)[0] == 0x00:
+            pass
+
+        self.cs.high()
+        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(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
+
+
+def esp8266_mount():
+    from machine import Pin, SPI
+    import sdcard
+    import uos
+    spi = SPI(-1, miso=Pin(12), mosi=Pin(13), sck=Pin(14))
+    sd = sdcard.SDCard(spi, Pin(15))
+    uos.VfsFat(sd, "")