2025-02-27 11:09 AM
Hi,
I am working on a custom project with the STM32H755ZI (Cortex-M4 core) using the SDMMC peripheral and FatFs to read from SD cards. The SD card is inserted into a slot on my custom PCB, and the SDMMC peripheral is clocked via PLL2, because of this problem :(https://community.st.com/t5/stm32-mcus-embedded-software/stm32h757-sdmmc1-only-works-with-m7-core/td-p/711340) I have tested several SD cards:
However, I am running into a blocking issue when calling:
fres = f_mount(&SDFatFS, "", 1);
The call stalls for approximately 30 seconds in the function SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count) ( sd_diskio.c), specifically at the line:
status = osMessageQueueGet(SDQueueID, (void *)&event, NULL, SD_TIMEOUT);
I understand this means the code is waiting for a DMA completion message (READ_CPLT_MSG), but it never arrives within the timeout window. As a result, the f_mount call does not complete successfully.
When stepping through the debugger, I do see that HAL_SD_IRQHandler() is called during the process. The hsd->SdCard.BlockNbr in that handler shows 60,518,400 blocks, which corresponds to 32 GB, so it appears the card capacity is being recognized. However, the interrupt eventually ends with the error flag HAL_SD_ERROR_RX_OVERRUN, so the read operation fails.
Below is the critical part of my sd_diskio.c (or SD_read function) where the code times out. The function is generated from STM32CubeMX
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
uint8_t ret;
DRESULT res = RES_ERROR;
uint32_t timer;
#if (osCMSIS < 0x20000U)
osEvent event;
#else
uint16_t event;
osStatus_t status;
#endif
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
uint32_t alignedAddr;
#endif
/*
* ensure the SDCard is ready for a new operation
*/
if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0)
{
return res;
}
#if defined(ENABLE_SCRATCH_BUFFER)
if (!((uint32_t)buff & 0x3))
{
#endif
/* Fast path cause destination buffer is correctly aligned */
ret = BSP_SD_ReadBlocks_DMA((uint32_t*)buff, (uint32_t)(sector), count);
if (ret == MSD_OK) {
#if (osCMSIS < 0x20000U)
/* wait for a message from the queue or a timeout */
event = osMessageGet(SDQueueID, SD_TIMEOUT);
if (event.status == osEventMessage)
{
if (event.value.v == READ_CPLT_MSG)
{
timer = osKernelSysTick();
/* block until SDIO IP is ready or a timeout occur */
while(osKernelSysTick() - timer <SD_TIMEOUT)
#else
status = osMessageQueueGet(SDQueueID, (void *)&event, NULL, SD_TIMEOUT);
if ((status == osOK) && (event == READ_CPLT_MSG))
{
timer = osKernelGetTickCount();
/* block until SDIO IP is ready or a timeout occur */
while(osKernelGetTickCount() - timer <SD_TIMEOUT)
#endif
{
if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
{
res = RES_OK;
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
/*
the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address,
adjust the address and the D-Cache size to invalidate accordingly.
*/
alignedAddr = (uint32_t)buff & ~0x1F;
SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
#endif
break;
}
}
#if (osCMSIS < 0x20000U)
}
}
#else
}
#endif
}
#if defined(ENABLE_SCRATCH_BUFFER)
}
else
{
/* Slow path, fetch each sector a part and memcpy to destination buffer */
int i;
for (i = 0; i < count; i++)
{
ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
if (ret == MSD_OK )
{
/* wait until the read is successful or a timeout occurs */
#if (osCMSIS < 0x20000U)
/* wait for a message from the queue or a timeout */
event = osMessageGet(SDQueueID, SD_TIMEOUT);
if (event.status == osEventMessage)
{
if (event.value.v == READ_CPLT_MSG)
{
timer = osKernelSysTick();
/* block until SDIO IP is ready or a timeout occur */
while(osKernelSysTick() - timer <SD_TIMEOUT)
#else
status = osMessageQueueGet(SDQueueID, (void *)&event, NULL, SD_TIMEOUT);
if ((status == osOK) && (event == READ_CPLT_MSG))
{
timer = osKernelGetTickCount();
/* block until SDIO IP is ready or a timeout occur */
ret = MSD_ERROR;
while(osKernelGetTickCount() - timer < SD_TIMEOUT)
#endif
{
ret = BSP_SD_GetCardState();
if (ret == MSD_OK)
{
break;
}
}
if (ret != MSD_OK)
{
break;
}
#if (osCMSIS < 0x20000U)
}
}
#else
}
#endif
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
/*
*
* invalidate the scratch buffer before the next read to get the actual data instead of the cached one
*/
SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE);
#endif
memcpy(buff, scratch, BLOCKSIZE);
buff += BLOCKSIZE;
}
else
{
break;
}
}
if ((i == count) && (ret == MSD_OK ))
res = RES_OK;
}
#endif
return res;
}
And in the interrupt handler (HAL_SD_IRQHandler), I see:
if (__HAL_SD_GET_FLAG(hsd, SDMMC_IT_RXOVERR) != RESET)
{
hsd->ErrorCode |= HAL_SD_ERROR_RX_OVERRUN;
}
Once that RX_OVERRUN error is set, it never triggers the correct completion event, so osMessageQueueGet() times out after 30 seconds.
Has anyone encountered an HAL_SD_ERROR_RX_OVERRUN during f_mount with SDHC/SDXC cards on STM32H7 devices?
I am attaching below the main configuration details of SDMMC and FatFs from STM32Cube (let me know if you need more detail):
Any guidance or suggestions would be greatly appreciated! Thank you in advance.