2019-01-14 06:50 PM
Hello all,
I have worked through a number of issues with the SDMMC system and am currently stuck with not being able to reliably transfer from micro SD. I wrote test code to repeatedly read a file from the external card. In some cases HAL_SD_ErrorCallback is called with error code SDMMC_ERROR_CMD_CRC_FAIL.
Here are some things I have tried:
1) Slowing down the clock speed
2) Redesigning the board to improve the signal integrity of the micro SD lines
3) Using SDMMC2 instead of SDMMC1
4) Using 1-bit mode
5) Upgrading to Cube 1.3
6) Switching the pullup mode on the pins on and off
Here is a code excerpt:
SD_HandleTypeDef hsd1;
void SDMgr::Init()
{
memset(&hsd1, 0, sizeof(hsd1));
hsd1.Instance = SDMMC1;
hsd1.Init.BusWide = SDMMC_BUS_WIDE_4B;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
// This assumes an incoming clock of 400MHz / 16 = 25MHz
// Based on measurements, this results in a 50MHz micro SD clock. We get checksum errors
// hsd1.Init.ClockDiv = 2;
// Based on measurements, this results in a 25MHz micro SD clock. We get checksum errors
hsd1.Init.ClockDiv = 4;
memset(&m_SDFatFs, 0, sizeof(m_SDFatFs));
memset(&m_SDPath, 0, sizeof(m_SDPath));
// It is not necessary to cal BSP_SD_Init() here. It is already being called
// through FatFS at the appropriate time.
Mount();
}
void SDMgr::Mount()
{
FATFS_UnLinkDriver(m_SDPath);
/*##-1- Link the micro SD disk I/O driver ##################################*/
if (FATFS_LinkDriver(&SD_Driver, m_SDPath) != 0)
{
Logger::Print("Unable to FATFS_LinkDriver\n");
return;
}
/*##-2- Register the file system object to the FatFs module ##############*/
if(f_mount(&m_SDFatFs, (TCHAR const*)m_SDPath, 0) != FR_OK)
{
/* FatFs Initialization Error */
Logger::Print("Unable to f_mount\n");
Error_Handler();
}
}
void HAL_SD_MspInit(SD_HandleTypeDef* hsd)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hsd->Instance==SDMMC1)
{
/* Peripheral clock enable */
__HAL_RCC_SDMMC1_CLK_ENABLE();
/**SDMMC1 GPIO Configuration
PC10 ------> SDMMC1_D2
PC11 ------> SDMMC1_D3
PC12 ------> SDMMC1_CK
PD2 ------> SDMMC1_CMD
PC8 ------> SDMMC1_D0
PC9 ------> SDMMC1_D1
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_8
|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO1;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO1;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO1;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
__HAL_RCC_SDMMC1_FORCE_RESET();
__HAL_RCC_SDMMC1_RELEASE_RESET();
/* NVIC configuration for SDIO interrupts */
HAL_NVIC_SetPriority(SDMMC1_IRQn, IRQ_PRIORITY_SD, 0);
HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
}
}
Any ideas? Thank you very much.
UPDATE: I am using FreeRTOS and followed the FatFs_uSD_DMA_RTOS sample.
Solved! Go to Solution.
2019-05-06 02:33 PM
This change ultimately proved to be the answer:
uint32_t SDMMC_CmdReadMultiBlock(SDMMC_TypeDef *SDMMCx, uint32_t ReadAdd)
{
uint32_t maskReg = SDMMCx->MASK;
SDMMCx->MASK = 0;
SDMMC_CmdInitTypeDef sdmmc_cmdinit;
uint32_t errorstate;
/* Set Block Size for Card */
sdmmc_cmdinit.Argument = (uint32_t)ReadAdd;
sdmmc_cmdinit.CmdIndex = SDMMC_CMD_READ_MULT_BLOCK;
sdmmc_cmdinit.Response = SDMMC_RESPONSE_SHORT;
sdmmc_cmdinit.WaitForInterrupt = SDMMC_WAIT_NO;
sdmmc_cmdinit.CPSM = SDMMC_CPSM_ENABLE;
(void)SDMMC_SendCommand(SDMMCx, &sdmmc_cmdinit);
/* Check for error conditions */
errorstate = SDMMC_GetCmdResp1(SDMMCx, SDMMC_CMD_READ_MULT_BLOCK, SDMMC_CMDTIMEOUT);
SDMMCx->MASK = maskReg;
return errorstate;
}
2019-01-14 08:53 PM
What's your platform? NUCLEO, EVAL or custom
Try backing this off
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
For the NUCLEO remove SB116/SB117 to lose the stubs
Does your socket have pull-ups, or not?
Would probably pull-up PD2/CMD
2019-01-14 10:59 PM
Clive,
Thanks for the quick response. Let me also thank you for your enormous contributions in this community.
This is a custom platform. There is no transceiver for the micro SD socket. From everything I have seen it is not required. Would you recommend one?
There are no external pullups. I don't believe the socket has anything built in. I will try out your recommendations tomorrow.
Thank you
2019-01-14 11:28 PM
The socket itself probably doesn't but usually one can place them close in. Typically 33K or 47K
The slew rate you're using will dump a lot of energy into the traces, this can be problematic if they are very short, and you don't have any series resistor (27R or 33R) on them.
VERY_HIGH can drive 100-220 MHz depending on the capacitive loading. MEDIUM (60-110) and HIGH (85-166) might be more accommodative.
You might also want to double check the soldering at the socket, Without a card, you could try putting the pins in a GPIO OD mode, pulled high, and then drive each pin low individually via ODR, and then checking the value readback via IDR. This should show any interconnectivity.
Used these on NUCLEO-H7 boards https://www.waveshare.com/micro-sd-storage-board.htm
2019-01-16 06:00 PM
First, thanks again Clive. For others who run into this problem, please note the following:
I tried a number of different options and the best results came from GPIO_SPEED_FREQ_MEDIUM.
This was true even for series resistors of 0, 25, and 100 Ohms. They didn't seem to make a difference. I found the internal GPIO pullups did the job (GPIO_PULLUP on the data and command lines).
After modifying it for my board, I can now run this example for an extended time without errors:
STM32Cube_FW_H7_V1.3.0\Projects\STM32H743I_EVAL\Examples\SD\SD_ReadWrite_DMA
My previous test code still has problems. My next move is to dig into the FatFs stack and see what the problem is.
2019-01-16 06:12 PM
I have a soak tester that fills the media and checks the integrity of the data.
Do you have a debug UART available? Which UART/PINS?
2019-01-16 06:21 PM
Posted a simple test app for NUCLEO-H743ZI here
https://community.st.com/s/question/0D50X0000ADEBXHSQ5/interface-sd-card-with-nucleo-h743zi-1bit4bit
//
// NUCLEO-144 SDIO/SDMMC
// CN8
/// NC PC8 (D0)
// IOREF PC9 (D1)
// RESET PC10 (D2)
// (3V3) 3V3 PC11 (D3)
// 5V PC12 (CLK)
// (GND) GND PD2 (CMD)
// GND PG2 PF3 (CS)
// VIN PG3 PF5 (WP)
//
2019-01-19 09:36 AM
Clive,
Thanks again, at this point I have to conclude the PCB and circuit is good but there is some kind of SDMMC state machine/driver stack problem. Specifically, I see the state machine crash following a multiblock transfer and a SDMMC_CMD_STOP_TRANSMISSION command. This affects both reading and writing. It seems to be more prevalent when using FatFs because of the more complex transactions.
I appreciate that this one may be out of your direct experience because you use a different driver stack. Perhaps you can alert the STM people?
Is there any special behavior needed to close a multiblock transaction?
Here are some things I can say with confidence:
I have reviewed the errata dated June 2018. I've attempted to patch some of the issues, including the Clock stop, with no success.
At this point, I'm not sure what my next move is. I need to sign off on this PCB to keep my project on schedule. I'm confident the board/circuit design is OK. I am unclear on how to get past this state machine issue.
I appreciate any guidance you have to offer.
void HAL_SD_IRQHandler(SD_HandleTypeDef *hsd)
{
uint32_t errorstate;
uint32_t context = hsd->Context;
/* Check for SDMMC interrupt flags */
if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_DATAEND) != RESET)
{
// Attempted fix for errata 2.11.5
if((context & SD_CONTEXT_DMA) != 0U && (((context & SD_CONTEXT_READ_MULTIPLE_BLOCK) != 0U) || ((context & SD_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U)))
{
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DATAEND | SDMMC_FLAG_CKSTOP);
}
else
{
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DATAEND);
}
__HAL_SD_DISABLE_IT(hsd, SDMMC_IT_DATAEND | SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT |\
SDMMC_IT_TXUNDERR | SDMMC_IT_RXOVERR | SDMMC_IT_TXFIFOHE |\
SDMMC_IT_RXFIFOHF);
__HAL_SD_DISABLE_IT(hsd, SDMMC_IT_IDMABTC);
__SDMMC_CMDTRANS_DISABLE( hsd->Instance);
if((context & SD_CONTEXT_IT) != 0U)
{
if(((context & SD_CONTEXT_READ_MULTIPLE_BLOCK) != 0U) || ((context & SD_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U))
{
errorstate = SDMMC_CmdStopTransfer(hsd->Instance);
if(errorstate != HAL_SD_ERROR_NONE)
{
hsd->ErrorCode |= errorstate;
HAL_SD_ErrorCallback(hsd);
}
}
/* Clear all the static flags */
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS);
hsd->State = HAL_SD_STATE_READY;
if(((context & SD_CONTEXT_READ_SINGLE_BLOCK) != 0U) || ((context & SD_CONTEXT_READ_MULTIPLE_BLOCK) != 0U))
{
HAL_SD_RxCpltCallback(hsd);
}
else
{
HAL_SD_TxCpltCallback(hsd);
}
}
else if((context & SD_CONTEXT_DMA) != 0U)
{
hsd->Instance->DLEN = 0;
hsd->Instance->DCTRL = 0;
hsd->Instance->IDMACTRL = SDMMC_DISABLE_IDMA;
/* Stop Transfer for Write Single/Multi blocks or Read Multi blocks */
if(((context & SD_CONTEXT_READ_MULTIPLE_BLOCK) != 0U) || ((context & SD_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U))
{
errorstate = SDMMC_CmdStopTransfer(hsd->Instance);
if(errorstate != HAL_SD_ERROR_NONE)
{
hsd->ErrorCode |= errorstate;
HAL_SD_ErrorCallback(hsd);
}
}
hsd->State = HAL_SD_STATE_READY;
if(((context & SD_CONTEXT_WRITE_SINGLE_BLOCK) != 0U) || ((context & SD_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U))
{
HAL_SD_TxCpltCallback(hsd);
}
if(((context & SD_CONTEXT_READ_SINGLE_BLOCK) != 0U) || ((context & SD_CONTEXT_READ_MULTIPLE_BLOCK) != 0U))
{
HAL_SD_RxCpltCallback(hsd);
}
}
else
{
/* Nothing to do */
}
}
else if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_TXFIFOHE) != RESET)
{
SD_Write_IT(hsd);
}
else if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_RXFIFOHF) != RESET)
{
SD_Read_IT(hsd);
}
else if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_TXUNDERR) != RESET)
{
/* Set Error code */
if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_DCRCFAIL) != RESET)
{
hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL;
}
if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_DTIMEOUT) != RESET)
{
hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT;
}
if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_RXOVERR) != RESET)
{
hsd->ErrorCode |= HAL_SD_ERROR_RX_OVERRUN;
}
if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_TXUNDERR) != RESET)
{
hsd->ErrorCode |= HAL_SD_ERROR_TX_UNDERRUN;
}
/* Clear All flags */
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS);
/* Disable all interrupts */
__HAL_SD_DISABLE_IT(hsd, SDMMC_IT_DATAEND | SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT|\
SDMMC_IT_TXUNDERR| SDMMC_IT_RXOVERR);
__SDMMC_CMDTRANS_DISABLE( hsd->Instance);
hsd->Instance->DCTRL |= SDMMC_DCTRL_FIFORST;
hsd->Instance->CMD |= SDMMC_CMD_CMDSTOP;
hsd->ErrorCode |= SDMMC_CmdStopTransfer(hsd->Instance);
hsd->Instance->CMD &= ~(SDMMC_CMD_CMDSTOP);
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_DABORT);
if((context & SD_CONTEXT_IT) != 0U)
{
/* Set the SD state to ready to be able to start again the process */
hsd->State = HAL_SD_STATE_READY;
HAL_SD_ErrorCallback(hsd);
}
else if((context & SD_CONTEXT_DMA) != 0U)
{
if(hsd->ErrorCode != HAL_SD_ERROR_NONE)
{
/* Disable Internal DMA */
__HAL_SD_DISABLE_IT(hsd, SDMMC_IT_IDMABTC);
hsd->Instance->IDMACTRL = SDMMC_DISABLE_IDMA;
/* Set the SD state to ready to be able to start again the process */
hsd->State = HAL_SD_STATE_READY;
HAL_SD_ErrorCallback(hsd);
}
}
else
{
/* Nothing to do */
}
}
else if(__HAL_SD_GET_FLAG(hsd, SDMMC_IT_IDMABTC) != RESET)
{
if(READ_BIT(hsd->Instance->IDMACTRL, SDMMC_IDMA_IDMABACT) == 0U)
{
/* Current buffer is buffer0, Transfer complete for buffer1 */
if((hsd->Context & SD_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U)
{
HAL_SDEx_Write_DMADoubleBuffer1CpltCallback(hsd);
}
else /* SD_CONTEXT_READ_MULTIPLE_BLOCK */
{
HAL_SDEx_Read_DMADoubleBuffer1CpltCallback(hsd);
}
}
else /* SD_DMA_BUFFER1 */
{
/* Current buffer is buffer1, Transfer complete for buffer0 */
if((context & SD_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U)
{
HAL_SDEx_Write_DMADoubleBuffer0CpltCallback(hsd);
}
else /* SD_CONTEXT_READ_MULTIPLE_BLOCK */
{
HAL_SDEx_Read_DMADoubleBuffer0CpltCallback(hsd);
}
}
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_FLAG_IDMABTC);
}
else
{
/* Nothing to do */
}
}
2019-01-19 11:26 AM
Do you have a debug UART available? Which UART/PINS?
2019-01-19 12:50 PM