<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards)]]></title><description><![CDATA[<p dir="auto">I've had many problems with Pycom boards, mostly LTE-radio-related. I recently solved one issue which I share here for the benefit of others</p>
<p dir="auto">No serious application would employ the overwrought, power-consumptive Pycom expansion boards, which contain SDIO-based SD card modules. An external SPI-based SD card module would be used in any serious application for low-cost, power-efficient persistent storage.</p>
<p dir="auto">This is currently impossible, since Pycom has:</p>
<ol>
<li><strong>Failed</strong> to correct the SPI bug noted here: <a href="https://forum.pycom.io/topic/1260/spi-bug/2">https://forum.pycom.io/topic/1260/spi-bug/2</a></li>
<li><strong>Failed</strong> to update <a href="http://sdcard.py" target="_blank" rel="noopener noreferrer nofollow">sdcard.py</a> to comport with Pycom</li>
<li><strong>Failed</strong> to provide any resolution in the following thread which would enable someone to actually get an SD card working: <a href="https://forum.pycom.io/topic/2064/external-sd-card">https://forum.pycom.io/topic/2064/external-sd-card</a></li>
</ol>
<p dir="auto">I can't believe I'm the first one to post a solution to these issues. I like the direction of the Pycom products and would love to keep using them, but there are lot of basic problems like this which need to be solved.</p>
<p dir="auto">The attached <a href="http://sdcard.py" target="_blank" rel="noopener noreferrer nofollow">sdcard.py</a> is edited to run on Pycom boards (most of that was noted in the thread above) AND contains a proxy SPI &quot;read&quot; function that actually obeys the write=argument. <strong>That Pycom's default SPI library is still broken in November 2018 is beyond my comprehension.</strong> (See this thread: <a href="https://forum.pycom.io/post/23519">https://forum.pycom.io/post/23519</a>)</p>
<p dir="auto"><a href="/assets/uploads/files/1542239004655-sdcard.py">0_1542239004189_sdcard.py</a></p>
<pre><code>
&quot;&quot;&quot;
EDITED November 2018 by Paul Maravelias
The original library fails for multiple reasons:
1. Incompatible syntax (low()/high() instead of value(0/1) and positional argument errors)
2. Pycom SPI bug (spi.read() IGNORES the write= argument)
I overcame the second issue by reimplementind spi.read() using only spi.write_readinto
Tested working on GPy + SparkFun microSD breakout module on SPI(0)
&quot;&quot;&quot;
&quot;&quot;&quot;
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(&quot;P9&quot;))
    os.mount(sd, '/sd2')
    os.listdir('/sd2')


&quot;&quot;&quot;
from micropython import const
import time


_CMD_TIMEOUT = const(100)

_R1_IDLE_STATE = const(1 &lt;&lt; 0)
#R1_ERASE_RESET = const(1 &lt;&lt; 1)
_R1_ILLEGAL_COMMAND = const(1 &lt;&lt; 2)
#R1_COM_CRC_ERROR = const(1 &lt;&lt; 3)
#R1_ERASE_SEQUENCE_ERROR = const(1 &lt;&lt; 4)
#R1_ADDRESS_ERROR = const(1 &lt;&lt; 5)
#R1_PARAMETER_ERROR = const(1 &lt;&lt; 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 spiread(self, nbytes, write=0x00):
        wb = bytearray()
        wb.extend(bytearray([write]*nbytes))
        rb = bytearray(len(wb))
        self.spi.write_readinto(wb,rb)
        return rb

    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(&quot;no SD card&quot;)
        '''

        # 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(&quot;couldn't determine SD card version&quot;)

        # 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(&quot;no response from SD card&quot;)
        csd = bytearray(16)
        self.readinto(csd)
        if csd[0] &amp; 0xc0 != 0x40:
            raise OSError(&quot;SD card CSD format not supported&quot;)
        self.sectors = ((csd[8] &lt;&lt; 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(&quot;can't set 512 block size&quot;)

        # 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(&quot;[SDCard] v1 card&quot;)
                return
        raise OSError(&quot;timeout waiting for v1 card&quot;)

    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(&quot;[SDCard] v2 card&quot;)
                return
        raise OSError(&quot;timeout waiting for v2 card&quot;)

    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 &gt;&gt; 24
        buf[2] = arg &gt;&gt; 16
        buf[3] = arg &gt;&gt; 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]
            response = self.spiread(1, write=0xff)[0]

            if not (response &amp; 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
        self.spiread(1, write=0xff) # ignore stuff byte
        for _ in range(_CMD_TIMEOUT):
            #if self.spi.read(1, write=0xff)[0] == 0xff:
            if self.spiread(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:
        while self.spiread(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, write=token)
        self.spiread(1, write=token)
        self.spi.write(buf)
        self.spi.write(b'\xff')
        self.spi.write(b'\xff')

        # check the response
        #if (self.spi.read(1, write=0xff)[0] &amp; 0x1f) != 0x05:
        if (self.spiread(1, write=0xff)[0] &amp; 0x1f) != 0x05:
            self.cs(1)
            self.spi.write(b'\xff')
            return

        # wait for write to finish
        #while self.spi.read(1, write=0xff)[0] == 0:
        while self.spiread(1, write=0xff)[0] == 0:
            pass

        self.cs(1)
        self.spi.write(b'\xff')

    def write_token(self, token):
        self.cs(0)
        #self.spi.read(1, write=token)
        self.spiread(1, write=token)
        self.spi.write(b'\xff')
        # wait for write to finish
        #while self.spi.read(1, write=0xff)[0] == 0x00:
        while self.spiread(1, write=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</code></pre>
]]></description><link>https://forum.pycom.io/topic/4000/fixed-broken-sdcard-py-library-previously-impossible-to-have-external-spi-usd-card-with-pycom-boards</link><generator>RSS for Node</generator><lastBuildDate>Wed, 22 Apr 2026 17:36:14 GMT</lastBuildDate><atom:link href="https://forum.pycom.io/topic/4000.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 14 Nov 2018 23:43:53 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards) on Wed, 14 Nov 2018 23:51:14 GMT]]></title><description><![CDATA[<p dir="auto">I've had many problems with Pycom boards, mostly LTE-radio-related. I recently solved one issue which I share here for the benefit of others</p>
<p dir="auto">No serious application would employ the overwrought, power-consumptive Pycom expansion boards, which contain SDIO-based SD card modules. An external SPI-based SD card module would be used in any serious application for low-cost, power-efficient persistent storage.</p>
<p dir="auto">This is currently impossible, since Pycom has:</p>
<ol>
<li><strong>Failed</strong> to correct the SPI bug noted here: <a href="https://forum.pycom.io/topic/1260/spi-bug/2">https://forum.pycom.io/topic/1260/spi-bug/2</a></li>
<li><strong>Failed</strong> to update <a href="http://sdcard.py" target="_blank" rel="noopener noreferrer nofollow">sdcard.py</a> to comport with Pycom</li>
<li><strong>Failed</strong> to provide any resolution in the following thread which would enable someone to actually get an SD card working: <a href="https://forum.pycom.io/topic/2064/external-sd-card">https://forum.pycom.io/topic/2064/external-sd-card</a></li>
</ol>
<p dir="auto">I can't believe I'm the first one to post a solution to these issues. I like the direction of the Pycom products and would love to keep using them, but there are lot of basic problems like this which need to be solved.</p>
<p dir="auto">The attached <a href="http://sdcard.py" target="_blank" rel="noopener noreferrer nofollow">sdcard.py</a> is edited to run on Pycom boards (most of that was noted in the thread above) AND contains a proxy SPI &quot;read&quot; function that actually obeys the write=argument. <strong>That Pycom's default SPI library is still broken in November 2018 is beyond my comprehension.</strong> (See this thread: <a href="https://forum.pycom.io/post/23519">https://forum.pycom.io/post/23519</a>)</p>
<p dir="auto"><a href="/assets/uploads/files/1542239004655-sdcard.py">0_1542239004189_sdcard.py</a></p>
<pre><code>
&quot;&quot;&quot;
EDITED November 2018 by Paul Maravelias
The original library fails for multiple reasons:
1. Incompatible syntax (low()/high() instead of value(0/1) and positional argument errors)
2. Pycom SPI bug (spi.read() IGNORES the write= argument)
I overcame the second issue by reimplementind spi.read() using only spi.write_readinto
Tested working on GPy + SparkFun microSD breakout module on SPI(0)
&quot;&quot;&quot;
&quot;&quot;&quot;
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(&quot;P9&quot;))
    os.mount(sd, '/sd2')
    os.listdir('/sd2')


&quot;&quot;&quot;
from micropython import const
import time


_CMD_TIMEOUT = const(100)

_R1_IDLE_STATE = const(1 &lt;&lt; 0)
#R1_ERASE_RESET = const(1 &lt;&lt; 1)
_R1_ILLEGAL_COMMAND = const(1 &lt;&lt; 2)
#R1_COM_CRC_ERROR = const(1 &lt;&lt; 3)
#R1_ERASE_SEQUENCE_ERROR = const(1 &lt;&lt; 4)
#R1_ADDRESS_ERROR = const(1 &lt;&lt; 5)
#R1_PARAMETER_ERROR = const(1 &lt;&lt; 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 spiread(self, nbytes, write=0x00):
        wb = bytearray()
        wb.extend(bytearray([write]*nbytes))
        rb = bytearray(len(wb))
        self.spi.write_readinto(wb,rb)
        return rb

    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(&quot;no SD card&quot;)
        '''

        # 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(&quot;couldn't determine SD card version&quot;)

        # 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(&quot;no response from SD card&quot;)
        csd = bytearray(16)
        self.readinto(csd)
        if csd[0] &amp; 0xc0 != 0x40:
            raise OSError(&quot;SD card CSD format not supported&quot;)
        self.sectors = ((csd[8] &lt;&lt; 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(&quot;can't set 512 block size&quot;)

        # 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(&quot;[SDCard] v1 card&quot;)
                return
        raise OSError(&quot;timeout waiting for v1 card&quot;)

    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(&quot;[SDCard] v2 card&quot;)
                return
        raise OSError(&quot;timeout waiting for v2 card&quot;)

    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 &gt;&gt; 24
        buf[2] = arg &gt;&gt; 16
        buf[3] = arg &gt;&gt; 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]
            response = self.spiread(1, write=0xff)[0]

            if not (response &amp; 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
        self.spiread(1, write=0xff) # ignore stuff byte
        for _ in range(_CMD_TIMEOUT):
            #if self.spi.read(1, write=0xff)[0] == 0xff:
            if self.spiread(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:
        while self.spiread(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, write=token)
        self.spiread(1, write=token)
        self.spi.write(buf)
        self.spi.write(b'\xff')
        self.spi.write(b'\xff')

        # check the response
        #if (self.spi.read(1, write=0xff)[0] &amp; 0x1f) != 0x05:
        if (self.spiread(1, write=0xff)[0] &amp; 0x1f) != 0x05:
            self.cs(1)
            self.spi.write(b'\xff')
            return

        # wait for write to finish
        #while self.spi.read(1, write=0xff)[0] == 0:
        while self.spiread(1, write=0xff)[0] == 0:
            pass

        self.cs(1)
        self.spi.write(b'\xff')

    def write_token(self, token):
        self.cs(0)
        #self.spi.read(1, write=token)
        self.spiread(1, write=token)
        self.spi.write(b'\xff')
        # wait for write to finish
        #while self.spi.read(1, write=0xff)[0] == 0x00:
        while self.spiread(1, write=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</code></pre>
]]></description><link>https://forum.pycom.io/post/23522</link><guid isPermaLink="true">https://forum.pycom.io/post/23522</guid><dc:creator><![CDATA[PaulM]]></dc:creator><pubDate>Wed, 14 Nov 2018 23:51:14 GMT</pubDate></item><item><title><![CDATA[Reply to Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards) on Thu, 15 Nov 2018 06:32:41 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://forum.pycom.io/uid/2766">@paulm</a> The driver you mentions was just a left-over from the <a href="http://micropython.org" target="_blank" rel="noopener noreferrer nofollow">micropython.org</a> repository, which was used as a base for Pycom's variant of MicroPython. There is a LOT of stuff lying around in the repository which is not needed by Pycom and may confuse people, obvious stuff like STM32 or ESP8266 ports, and less obvious things like the driver you adapted.<br />
I suggested already to clean up the repository, but no-one seems to have or take time for that. Tidying things up seems not to be the most attractive task. But, during tidying this driver would have been removed.</p>
]]></description><link>https://forum.pycom.io/post/23528</link><guid isPermaLink="true">https://forum.pycom.io/post/23528</guid><dc:creator><![CDATA[robert-hh]]></dc:creator><pubDate>Thu, 15 Nov 2018 06:32:41 GMT</pubDate></item><item><title><![CDATA[Reply to Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards) on Thu, 15 Nov 2018 20:19:46 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://forum.pycom.io/uid/98">@robert-hh</a> Thanks for the background info. All I know is this is necessary for using external SPI SD cards, and it would not work by following solely the instructions in the thread I linked above.</p>
]]></description><link>https://forum.pycom.io/post/23576</link><guid isPermaLink="true">https://forum.pycom.io/post/23576</guid><dc:creator><![CDATA[PaulM]]></dc:creator><pubDate>Thu, 15 Nov 2018 20:19:46 GMT</pubDate></item><item><title><![CDATA[Reply to Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards) on Wed, 19 Dec 2018 02:22:21 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://forum.pycom.io/uid/2766">@paulm</a>   - very glad to find this because the combination of SD Card, SPI and GPY has had me going around in circles trying to get a workable solution.<br />
Can you advise what pins you used for the SPI (default SPI)?</p>
<p dir="auto">On the GPY/Expansion board these aren't very clear to me (noobie) but seem to be from these:</p>
<p dir="auto">P4    =  SD CMD     = ?<br />
P8    =  SD DAT0    = SPI<br />
P9    = SDA             = MOSI  = ?<br />
P14 = MISO           = ?<br />
P23 = SD CLK       = ?         = ?</p>
<p dir="auto">Getting the right pins is part of my problems in getting an external SD Card to work. I'd really like to clarify the pins - a lot of posts here assume the OP knows all the in's and outs but the Pycom documentation has a few holes in  it I think, which is compounded by old, incorrect information</p>
<p dir="auto">Stevo</p>
]]></description><link>https://forum.pycom.io/post/24368</link><guid isPermaLink="true">https://forum.pycom.io/post/24368</guid><dc:creator><![CDATA[Stevo52]]></dc:creator><pubDate>Wed, 19 Dec 2018 02:22:21 GMT</pubDate></item><item><title><![CDATA[Reply to Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards) on Wed, 02 Jan 2019 11:35:43 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://forum.pycom.io/uid/3193">@stevo52</a> Glad I could be of help. I am using the following for pin config on a new GPy. I can understand how getting this working would drive someone crazy, since Pycom has still failed to fix the SPI library:<br />
spi = SPI(0, mode=SPI.MASTER, baudrate=1000000, polarity=0, phase=0, pins=('P10','P11','P8'))<br />
sd = sdcard.SDCard(spi,Pin('P9', Pin.OUT))</p>
]]></description><link>https://forum.pycom.io/post/24578</link><guid isPermaLink="true">https://forum.pycom.io/post/24578</guid><dc:creator><![CDATA[PaulM]]></dc:creator><pubDate>Wed, 02 Jan 2019 11:35:43 GMT</pubDate></item><item><title><![CDATA[Reply to Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards) on Thu, 03 Jan 2019 08:08:34 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://forum.pycom.io/uid/2766">@paulm</a>  Thanks for the reply. It took a while but I think I have it sorted now, using 'P10', 'P11' ,'P14' and 'P9' for CS. I can on;y get the SPI interface to work at &gt; 10000000 though but that works reliably at least.</p>
<p dir="auto">This forum is not the easiest to work with - I am currently trying to post a query about i2c but get tols I need to 'add a tag' but I can't find out how to do that..  I  am sure it isn't hard, but again prior knowledge is 'assumed'..</p>
]]></description><link>https://forum.pycom.io/post/24602</link><guid isPermaLink="true">https://forum.pycom.io/post/24602</guid><dc:creator><![CDATA[Stevo52]]></dc:creator><pubDate>Thu, 03 Jan 2019 08:08:34 GMT</pubDate></item><item><title><![CDATA[Reply to Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards) on Thu, 03 Jan 2019 08:17:41 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://forum.pycom.io/uid/3193">@stevo52</a> For tags: At the bottom line where it say &quot;enter tag&quot;, just type the words that you like to tag the post with,</p>
]]></description><link>https://forum.pycom.io/post/24603</link><guid isPermaLink="true">https://forum.pycom.io/post/24603</guid><dc:creator><![CDATA[robert-hh]]></dc:creator><pubDate>Thu, 03 Jan 2019 08:17:41 GMT</pubDate></item><item><title><![CDATA[Reply to Fixed broken sdcard.py library (previously impossible to have external SPI uSD card with Pycom boards) on Thu, 03 Jan 2019 08:31:23 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://forum.pycom.io/uid/98">@robert-hh</a>  Thanks, I am working on a very small laptop so I didn't see that as it wasn't very obvious  :-)</p>
]]></description><link>https://forum.pycom.io/post/24604</link><guid isPermaLink="true">https://forum.pycom.io/post/24604</guid><dc:creator><![CDATA[Stevo52]]></dc:creator><pubDate>Thu, 03 Jan 2019 08:31:23 GMT</pubDate></item></channel></rss>