Content-Length: 26012 | pFad | http://github.com/micropython/micropython-lib/pull/765.patch
thub.com
From aecced4fd90afdd2b0e73855b56a31c57bc02c60 Mon Sep 17 00:00:00 2001
From: Marcus Mendenhall
Date: Thu, 21 Dec 2023 10:11:53 -0500
Subject: [PATCH] drivers/sdcard/sdcard.py: Fixes per PR discussion.
Merged crc7.py into sdcard.py, fixed many little things per suggestions.
Removed hardwired crcs from cmd, always recompute.
Signed-off-by: Marcus Mendenhall
---
micropython/drivers/storage/sdcard/crc16.py | 111 +++++
.../drivers/storage/sdcard/manifest.py | 2 +-
.../storage/sdcard/sd_card_spi_test.py | 101 +++++
micropython/drivers/storage/sdcard/sdcard.py | 378 ++++++++++--------
4 files changed, 421 insertions(+), 171 deletions(-)
create mode 100644 micropython/drivers/storage/sdcard/crc16.py
create mode 100644 micropython/drivers/storage/sdcard/sd_card_spi_test.py
diff --git a/micropython/drivers/storage/sdcard/crc16.py b/micropython/drivers/storage/sdcard/crc16.py
new file mode 100644
index 000000000..7ad799561
--- /dev/null
+++ b/micropython/drivers/storage/sdcard/crc16.py
@@ -0,0 +1,111 @@
+import micropython
+from uctypes import addressof
+
+# ruff: noqa: F821 - @asm_thumb and @viper decorator adds names to function scope
+
+# https://electronics.stackexchange.com/questions/321304/how-to-use-the-data-crc-of-sd-cards-in-spi-mode
+# for sd bit mode
+import array
+
+_sd_crc16_table = array.array("H", (0 for _ in range(256)))
+# /* Generate CRC16 table */
+# for (byt = 0U; byt < 256U; byt ++){
+# crc = byt << 8;
+# for (bit = 0U; bit < 8U; bit ++){
+# crc <<= 1;
+# if ((crc & 0x10000U) != 0U){ crc ^= 0x1021U; }
+# }
+# sd_crc16_table[byt] = (crc & 0xFFFFU);
+# }
+for byt in range(256):
+ crc = byt << 8
+ for bit in range(8):
+ crc = crc << 1
+ if (crc & 0x10000) != 0:
+ crc ^= 0x1021
+ _sd_crc16_table[byt] = crc & 0xFFFF
+
+
+# /* Running CRC16 calculation for a byte. */
+# static unsigned int sd_crc16_byte(unsigned int crcval, unsigned int byte)
+# {
+# return (sd_crc16_table[(byte ^ (crcval >> 8)) & 0xFFU] ^ (crcval << 8)) & 0xFFFFU;
+# }
+
+
+@micropython.viper
+def crc16_viper(crc: int, data) -> int:
+ dp = ptr8(addressof(data))
+ tp = ptr16(addressof(_sd_crc16_table))
+ nn = int(len(data))
+ idx = 0
+ while idx < nn:
+ crc = ((crc << 8) & 0xFFFF) ^ tp[((crc >> 8) ^ dp[idx]) & 0xFF]
+ idx += 1
+ return crc
+
+
+try: # if we have asm_thumb, this goes faster
+
+ @micropython.asm_thumb
+ def _crc_loop_16(r0, r1, r2, r3) -> int:
+ # r0 is data address
+ # r1 is table address
+ # r2 is CRC
+ # r3 is count
+ mov(r4, 0)
+ mvn(r4, r4) # all ones now
+ mov(r7, 16)
+ lsr(r4, r7) # R4 = half-word of ones
+ mov(r5, 0xFF) # used for byte masking
+ label(loop)
+ mov(r6, r2) # copy current CRC
+ mov(r7, 8)
+ lsr(r6, r7) # crc >> 8
+ ldrb(r7, [r0, 0]) # fetch new byte dp[idx]
+ add(r0, 1) # push to next byte address
+ eor(r6, r7) # r6 = (crc>>8) ^ dp[idx]
+ and_(r6, r5) # mask byte ( (crc>>8) ^ dp[idx]) & 0xff
+ add(r6, r6, r6) # double for table offset
+ add(r6, r6, r1) # table data address
+ ldrh(r6, [r6, 0]) # fetch table syndrome
+ mov(r7, 8)
+ lsl(r2, r7) # CRC << 8
+ and_(r2, r4) # (crc << 8) & 0xffff)
+ eor(r2, r6) # new CRC
+ sub(r3, 1)
+ bne(loop)
+ mov(r0, r2)
+
+ @micropython.viper
+ def crc16(crc: int, data) -> int:
+ return int(
+ _crc_loop_16(
+ int(addressof(data)),
+ int(addressof(_sd_crc16_table)),
+ crc,
+ int(len(data)),
+ )
+ )
+
+except:
+ # wrapper to allow the pure-python implementation to be accessed by the right name if asm_thumb doesn't work
+ @micropython.viper
+ def crc16(crc: int, data) -> int:
+ return int(crc16_viper(crc, data))
+
+
+# def test_speed():
+# data = b"\xaa"*1024
+# import time
+# crc = 0
+# start = time.ticks_us()
+# for i in range(1024):
+# crc = crc16(crc, data)
+# print("asm crc speed = ", f"{crc:08x}", 2**20 / (time.ticks_diff(time.ticks_us(), start) / 1e6), "bytes/s")
+#
+# crc = 0
+# start = time.ticks_us()
+# for i in range(1024):
+# crc = crc16_viper(crc, data)
+# print("py crc speed = ", f"{crc:08x}", 2**20 / (time.ticks_diff(time.ticks_us(), start) / 1e6), "bytes/s")
diff --git a/micropython/drivers/storage/sdcard/manifest.py b/micropython/drivers/storage/sdcard/manifest.py
index cb4647eeb..2589343d4 100644
--- a/micropython/drivers/storage/sdcard/manifest.py
+++ b/micropython/drivers/storage/sdcard/manifest.py
@@ -1,3 +1,3 @@
-metadata(description="SDCard block device driver.", version="0.1.0")
+metadata(description="SDCard block device driver.", version="0.2.0")
module("sdcard.py", opt=3)
diff --git a/micropython/drivers/storage/sdcard/sd_card_spi_test.py b/micropython/drivers/storage/sdcard/sd_card_spi_test.py
new file mode 100644
index 000000000..1df2a32cd
--- /dev/null
+++ b/micropython/drivers/storage/sdcard/sd_card_spi_test.py
@@ -0,0 +1,101 @@
+# SD card setup
+
+from machine import SPI, Pin, freq
+
+freq(120_000_000)
+print("freq:", freq())
+
+import os
+from sdcard import SDCard
+import time
+
+# the bus spi1 on these pins on my test card
+
+# I have cs on GP13 for this one
+spi = SPI(1, 24_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12))
+spi.init() # Ensure right baudrate
+
+from crc16 import crc16
+
+sd = SDCard(spi=spi, cs=Pin(13, Pin.OUT), baudrate=24_000_000, crc16_function=None)
+vfs = os.VfsFat(sd)
+os.mount(vfs, "/fc")
+
+
+def sdtest():
+ print("Filesystem check")
+ print(os.listdir("/fc"))
+
+ line = "abcdefghijklmnopqrstuvwxyz\n"
+ lines = line * 200 # 5400 chars
+ short = "1234567890\n"
+
+ fn = "/fc/rats.txt"
+ print()
+ print("Multiple block read/write")
+ loops = 1000
+ t0 = time.ticks_ms()
+ with open(fn, "w") as f:
+ n = f.write(lines)
+ print(n, "bytes written")
+ n = f.write(short)
+ print(n, "bytes written")
+ for i in range(loops):
+ n = f.write(lines)
+
+ nbytes = loops * len(lines) + len(lines) + len(short)
+ rate = 1000 * nbytes / time.ticks_diff(time.ticks_ms(), t0)
+ print(nbytes, "bytes written at ", rate / 1e6, "MB/s")
+
+ stat = os.stat(fn)
+ filesize = stat[6]
+ total = 0
+ t0 = time.ticks_ms()
+
+ readbuf = bytearray(8192)
+ import uctypes
+
+ with open(fn, "rb") as f:
+ f.readinto(readbuf)
+ big_readback = readbuf[: len(lines)] # check a big chunk of data
+
+ with open(fn, "rb") as f:
+ while (count := f.readinto(readbuf)) != 0:
+ total += count
+
+ rate = 1000 * total / time.ticks_diff(time.ticks_ms(), t0)
+
+ print("final file size", filesize, "expected", nbytes, "read", total, "rate=", rate / 1e6)
+
+ fn = "/fc/rats1.txt"
+ print()
+ print("Single block read/write")
+ with open(fn, "w") as f:
+ n = f.write(short) # one block
+ print(n, "bytes written")
+
+ with open(fn, "r") as f:
+ result2 = f.read()
+ print(len(result2), "bytes read")
+
+ print()
+ print("Verifying data read back")
+ success = True
+ if result2 == short:
+ print("Small file Pass")
+ else:
+ print("Small file Fail")
+ success = False
+ if big_readback == lines:
+ print("Big read Pass")
+ else:
+ print("Big readFail")
+ success = False
+
+ print()
+ print("Tests", "passed" if success else "failed")
+
+
+sdtest()
+
+os.umount("/fc")
diff --git a/micropython/drivers/storage/sdcard/sdcard.py b/micropython/drivers/storage/sdcard/sdcard.py
index c9c991f59..00633cc84 100644
--- a/micropython/drivers/storage/sdcard/sdcard.py
+++ b/micropython/drivers/storage/sdcard/sdcard.py
@@ -1,57 +1,96 @@
-"""
-MicroPython driver for SD cards using SPI bus.
+#
+# 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 pyboard:
+#
+# import pyb, sdcard, os
+# sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
+# pyb.mount(sd, '/sd2')
+# os.listdir('/')
+#
+# Example usage on ESP8266:
+#
+# import machine, sdcard, os
+# sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))
+# os.mount(sd, '/sd')
+# os.listdir('/')
+#
+# Note about the crc_function:
+# this is crc(seed: int, buf: buffer) -> int
+# If no crc16 function is provided, CRCs are not computed on data transfers.
+# If a crc16 is provided, the CRC function of the SD card is enabled,
+# and data transfers both ways are protected by it
+#
+
+import micropython
+from micropython import const
+import time
+import uctypes
+from errno import ETIMEDOUT, EIO, ENODEV, EINVAL
-Requires an SPI bus and a CS pin. Provides readblocks and writeblocks
-methods so the device can be mounted as a filesystem.
+crc7_be_syndrome_table = (
+ b"\x00\x12$6HZl~\x90\x82\xb4\xa6\xd8\xca\xfc\xee2 \x16\x04zh^L\xa2\xb0\x86\x94\xea\xf8"
+ b"\xce\xdcdv@R,>\x08\x1a\xf4\xe6\xd0\xc2\xbc\xae\x98\x8aVDr`\x1e\x0c:(\xc6\xd4\xe2\xf0"
+ b"\x8e\x9c\xaa\xb8\xc8\xda\xec\xfe\x80\x92\xa4\xb6XJ|n\x10\x024&\xfa\xe8\xde\xcc\xb2\xa0"
+ b'\x96\x84jxN\\"0\x06\x14\xac\xbe\x88\x9a\xe4\xf6\xc0\xd2<.\x18\ntfPB\x9e\x8c\xba\xa8'
+ b"\xd6\xc4\xf2\xe0\x0e\x1c*8FTbp\x82\x90\xa6\xb4\xca\xd8\xee\xfc\x12\x006$ZH~l\xb0\xa2"
+ b"\x94\x86\xf8\xea\xdc\xce 2\x04\x16hzL^\xe6\xf4\xc2\xd0\xae\xbc\x8a\x98vdR@>,\x1a\x08"
+ b"\xd4\xc6\xf0\xe2\x9c\x8e\xb8\xaaDV`r\x0c\x1e(:JXn|\x02\x10&4\xda\xc8\xfe\xec\x92\x80"
+ b'\xb6\xa4xj\\N0"\x14\x06\xe8\xfa\xcc\xde\xa0\xb2\x84\x96.<\n\x18ftBP\xbe\xac\x9a\x88\xf6'
+ b"\xe4\xd2\xc0\x1c\x0e8*TFpb\x8c\x9e\xa8\xba\xc4\xd6\xe0\xf2"
+)
-Example usage on pyboard:
- import pyb, sdcard, os
- sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
- pyb.mount(sd, '/sd2')
- os.listdir('/')
+def crc7(buf) -> int:
+ crc = 0
+ for b in buf:
+ crc = crc7_be_syndrome_table[crc ^ b]
+ return crc
-Example usage on ESP8266:
- import machine, sdcard, os
- sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))
- os.mount(sd, '/sd')
- os.listdir('/')
+def gb(bigval, b0, bn):
+ # get numbered bits from a buf_to_int from, for example, the CSD
+ return (bigval >> b0) & ((1 << (1 + bn - b0)) - 1)
-"""
-from micropython import const
-import time
-
-
-_CMD_TIMEOUT = const(100)
+_CMD_TIMEOUT = const(50)
_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_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)
+_HCS_BIT = const(1 << 30) # for ACMD41
class SDCard:
- def __init__(self, spi, cs, baudrate=1320000):
+ def __init__(self, spi, cs, baudrate=1320000, crc16_function=None):
self.spi = spi
self.cs = cs
self.cmdbuf = bytearray(6)
- self.dummybuf = bytearray(512)
+ self.cmdbuf5 = memoryview(self.cmdbuf)[:5] # for crc7 generation
self.tokenbuf = bytearray(1)
- for i in range(512):
- self.dummybuf[i] = 0xFF
- self.dummybuf_memoryview = memoryview(self.dummybuf)
-
+ self.crcbuf = bytearray(2)
+ self.crc16 = None # during init
# initialise the card
self.init_card(baudrate)
+ self.check_crcs(crc16_function) # now set it up
+
+ def check_crcs(self, crc16_function):
+ self.crc16 = crc16_function
+ result = self.cmd(
+ 59, 1 if crc16_function else 0, release=True
+ ) # send CRC enable/disable command
+ return result
def init_spi(self, baudrate):
try:
@@ -63,6 +102,9 @@ def init_spi(self, baudrate):
# on pyboard
self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
+ def _spiff(self):
+ self.spi.write(b"\xff")
+
def init_card(self, baudrate):
# init CS pin
self.cs.init(self.cs.OUT, value=1)
@@ -70,82 +112,74 @@ def init_card(self, baudrate):
# 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")
+ # clock card at least 100 cycles with cs high (16 bytes = 128 cycles)
+ # use explicit string here for small memory footprint
+ self.spi.write(b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\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")
+ raise OSError(ENODEV, "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")
+ r = self.cmd(8, 0x01AA, 4) # probe version
+ v2 = r == _R1_IDLE_STATE
+ v1 = r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND)
+
+ if not (v1 or v2):
+ raise OSError(EIO, "couldn't determine SD card version")
+ arg41 = _HCS_BIT if v2 else 0 # we support high capacity, on v2 cards
+ for i in range(_CMD_TIMEOUT): # loop on acmd41 to get
+ self.cmd(55, 0)
+ if (r := self.cmd(41, arg41)) == 0:
+ break
+ time.sleep_ms(5)
+ if r != 0:
+ raise OSError(ETIMEDOUT, "card type", "v2" if v2 else "v1")
# 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")
+ if self.cmd(9, 0, 0, False) != 0:
+ raise OSError(EIO, "no CSD response")
csd = bytearray(16)
self.readinto(csd)
- if csd[0] & 0xC0 == 0x40: # CSD version 2.0
- self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
- elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB)
- c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6
- c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7
- read_bl_len = csd[5] & 0b1111
+ self.CSD = csd_int = int.from_bytes(
+ csd, "big"
+ ) # convert 16-byte CSD to a giant integer for bit extraction
+ _gb = gb # just for local binding
+ # use bit numbers from SD card spec v9.0.0, table 5.3.2
+ vers = _gb(csd_int, 126, 127)
+ if vers == 1: # CSD version 2.0
+ self.sectors = (_gb(csd_int, 48, 69) + 1) * 1024
+ self.cdv = 1
+ elif vers == 0x00: # CSD version 1.0 (old, <=2GB)
+ c_size = _gb(csd_int, 62, 73)
+ c_size_mult = _gb(csd_int, 47, 49)
+ read_bl_len = _gb(csd_int, 80, 83)
capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len)
self.sectors = capacity // 512
+ self.cdv = 512 # converts bytes to sectors
else:
- raise OSError("SD card CSD format not supported")
+ raise OSError(EIO, "CSD format unknown")
# 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")
+ if self.cmd(16, 512) != 0:
+ raise OSError(EIO, "can't set 512 block size")
# set to high data rate now that it's initialised
self.init_spi(baudrate)
- def init_card_v1(self):
- for i in range(_CMD_TIMEOUT):
- time.sleep_ms(50)
- self.cmd(55, 0, 0)
- if self.cmd(41, 0, 0) == 0:
- # SDSC card, uses byte addressing in read/write/erase commands
- 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) # 4-byte response, negative means keep the first byte
- ocr = self.tokenbuf[0] # get first byte of response, which is OCR
- if not ocr & 0x40:
- # SDSC card, uses byte addressing in read/write/erase commands
- self.cdv = 512
- else:
- # SDHC/SDXC card, uses block addressing in read/write/erase commands
- 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, skip1=False):
- self.cs(0)
+ def cmd(self, cmd, arg, final=0, release=True, skip1=False):
+ cs = self.cs # prebind
+ w = self.spi.write
+ r = self.spi.readinto
+ tb = self.tokenbuf
+ spiff = self._spiff
+
+ cs(0) # select chip
# create and send the command
buf = self.cmdbuf
@@ -154,150 +188,154 @@ def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
buf[2] = arg >> 16
buf[3] = arg >> 8
buf[4] = arg
- buf[5] = crc
- self.spi.write(buf)
+ buf[5] = crc7(self.cmdbuf5) | 1
+ w(buf)
if skip1:
- self.spi.readinto(self.tokenbuf, 0xFF)
+ r(tb, 0xFF)
# wait for the response (response[7] == 0)
for i in range(_CMD_TIMEOUT):
- self.spi.readinto(self.tokenbuf, 0xFF)
- response = self.tokenbuf[0]
+ r(tb, 0xFF)
+ response = tb[0]
+ # print(f"response: {response:02x}")
+
if not (response & 0x80):
# this could be a big-endian integer that we are getting here
# if final<0 then store the first byte to tokenbuf and discard the rest
+ if response & _R1_COM_CRC_ERROR:
+ cs(1)
+ spiff()
+ raise OSError(EIO, f"CRC err on cmd: {cmd:02d}")
if final < 0:
- self.spi.readinto(self.tokenbuf, 0xFF)
+ r(tb, 0xFF)
final = -1 - final
for j in range(final):
- self.spi.write(b"\xff")
+ spiff()
if release:
- self.cs(1)
- self.spi.write(b"\xff")
+ cs(1)
+ spiff()
return response
+ else:
+ if i > (_CMD_TIMEOUT // 2):
+ time.sleep_ms(1) # very slow response, give it time
# timeout
- self.cs(1)
- self.spi.write(b"\xff")
- return -1
+ cs(1)
+ spiff()
+ raise OSError(ETIMEDOUT, "command:", cmd, "arg:", arg)
def readinto(self, buf):
- self.cs(0)
+ cs = self.cs
+ spiff = self._spiff
+
+ cs(0)
# read until start byte (0xff)
for i in range(_CMD_TIMEOUT):
self.spi.readinto(self.tokenbuf, 0xFF)
if self.tokenbuf[0] == _TOKEN_DATA:
break
- time.sleep_ms(1)
+ if i > _CMD_TIMEOUT // 2:
+ time.sleep_ms(1) # if response is slow, wait longer
+
else:
- self.cs(1)
- raise OSError("timeout waiting for response")
+ cs(1)
+ raise OSError(ETIMEDOUT, "read timeout")
- # read data
- mv = self.dummybuf_memoryview
- if len(buf) != len(mv):
- mv = mv[: len(buf)]
- self.spi.write_readinto(mv, buf)
+ self.spi.readinto(buf, 0xFF)
# read checksum
- self.spi.write(b"\xff")
- self.spi.write(b"\xff")
+ ck = self.spi.read(2, 0xFF)
+ if self.crc16:
+ crc = self.crc16(self.crc16(0, buf), ck)
+ if crc != 0:
+ raise OSError(EIO, f"bad data CRC: {crc:04x}")
- self.cs(1)
- self.spi.write(b"\xff")
+ cs(1)
+ spiff()
def write(self, token, buf):
- self.cs(0)
+ cs = self.cs
+ spiff = self._spiff
+ r = self.spi.read
+ w = self.spi.write
- # 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")
+ cs(0)
- # check the response
- if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:
- self.cs(1)
- self.spi.write(b"\xff")
- return
+ # send: start of block, data, checksum
+ r(1, token)
+ w(buf)
+ if self.crc16:
+ crc = self.crc16(0, buf)
+ self.crcbuf[0] = crc >> 8
+ self.crcbuf[1] = crc & 0xFF
+ w(self.crcbuf) # write checksum
+ else:
+ w(b"\xff\xff")
+ # check the response
+ if ((r(1, 0xFF)[0]) & 0x1F) != 0x05:
+ cs(1)
+ spiff()
+ raise OSError(EIO, "write fail")
# wait for write to finish
- while self.spi.read(1, 0xFF)[0] == 0:
+ while (r(1, 0xFF)[0]) == 0:
pass
- self.cs(1)
- self.spi.write(b"\xff")
+ cs(1)
+ spiff()
def write_token(self, token):
self.cs(0)
self.spi.read(1, token)
- self.spi.write(b"\xff")
+ self._spiff()
# wait for write to finish
while self.spi.read(1, 0xFF)[0] == 0x00:
pass
self.cs(1)
- self.spi.write(b"\xff")
+ self._spiff()
+
+ @staticmethod
+ def blocks(buf):
+ nblocks, err = divmod(len(buf), 512)
+ if not nblocks or err:
+ raise OSError(EINVAL, "Buffer length is invalid")
+ return nblocks
def readblocks(self, block_num, buf):
# workaround for shared bus, required for (at least) some Kingston
# devices, ensure MOSI is high before starting transaction
- self.spi.write(b"\xff")
+ self._spiff()
+ nblocks = self.blocks(buf)
- nblocks = len(buf) // 512
- assert nblocks and not len(buf) % 512, "Buffer length is invalid"
- if nblocks == 1:
- # CMD17: set read address for single block
- if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
- # release the card
- self.cs(1)
- raise OSError(5) # EIO
- # receive the data and release card
- self.readinto(buf)
- else:
- # CMD18: set read address for multiple blocks
- if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
- # release the card
- self.cs(1)
- raise OSError(5) # EIO
- offset = 0
- mv = memoryview(buf)
- while nblocks:
- # receive the data and release card
- self.readinto(mv[offset : offset + 512])
- offset += 512
- nblocks -= 1
- if self.cmd(12, 0, 0xFF, skip1=True):
- raise OSError(5) # EIO
+ # CMD18: set read address for multiple blocks
+ if self.cmd(18, block_num * self.cdv, release=False) != 0:
+ # release the card
+ self.cs(1)
+ raise OSError(EIO) # EIO
+ mv = memoryview(buf)
+ for offset in range(0, nblocks * 512, 512):
+ self.readinto(mv[offset : offset + 512])
+
+ if self.cmd(12, 0, skip1=True):
+ raise OSError(EIO) # EIO
def writeblocks(self, block_num, buf):
# workaround for shared bus, required for (at least) some Kingston
# devices, ensure MOSI is high before starting transaction
- self.spi.write(b"\xff")
-
- 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:
- raise OSError(5) # EIO
-
- # 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:
- raise OSError(5) # EIO
- # 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)
+ self._spiff()
+ nblocks = self.blocks(buf)
+
+ # CMD25: set write address for first block
+ if self.cmd(25, block_num * self.cdv) != 0:
+ raise OSError(EIO) # EIO`
+ # send the data
+ mv = memoryview(buf)
+ for offset in range(0, nblocks * 512, 512):
+ self.write(_TOKEN_CMD25, mv[offset : offset + 512])
+ self.write_token(_TOKEN_STOP_TRAN)
def ioctl(self, op, arg):
if op == 4: # get number of blocks
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/micropython/micropython-lib/pull/765.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy