Skip to content

CHSd: send 8 idle clocks before each command, so more microSD cards work and it follows the specification - #21

Merged
bateske merged 3 commits into
bateske:mainfrom
joyrider3774:chsd-idle-clocks-before-command
Oct 9, 2026
Merged

bateske merged 3 commits into
bateske:mainfrom
joyrider3774:chsd-idle-clocks-before-command

Conversation

@joyrider3774

Copy link
Copy Markdown
Contributor

Summary

CHSd's cmd() (libraries/CHSd/src/SdSpi.cpp) sends a command right after the previous response. The SD Physical Layer Simplified Specification requires at least 8 clock cycles between the end of a response and the start of the next command. In SPI mode that's N_RC, at least one byte of idle clocks; see the SPI timing values in section 7.5. Many cards tolerate the missing gap, but strict ones don't. Adding one idle byte makes CHSd follow the spec, so it supports more cards, at practically no cost.

What happens

With a SanDisk 32 GB microSD (CID maker 3, product "SK32G"), FAT32, CHStlView from board package 0.3.0 says no card on a rev0 board. The SD menu bootloader and CHSDtoUSB read the same card fine.

Logged on the device during identification at 187.5 kHz, right after a power cycle:

the first CMD0 answers 01, as it should;
the next CMD0, sent right after that response, comes back as C1 7F: the card is still finishing its previous answer, so the new command arrives misaligned;
from then on every answer is shifted by 6 bits (7F, then 3F for ACMD41);
after a few commands the card stops answering entirely, until its power is cycled.
The bootloader's sd.c sends x(0xFF) at the start of every command, and CHSDtoUSB's sdfatlib driver calls waitNotBusy() first, which also clocks idle bytes. That's why both work with this card.

The fix

One line at the start of cmd():

static uint8_t cmd(uint8_t c, uint32_t arg) {
    xfer(0xFF);   // N_RC: at least 8 clocks after the previous response (SD spec)
    xfer((uint8_t)(0x40 | c));

Tested on the device (rev0, same card):

CHStlView from the 0.3.0 board package, as released: no card.
The same CHStlView source built with only this line added: it reads the card.
With a test sketch on the patched CHSd: the card initialises at the first try, FAT32 mounts, files are found, and single-block reads and 24 MHz stream() reads (200 runs of 17 blocks) complete with no failures.
Cost: one byte per command, not per data byte. Measured in a cycle-accurate emulator: about +0.2% on a 17-block stream at 24 MHz.

Affected: every sketch using CHSd, in board package 0.3.0 and the current main: CHStlView, and CHWords, CHCrossword and CHWordWheel for their card files.

bateske and others added 2 commits October 8, 2026 20:57
tests/test_spi.cpp's card model is now strict about N_RC: a command that
starts on the byte right after a response's last byte counts as a
failure. Without the previous commit's idle byte every init case fails
(11 or 12 commands per init); with it all 7 pass.

platform/README.md records the change (CLAUDE.md rule 6): +8 B in each of
CHWords, CHWordWheel, CHCrossword and CHStlView, RAM unchanged, both save
pages kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bateske
bateske merged commit e1f8d05 into bateske:main Oct 9, 2026
2 checks passed
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.

2 participants