From fdac8dc5788033c7f501a3dc04688ea85003b872 Mon Sep 17 00:00:00 2001 From: Vince Parsons Date: Mon, 28 Sep 2026 19:39:08 -0700 Subject: [PATCH] sdcard: Keep card selected across multi-block transfers. readblocks() and writeblocks() released chip select after every block of a CMD18/CMD25 multi-block transfer (readinto() and write() end with cs(1) and an extra clocked byte), reselecting the card for the next block. A Lexar 32GB microSDHC card loses bit alignment across that release: every block after the first is read shifted by 4 bits, so the data token 0xfe arrives as 0xe0 (ff e0 00 ... for ff fe 00 ...) and readinto() times out. On a Pico 2 W, 200 of 200 two-block reads failed at both the default 1.32MHz and 12MHz. Keep the card selected from the command through to CMD12 or the stop token, as CircuitPython's sdcardio and adafruit_sdcard drivers do. readinto() and write() gain a release argument, True by default, so single-block transfers are unchanged. With this change, the same 200 two-block reads all succeed and match single-block reads of the same blocks, at both clock rates, and multi-block writes read back correctly. A card that worked before still works. Signed-off-by: Vince Parsons --- .../drivers/storage/sdcard/manifest.py | 2 +- micropython/drivers/storage/sdcard/sdcard.py | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/micropython/drivers/storage/sdcard/manifest.py b/micropython/drivers/storage/sdcard/manifest.py index 2589343d4..37fae6218 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.2.0") +metadata(description="SDCard block device driver.", version="0.2.1") module("sdcard.py", opt=3) diff --git a/micropython/drivers/storage/sdcard/sdcard.py b/micropython/drivers/storage/sdcard/sdcard.py index 992f52eea..94c69cc35 100644 --- a/micropython/drivers/storage/sdcard/sdcard.py +++ b/micropython/drivers/storage/sdcard/sdcard.py @@ -191,7 +191,7 @@ def cmd(self, cmd, arg, final=0, release=True, skip1=False): self.spi.write(b"\xff") return -1 - def readinto(self, buf): + def readinto(self, buf, release=True): self.cs(0) # read until start byte (0xff) @@ -214,10 +214,11 @@ def readinto(self, buf): self.spi.write(b"\xff") self.spi.write(b"\xff") - self.cs(1) - self.spi.write(b"\xff") + if release: + self.cs(1) + self.spi.write(b"\xff") - def write(self, token, buf): + def write(self, token, buf, release=True): self.cs(0) # send: start of block, data, checksum @@ -236,8 +237,9 @@ def write(self, token, buf): while self.spi.read(1, 0xFF)[0] == 0: pass - self.cs(1) - self.spi.write(b"\xff") + if release: + self.cs(1) + self.spi.write(b"\xff") def write_token(self, token): self.cs(0) @@ -274,8 +276,10 @@ def readblocks(self, block_num, buf): offset = 0 mv = memoryview(buf) while nblocks: - # receive the data and release card - self.readinto(mv[offset : offset + 512]) + # receive the data, keeping the card selected until CMD12: + # some cards lose bit alignment if CS is released between + # the blocks of a multi-block read + self.readinto(mv[offset : offset + 512], release=False) offset += 512 nblocks -= 1 if self.cmd(12, 0, skip1=True): @@ -297,13 +301,15 @@ def writeblocks(self, block_num, buf): self.write(_TOKEN_DATA, buf) else: # CMD25: set write address for first block - if self.cmd(25, block_num * self.cdv) != 0: + if self.cmd(25, block_num * self.cdv, release=False) != 0: + # release the card + self.cs(1) raise OSError(5) # EIO - # send the data + # send the data, keeping the card selected until the stop token offset = 0 mv = memoryview(buf) while nblocks: - self.write(_TOKEN_CMD25, mv[offset : offset + 512]) + self.write(_TOKEN_CMD25, mv[offset : offset + 512], release=False) offset += 512 nblocks -= 1 self.write_token(_TOKEN_STOP_TRAN)