cancel
Showing results for 
Search instead for 
Did you mean: 

SDMMC DATA_TIMEOUT occurs when bus is idle

nattayb
Associate II

I'm using an STM32F779 on a known working custom board connected to an EMMC chip (8 wide). I'm using the SDMMC2 peripheral with a DMA channel for Rx as well as Tx and the SDMMC interrupt enabled. My application has two modes, in the first I log data and in the presence of USB it acts as a MSD. When connected via USB the host computer reads out blocks and interprets the file system correctly. However, after some period the SDMMC2 - DATA_TIMEOUT interrupt occurs when it shouldn't and breaks the system (DMA_TX is zeroed and then eventually a hard fault occurs).

The DCTRL register does not self clear after a transaction but is cleared at the beginning of a new read or write. This implies that as long as a read/write is performed at least once before the timer expires there is no issue.

1 ACCEPTED SOLUTION

Accepted Solutions
nattayb
Associate II

Found the problem. Having the SDMMC2 interrupt and the DMA Rx/Tx interrupts enabled at the same time will not work (at least consistently).

  1. When a DMA based read is issued with all three global interrupts enabled the first interrupt to be called is for the Rx DMA complete - MMC_DMAReceiveCplt()
  2. At this point the interrupts have already been queued by the NVIC so the SDMMC interrupt will be called. However, in the DMA handler the interrupts for the peripheral are blindly cleared and then the HAL_MMC_RxCpltCallback is called.
  3. Next the HAL_MMC_IRQHanlder is called but no interrupts appear set. This is because they were cleared in the previous ISR. If they hadn't been cleared then this ISR would eventually see the DATA_END interrupt and clear the device timer bit in the control register of the SDMMC peripheral. However it does not.
  4. If a read/write is not performed by the timeout then it will occur and will be handled incorrectly.
  5. Eventually if the SD bus is idle for long enough, the never-disabled DATA_TIMEOUT will fire (based on SDMMC bus speed) and you'll be in my shoes.

The driver will not work without all three (DMA-Rx, DMA-Tx, SDMMCx) enabled. The solution is to remove line 2544 in MMC_DMAReceiveCplt so that the interrupt is correctly handled by the SDMMC ISR.

  /* Disable the DMA transfer for transmit request by setting the DMAEN bit
  in the MMC DCTRL register */
  hmmc->Instance->DCTRL &= (uint32_t)~((uint32_t)SDMMC_DCTRL_DMAEN);
  
  /* Clear all the static flags */
  //__HAL_MMC_CLEAR_FLAG(hmmc, SDMMC_STATIC_DATA_FLAGS);
  
  hmmc->State = HAL_MMC_STATE_READY;

View solution in original post

1 REPLY 1
nattayb
Associate II

Found the problem. Having the SDMMC2 interrupt and the DMA Rx/Tx interrupts enabled at the same time will not work (at least consistently).

  1. When a DMA based read is issued with all three global interrupts enabled the first interrupt to be called is for the Rx DMA complete - MMC_DMAReceiveCplt()
  2. At this point the interrupts have already been queued by the NVIC so the SDMMC interrupt will be called. However, in the DMA handler the interrupts for the peripheral are blindly cleared and then the HAL_MMC_RxCpltCallback is called.
  3. Next the HAL_MMC_IRQHanlder is called but no interrupts appear set. This is because they were cleared in the previous ISR. If they hadn't been cleared then this ISR would eventually see the DATA_END interrupt and clear the device timer bit in the control register of the SDMMC peripheral. However it does not.
  4. If a read/write is not performed by the timeout then it will occur and will be handled incorrectly.
  5. Eventually if the SD bus is idle for long enough, the never-disabled DATA_TIMEOUT will fire (based on SDMMC bus speed) and you'll be in my shoes.

The driver will not work without all three (DMA-Rx, DMA-Tx, SDMMCx) enabled. The solution is to remove line 2544 in MMC_DMAReceiveCplt so that the interrupt is correctly handled by the SDMMC ISR.

  /* Disable the DMA transfer for transmit request by setting the DMAEN bit
  in the MMC DCTRL register */
  hmmc->Instance->DCTRL &= (uint32_t)~((uint32_t)SDMMC_DCTRL_DMAEN);
  
  /* Clear all the static flags */
  //__HAL_MMC_CLEAR_FLAG(hmmc, SDMMC_STATIC_DATA_FLAGS);
  
  hmmc->State = HAL_MMC_STATE_READY;