STM32CubeL4
STM32CubeL4 copied to clipboard
SDMMC: DMA is disabled too early to complete transfer when reading from SdCard clocked at 16+ MHz from MSI
Describe the set-up
- Custom board with STM32L431RCTx.
- CubeIDE 1.15.1, L4 drivers v 1.18.0
Describe the bug Reading from an SdCard hangs with the following conditions: SDMMC_DCTRL.DMAEN = 0 SDMMC_DCTRL.DTEN = 0 SDMMC_DCOUNT = 0
DMA_CCR5.EN = 1 DMA_CNDTR5 = 1 (!) which means no transfer complete interrupt will be generated ever
The issue is similar to https://community.st.com/t5/stm32-mcus-products/stm32l4-sd-dma-8-mhz-read-failure-due-to-missing-byte/td-p/385880.
How To Reproduce
- Indicate the global behavior of your application project.
SDMMC interface is configured with DMA 2 Channel 5, 1 bit data line SDMMC clock is sourced form MSI @ 48 MHz, NO devider bypass, HW flow control is ON. PCLK2 clock is 8.192 MHz and sourced from HSE via PLL
Call HAL_MMC_ReadBlocks_DMA() in a loop to read chunks of 1 or 8 blocks waiting until read completes, i.e. HAL_MMC_GetCardState() returns HAL_MMC_CARD_TRANSFER, after each HAL_MMC_ReadBlocks_DMA() call.
After some successful reads the next reading hangs with the conditions (register values) described above.
- The modules that you suspect to be the cause of the problem (Driver, BSP, MW ...).
HAL driver for SDMMC
- The use case that generates the problem. (see 1)
- How we can reproduce the problem. (see 1)
Additional context
The root cause is how read completion (SDMMC_STA.DATAEND = 1) is handled in function HAL_MMC_IRQHandler() in stm32l4xx_hal_mmc.c
I did two things in HAL_MMC_IRQHandler() to fix the issue:
- Commented out line https://github.com/STMicroelectronics/stm32l4xx_hal_driver/blob/42d324ac323088701270f2c19f328bcad0457c52/Src/stm32l4xx_hal_mmc.c#L1659
As per the reference manual:
-- It is not necessary to clear the enable bit (SDMMC_DCTRL.DTEN) after the end of a data transfer but the SDMMC_DCTRL must be updated to enable a new data transfer
- Added if statement so the lines 1709-1736 get executed ONLY for write requests.
if (((context & MMC_CONTEXT_WRITE_SINGLE_BLOCK) != 0U) || ((context & MMC_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U))
{
// lines 1709-1736
}
The code in lines 1709-1736 is ALSO present in MMC_DMAReceiveCplt() where it should be executed when DMA reports transfer complete for reads.
Hello @dima-kapustin,
Thank you for the report. Could you please, share your IOC file or an extract of the config or project, at least for SDMMC and DMA. So, I can use it to reproduce the issue.
In the meanwhile, I will try to analyze your fix or workaround.
With regards.
Hi @KRASTM !
Here you go (it is almost standard-generated by CubeIDE):
/**
* Initializes the Global MSP.
*/
void HAL_MspInit(void)
{
/* USER CODE BEGIN MspInit 0 */
/* USER CODE END MspInit 0 */
__HAL_RCC_SYSCFG_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE();
/* System interrupt init*/
/* USER CODE BEGIN MspInit 1 */
/* USER CODE END MspInit 1 */
}
/**
* @brief MMC MSP Initialization
* This function configures the hardware resources used in this example
* @param hmmc: MMC handle pointer
* @retval None
*/
void HAL_MMC_MspInit(MMC_HandleTypeDef* hmmc)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hmmc->Instance==SDMMC1)
{
/* USER CODE BEGIN SDMMC1_MspInit 0 */
LL_GPIO_SetOutputPin(SdCard_D0_GPIO_Port, SdCard_D0_Pin);
LL_GPIO_SetOutputPin(SdCard_CK_GPIO_Port, SdCard_CK_Pin);
LL_GPIO_SetOutputPin(SdCard_CMD_GPIO_Port, SdCard_CMD_Pin);
LL_RCC_SetSDMMCClockSource(LL_RCC_SDMMC1_CLKSOURCE_MSI);
SdCardPowerOnPin::reset();
LL_mDelay(50);
/* USER CODE END SDMMC1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SDMMC1_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**SDMMC1 GPIO Configuration
PC8 ------> SDMMC1_D0
PC12 ------> SDMMC1_CK
PD2 ------> SDMMC1_CMD
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
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_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* SDMMC1 DMA Init */
/* SDMMC1 Init */
hdma_sdmmc1.Instance = DMA2_Channel5;
hdma_sdmmc1.Init.Request = DMA_REQUEST_7;
hdma_sdmmc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_sdmmc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdmmc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdmmc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdmmc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_sdmmc1.Init.Mode = DMA_NORMAL;
hdma_sdmmc1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
if (HAL_DMA_Init(&hdma_sdmmc1) != HAL_OK)
{
Error_Handler();
}
/* Several peripheral DMA handle pointers point to the same DMA handle.
Be aware that there is only one channel to perform all the requested DMAs. */
/* Be sure to change transfer direction before calling
HAL_SD_ReadBlocks_DMA or HAL_SD_WriteBlocks_DMA. */
__HAL_LINKDMA(hmmc,hdmarx,hdma_sdmmc1);
__HAL_LINKDMA(hmmc,hdmatx,hdma_sdmmc1);
/* SDMMC1 interrupt Init */
HAL_NVIC_SetPriority(SDMMC1_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
/* USER CODE BEGIN SDMMC1_MspInit 1 */
SdCardResetPin::set();
LL_mDelay(50);
/* USER CODE END SDMMC1_MspInit 1 */
}
}
static void MX_SDMMC1_MMC_Init(void)
{
/* USER CODE BEGIN SDMMC1_Init 0 */
/* USER CODE END SDMMC1_Init 0 */
/* USER CODE BEGIN SDMMC1_Init 1 */
/* USER CODE END SDMMC1_Init 1 */
hmmc1.Instance = SDMMC1;
hmmc1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
hmmc1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
hmmc1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_ENABLE;
hmmc1.Init.BusWide = SDMMC_BUS_WIDE_1B;
hmmc1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE;
hmmc1.Init.ClockDiv = 0;
if (HAL_MMC_Init(&hmmc1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SDMMC1_Init 2 */
/* USER CODE END SDMMC1_Init 2 */
}
Hello @dima-kapustin,
I tried using the config that you shared, but unfortunately, I didn't manage to reproduce the problem on our board (not the same µc, the L496). If you have ST board that you can use it to test an example based on your config, or if you can share some screenshots of the debug mode while the problem occurs, it can help us to confirm the issue.
In the other hand, I shared your fix and proposal with our team in order to analyze it and confirm it.
With regards,
Hi @KRASTM!
Thanks for pushing it through!
I am afraid I cannot provide more details about the setup at the moment.
We faced the issue when increased SDMMC clock rate over 16 MHz, i.e. 24 MHz and higher. At clock rates lower than or equal to 16 MHz the issue did not appear for years (!).
I think there is fundamental issue in the irq processing flow:
-
in write direction (DMA -> SDMMC -> SdCars) cleanup shall be done when SDMMC reports end of data transmission
-
in read direction (SdCard -> SDMMC -> DMA) cleanup shall be done after DMA reports end of data transmission.
However, in the current implementation the cleanup is done after SDMMC reports end of data transmission in both directions - write and read. When SDMMC clock is low enough, everything works fine. But as soon as SDMMC clock rate increases (24+ MHz in our case) the cleanup fails because DMA gets disabled before it completes data transfer and reports end of the transmission.
Beat regards, Dima.
Hello @dima-kapustin,
There is a section in the datasheet DS11453 page174, about SDMMC characteristics:
Could you have a look at it, and I suggest you change or set the Speed mode into: OSPEEDRy[1:0] = 11, High or very High and give it a try. It may help you.
With regards,
This is one of the first things we tested... No, it does not help with the issue.
Thanks, Dima.
Hello @dima-kapustin,
Regarding your issue, our development team manage to test the driver at 24 MHz, but they didn't encounter any problem. Thus, they consider there is no issue with the SDMMC driver.
Please allow me to close this issue. In the meanwhile, if you find more information to share with us, do not hesitate to log another one. Thank you for your comprehension.
With regards,
Hi @KRASTM ,
What PCLK2 clock value is in your tests?
And what is SDMMC clock source?
Thanks
After intensive testing on hundreds devices during 2 weeks it turned out that SDMMC_DCTRL.DTEN must be reset when RX/TX completes. Hence, we do it in both - the read and the write paths. Please, see the modified file with all the changes described here and above attached. stm32l4xx_hal_mmc.zip