2025-04-30 1:49 AM
Hi guys,
I'm trying to implement a sd card interface for the STM32U5 series using SDMMC1 interface. Since there is no native FATFS support over CubeMX, I guess, I started to use the generic fat file module from elm-chan.
Basic configuration:
1. SDMMC1 in SD 4 bits wide bus mode, clock divider 4, power safe for clock enabled, no hardware control, no external transceiver, SDMMC1 global interrupt enabled, gpios pullup all enabled despite of clk
2. Modified diskio.c see code example
The interface works and I can also kind of work with fatFS, f_mount, f_open works basiclly, but sometimes I got a FR_DISK_ERR. On my scope I can see that the SD clock is always on when the error occures. So I think there is an issue with the low level driver level or better say somethimg is going on with the diskio layer. Please see the attached pictures of working f_mount and f_open and the f_open where the clock doesn't stop.
I'm using FreeRTOS and I have a dedicated SD_Card Task.
My modified diskio.c:
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "stm32u5xx_hal.h"
#include "sdmmc.h"
/* Definitions of physical drive number for each drive */
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
extern SD_HandleTypeDef hsd1;
DSTATUS Stat = STA_NOINIT;
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
if (pdrv == 0) {
HAL_SD_CardStateTypeDef state = HAL_SD_GetCardState(&hsd1);
if (state == HAL_SD_CARD_TRANSFER || state == HAL_SD_CARD_READY) {
return RES_OK; // Card is ready
} else {
return STA_NOINIT; // Card is not ready
}
}
return STA_NOINIT; // Only drive 0 supported
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
if (pdrv != 0) return STA_NOINIT; // Only support drive 0 for SD
if (!(Stat & STA_NOINIT)) // If already initialized
{
return RES_OK; // No need to reinitialize
}
// Otherwise, maybe check card presence etc if needed
if (HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_TRANSFER) // Already ready
{
Stat &= ~STA_NOINIT; // Clear the NOINIT bit
return RES_OK;
}
// (Optional) If you really need HAL_SD_Init() again, only under safe conditions
return STA_NOINIT; // Fail if not ready
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
if (pdrv != 0) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
HAL_StatusTypeDef hal_res = HAL_SD_ReadBlocks(&hsd1, buff, sector, count, HAL_MAX_DELAY);
if (hal_res == HAL_OK)
{
/* Wait until SD card is ready */
uint32_t timeout = HAL_GetTick() + 500; // 500ms timeout
while (HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER)
{
if (HAL_GetTick() > timeout)
{
return RES_ERROR; // Timeout, SD busy too long
}
osDelay(1); // yield to other FreeRTOS tasks
}
return RES_OK;
}
return RES_ERROR;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
if (pdrv != 0) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
HAL_StatusTypeDef hal_res = HAL_SD_WriteBlocks(&hsd1, (uint8_t*)buff, sector, count, HAL_MAX_DELAY);
if (hal_res == HAL_OK)
{
/* Wait until SD card is ready */
uint32_t timeout = HAL_GetTick() + 500; // 500ms timeout
while (HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER)
{
if (HAL_GetTick() > timeout)
{
return RES_ERROR; // Timeout
}
osDelay(1); // yield in FreeRTOS
}
return RES_OK;
}
return RES_ERROR;
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
if (pdrv != 0) return RES_PARERR;
HAL_SD_CardInfoTypeDef info;
HAL_SD_GetCardInfo(&hsd1, &info);
switch (cmd) {
case GET_SECTOR_COUNT:
*(DWORD*)buff = info.LogBlockNbr;
return RES_OK;
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
return RES_OK;
case GET_BLOCK_SIZE:
*(DWORD*)buff = info.LogBlockSize / 512;
return RES_OK;
}
return RES_PARERR;
}
Initalize SDMMC and HAL_SD:
void MX_SDMMC1_SD_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 */
hsd1.Instance = SDMMC1;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING;
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_ENABLE;
hsd1.Init.BusWide = SDMMC_BUS_WIDE_4B;
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
hsd1.Init.ClockDiv = 4;
if (HAL_SD_Init(&hsd1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SDMMC1_Init 2 */
/* USER CODE END SDMMC1_Init 2 */
}
Would be great if I get some input from you guys - how I can fix this.
Thanks,
Julian
2025-04-30 2:58 AM
Hi Julian,
Below is the typical implementation for the MX_SDMMC1_SD_Init()
API used in the STM32CubeH7 package:
/**
* @brief Initializes the SDMMC1 peripheral.
* @PAram hsd SD handle
* @retval HAL status
*/
__weak HAL_StatusTypeDef MX_SDMMC1_SD_Init(SD_HandleTypeDef *hsd)
{
HAL_StatusTypeDef ret = HAL_OK;
/* uSD device interface configuration */
hsd->Instance = SDMMC1;
hsd->Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
hsd->Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
hsd->Init.BusWide = SDMMC_BUS_WIDE_1B;
hsd->Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
hsd->Init.ClockDiv = 4; /* SDMMC_NSpeed_CLK_DIV */
hsd->Init.TranceiverPresent = SDMMC_TRANSCEIVER_NOT_PRESENT;
/* HAL SD initialization */
if(HAL_SD_Init(hsd) != HAL_OK)
{
ret = HAL_ERROR;
}
return ret;
}
2025-04-30 4:10 AM
Thanks for your fast reply. I think that the SDMMC Init and HAL_SD Init is not the root cause of this issue. I already tested some other configuration of the sdmmc like SDMMC_BUS_WIDE_1B instead of SDMMC_BUS_WIDE_4B and clock rising instead of falling etc.
So I think the issue is more related to the diskio setup or better say how the HAL_SD API for writing and reading is implemented to diskio.c
Best,
Julian