2024-01-22 2:17 AM
I have been using microSD chips reliably for many years with stm32f4, stm32h7 and stm32l4.
But for one of my products, where space is at a premium, I am trying to use a SD-NAND chip XTSD04G, and I am running into occasional errors where an SDIO R1 command fails with SDMMC_STA gives the error CTIMEOUT
This only happens when I am running my system at maximum capacity (writing at 80kbits/s, with occasional reads interleaved). To reduce loading on the memory-chip, the majority of writes use SD_CMD_WRITE_MULT_BLOCK, writing 8kbytes i.e. 16 sectors of 512 bytes in each hit.
The data-sheet for XTSD04G claims it is up to class 8, so that implies it should support 8MB/s (even if that were 8mbit/s I would be well within that limit). And it claims to support up to 50 MHz clock frequency, so the stm32l4's 48MHz clock should be fine. And for reference, yes I do use it in 4-bit mode.
As I am not a paid-up member of SDIO, I only have access to their simplified specifications.
Is there any guidance for how to recover from CTIMEOUT without having to go through the long process of resetting the memory-card? (At the moment I reset the memory-chip, re-identify it at 1-bit and then switch to 4-bit mode).
My code is
uint32_t SDIO_bulkWriteSector(const uint8_t *buffer, uint32_t sector) { /* writes a single sector but using CMD25 = WRITE_MULTIPLE_BLOCK */
unsigned int reply;
static uint32_t lastBulkSector; /* this must be 1 less than address to continue with current bulk write if one is active */
ctl_events_wait(CTL_EVENT_WAIT_ALL_EVENTS_WITH_AUTO_CLEAR, &sdio_es, SDIO_ES_COMMAND, CTL_TIMEOUT_NONE, 0);
showStatus(eDfsLowCommand, 1);
if ((sdio_es & SDIO_ES_SD_BULK_WRITING) && (lastBulkSector + 1 != sector)) {
SDIO_innerEndBulkWriteSector();
}
lastBulkSector = sector;
if (loadedCard.CardType != eSDIO_HIGH_CAPACITY_SD_CARD)
sector *= 512;
/* Wait till card is ready for data Added */
if (sdio_es & SDIO_ES_POLL_FOR_READY_DUE) {
reply = sdio_pollForReady();
ctl_events_set_clear(&sdio_es, 0, SDIO_ES_POLL_FOR_READY_DUE);
if (reply) {
showStatus(eDfsLowCommand, 0);
ctl_events_set_clear(&sdio_es, SDIO_ES_COMMAND, 0);
return reply;
}
}
if (!(sdio_es & SDIO_ES_SD_BULK_WRITING)) {
reply = SDIO_R1_Command(SD_CMD_WRITE_MULT_BLOCK, sector);
if (reply) {
showStatus(eDfsLowCommand, 0);
ctl_events_set_clear(&sdio_es, SDIO_ES_COMMAND, 0);
return reply;
}
}
/* SDIO_DataConfig */
SDIO->DTIMER = SD_DATATIMEOUT; /* very long time */
SDIO->DLEN = PREFERRED_BLOCK_SIZE;
peripheral_bitband_write(&DMAm_Streamn(SDIO_DMA_n, SDIO_DMA_STREAM_NUMBER)->dmaCR, 0, 0);
DMAm_Streamn(SDIO_DMA_n, SDIO_DMA_STREAM_NUMBER)->dmaCR = 0//(SDIO_DMA_CHANNEL_NUMBER * DMA_SxCR_CHSEL_0)
/* | double-buffer - not yet */
| DMA_SxCR_PFCTRL /* let the SDIO peripheral say when everything is transferred */
| DMA_SxCR_MBURST_0 | DMA_SxCR_PBURST_0 /* bursts of 4 transfers */
| (SDIO_DMA_PRIORITY * DMA_SxCR_PL_0)
| DMA_SxCR_MSIZE_1 | DMA_SxCR_PSIZE_1 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE | DMA_SxCR_TEIE;
DMA_SET_CHANNEL(SDIO_DMA_n, SDIO_DMA_STREAM_NUMBER, SDIO_DMA_CHANNEL_NUMBER);
DMAm_Streamn(SDIO_DMA_n, SDIO_DMA_STREAM_NUMBER)->dmaNDTR = 512 / 4;
DMAm_Streamn(SDIO_DMA_n, SDIO_DMA_STREAM_NUMBER)->dmaPAR = (unsigned long)&SDIO->FIFO;
DMAm_Streamn(SDIO_DMA_n, SDIO_DMA_STREAM_NUMBER)->dmaM0AR = (unsigned long)buffer;
peripheral_bitband_write(&DMAm_Streamn(SDIO_DMA_n, SDIO_DMA_STREAM_NUMBER)->dmaCR, 0, 1);DELAY;
SDIO->DCTRL = SDIO_DCTRL_DTEN | SDIO_DataBlockSize_512b | SDIO_TransferDir_ToCard | SDIO_TransferMode_Block | SDIO_DCTRL_DMAEN | (SDIO->DCTRL & ~0xFFF);
reply = ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR, &sdio_es, SDIO_ES_DMA_INT, CTL_TIMEOUT_DELAY, SDIO_TIMEOUT_DURATION_TICKS);
if (!reply) { /* timeout */
showStatus(eDfsLowCommand, 0);
ctl_events_set_clear(&sdio_es, SDIO_ES_COMMAND, 0);
return 1;
}
reply = SDIO->STA & (SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL | SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR);
showStatus(eDfsLowCommand, 0);
ctl_events_set_clear(&sdio_es, SDIO_ES_COMMAND | SDIO_ES_POLL_FOR_READY_DUE | SDIO_ES_SD_BULK_WRITING, 0);
return reply;
}unsigned SDIO_R1_Command(unsigned int cmdIndex, unsigned int argument) { /* returns 0 on no error */
unsigned wait_reply;
// while (SDIO->STA & (SDIO_STA_CMDACT/* | SDIO_STA_TXACT | SDIO_STA_RXACT*/))
// { ; }
SDIO->ICR = SDIO->STA; /* clear all pending status bits */
SDIO->ARG = argument;
SDIO->MASK = SDIO_MASK_CCRCFAILIE | SDIO_MASK_CMDRENDIE | SDIO_MASK_CTIMEOUTIE;
DELAY;
SDIO->CMD = cmdIndex
| SDIO_Response_Short
| 0 /* SDIO_Wait_No */
| SDIO_CMD_CPSMEN; /* SDIO_CPSM_Enable */
uint32_t sta = 0;
wait_reply = ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR, &sdio_es, SDIO_ES_PERIPHERAL_INT, CTL_TIMEOUT_DELAY, SDIO_TIMEOUT_DURATION_TICKS);
if (!wait_reply || !((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CCRCFAIL))) {
logString("R1 fail, STA=");
logX(sta);
logString("\n");
return SDIO->STA | SDIO_R1_COMMAND_STA_FAIL;
}
if (SDIO->RESPCMD != cmdIndex) {
logString("R1 fail RESPCMD=");
logX(SDIO->RESPCMD);
logString(" should be ");
logX(cmdIndex);
logString("\n");
return SDIO->RESPCMD | SDIO_R1_COMMAND_BAD_COMMAND;
}
if (SDIO->RESP1 & SD_OCR_ERRORBITS) {
logString("R1 fail errorBits, SDIO->RESP1=");
logX(SDIO->RESP1);
logString("\n");
return (SDIO->RESP1 >> 3) | SDIO_R1_COMMAND_R1_FAIL;
}
return 0;
}And it fails with R1 fail, STA=4. DELAY is __dsb(); followed by 6 NOPs.
Any thoughts or suggestions?