cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 unreliable micro SD transfers

AHigg
Associate II

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.

1 ACCEPTED SOLUTION

Accepted Solutions
AHigg
Associate II

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;
}

View solution in original post

19 REPLIES 19

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

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
AHigg
Associate II

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

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

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

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.

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?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

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)
//

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

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:

  • My PCB can reliably perform low level DMA transfers to micro SD. I have run extended read/write tests at 25Mhz
  • With FatFs in place, I can successfully read files at 50MHz and write them at 10MHz
  • When I crank up the speed on the write test, the state machine will crash following a stop command
  • When I use my product level code that attempts to read many things at once, the state machine will crash following a stop command
  • I did obtain a Nucleo board and the WaveShare micro SD module. My initial tests yielded worse results than my custom board, so I stopped pursuing it.

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 */
  }
}

Do you have a debug UART available? Which UART/PINS?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
AHigg
Associate II
I’m not sure where you are headed with this. I do have a debug port available on USART2, pins PD5 and PD6. I haven’t used it before.
I am using a Segger J-Link on the SWD port for debugging purposes.
I did run some of your test code on the Nucleo with the WaveShare micro SD socket. I wasn’t sure what to expect so it was of limited use.
Thank you and please let me know how I can help.
Aaron Higgins
1010music LLC
aaron@1010music.com