2024-01-22 02: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?