Skip to content

sdcard: Keep card selected across multi-block transfers. - #1166

Open
VinceParsons wants to merge 1 commit into
micropython:masterfrom
VinceParsons:sdcard-multiblock-cs
Open

VinceParsons wants to merge 1 commit into
micropython:masterfrom
VinceParsons:sdcard-multiblock-cs

Conversation

@VinceParsons

Copy link
Copy Markdown

Summary

sdcard.py releases chip select between the blocks of a multi-block transfer: readblocks() calls readinto() once per block of a CMD18 read, and readinto() ends with cs(1) plus an extra clocked byte; writeblocks() does the same through write() for CMD25. Some cards don't cope with that.

With a new Lexar 32GB microSDHC card (Lexar Blue Series 633x), every multi-block read fails on its second block. Capturing the bytes the driver sees where the data token should be:

ff e0 00 00 00 00 ...    (expected: ff fe 00 00 ...)

That's the 0xfe data token followed by data, read 4 bits late - the card loses bit alignment across the CS release. The driver then loops waiting for 0xfe and raises OSError('timeout waiting for response').

This keeps the card selected from the command through to CMD12 / the stop token, as CircuitPython's drivers do:

readinto() and write() take a new release argument, defaulting to True, so single-block transfers and any external callers are unchanged. Version bumped to 0.2.1.

Testing

Raspberry Pi Pico 2 W (RP2350), MicroPython 1.26.0, this repo's current sdcard.py vs this patch, with the Lexar card. Read-only test: 200 two-block reads (CMD18), each compared against single-block reads (CMD17) of the same blocks:

Driver 1.32MHz (default) 12MHz
current sdcard.py 200/200 fail (timeout waiting for response) 200/200 fail
with this patch 0 errors, all blocks match 0 errors, all blocks match

With the patch, multi-block writes (3 x 256KB, written in 8KB chunks through VfsFat) also read back correctly. A card of another brand that worked before the change still works with it.

Not tested on other ports/boards.

Trade-offs and Alternatives

None significant - a few extra bytecode instructions. The card is held selected slightly longer (until the stop command), which is what the SD SPI protocol expects anyway.

Generative AI

I used generative AI tools when creating this PR, but a human has checked the code and is responsible for the code and the description above.

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 <micropython@vinceparsons.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant