cancel
Showing results for 
Search instead for 
Did you mean: 

STM32U5 SDMMC1 FATFS integration

Julian_
Associate III

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.

Julian__0-1746000981422.png

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

15 REPLIES 15
KORKAD
ST Employee

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

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

Julian_
Associate III

@KORKADHi, are there any other suggestions to this issue? Would be great to get an update. Thanks.

Hi Julian,

Sorry for the delay. FATFS is supported natively over CubeMX on H7RS products. You can refer to the generated code as reference code.

 

 

 

Julian_
Associate III

@KORKADThanks for the reference. I checked out the FatFs driver implementation for H7RS products. Therefore I downloaded the STM32Cube_FW_H7RS_V1.2.0 and compared the sd_diskio.c implemtation to my own implementation. Basiclly it's more or less the some - despite this fact I used the implementation in en.stm32cubeh7rs-v1-2-0.zip\STM32Cube_FW_H7RS_V1.2.0\Middlewares\Third_Party\FatFs\source\drivers\sd->diskio.c to test this too.

Unfortunally I got the same issue with the never ending clock that appears sometimes. Do you know if the blocking read/write access to the sd card is possible in an own RTOS task. Or should switch to sd_diskio_dma_rtos.c? My preference is the blocking code, if possible.

I appreciate your support hopefully we can fix the issue.

 

Best,

Julian

Current diskio.c in my project:

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

#define SD_TIMEOUT (1000)

extern SD_HandleTypeDef hsd1;
static volatile DSTATUS Stat = STA_NOINIT;


/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	  Stat = STA_NOINIT;

	  if (HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_TRANSFER)
	  {
	    Stat &= ~STA_NOINIT;
	  }

	  return Stat;
}



/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE lun				/* Physical drive nmuber to identify the drive */
)
{
	Stat = STA_NOINIT;

	#if (ENABLE_SD_INIT == 1)
	  sdmmc_sd_init();
	  Stat = SD_check_status(lun);
	#else
	  Stat = disk_status(lun);
	#endif
	  return Stat;
}



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

    DRESULT res = RES_ERROR;

    if (HAL_SD_ReadBlocks(&hsd1, (uint8_t*)buff, sector, count, SD_TIMEOUT) == HAL_OK)
    {
      /* Wait until the card state is ready */
      while (HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER)
      {
      }
      res = RES_OK;
    }
    return res;
}



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

	DRESULT res = RES_ERROR;

	if (HAL_SD_WriteBlocks(&hsd1, (uint8_t*)buff, sector, count, SD_TIMEOUT) == HAL_OK)
	{
	   /* Wait until the card state is ready */
	   while (HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER)
	   {
	   }
	   res = RES_OK;
	  }
	  return res;
}

#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	  DRESULT res = RES_ERROR;
	  HAL_SD_CardInfoTypeDef CardInfo;

	  if (Stat & STA_NOINIT) return RES_NOTRDY;

	  switch (cmd)
	  {
	     /* Make sure that no pending write process */
	     case CTRL_SYNC:
	       res = RES_OK;
	       break;

	     /* Get number of sectors on the disk (DWORD) */
	     case GET_SECTOR_COUNT:
	       HAL_SD_GetCardInfo(&hsd1, &CardInfo);
	       *(DWORD*)buff = CardInfo.LogBlockNbr;
	       res = RES_OK;
	       break;

	     /* Get R/W sector size (WORD) */
	     case GET_SECTOR_SIZE:
	       HAL_SD_GetCardInfo(&hsd1, &CardInfo);
	       *(WORD*)buff = CardInfo.LogBlockSize;
	       res = RES_OK;
	       break;

	     /* Get erase block size in unit of sector (DWORD) */
	     case GET_BLOCK_SIZE:
	       HAL_SD_GetCardInfo(&hsd1, &CardInfo);
	       *(DWORD*)buff = CardInfo.LogBlockSize / BLOCKSIZE;
	       res = RES_OK;
	       break;

	     default:
	       res = RES_PARERR;
	  }

	  return res;
}

Hello @Julian_ 

To effectively debug the issue, start by ensuring the SDMMC functions correctly using only HAL. Once confirmed, integrate FATFS and verify its operation. Finally, incorporate FreeRTOS and perform another check.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om
Julian_
Associate III

@Saket_Om  @KORKAD 

The SDMMC functions seems to work running in main.c without the RTOS. Today I checked if the FATFS works in main without starting the OS. f_mount, f_open, f_write seems to work fine but I used HAL_Delay(20) and after any function of fatfs is called the system tick isn't incremented so the code stucks in an endless loop when using HAL_Delay() maybe this is an interessting fact for you - to give me a hint.

HAL LAyer works fine:

  while(1){
	  HAL_StatusTypeDef res;

	      // Wait until SD card is ready
	      while (HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER) {
	          HAL_Delay(1);
	      }

	      printf("Writing to SD sector %lu...\n", (unsigned long)TEST_SECTOR);
	      res = HAL_SD_WriteBlocks(&hsd1, tx_buffer, TEST_SECTOR, 1, 1000);
	      if (res != HAL_OK) {
	          printf("Write failed: %d\n", res);

	      }

	      // Wait until SD card is ready
	      while (HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER) {
	          HAL_Delay(1);
	      }

	      printf("Reading back from SD...\n");
	      res = HAL_SD_ReadBlocks(&hsd1, rx_buffer, TEST_SECTOR, 1, 1000);
	      if (res != HAL_OK) {
	          printf("Read failed: %d\n", res);

	      }

	      while (HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER) {
	          HAL_Delay(1);
	      }

	      // Verify
	      if (memcmp(tx_buffer, rx_buffer, SECTOR_SIZE) == 0) {
	          printf("SD test successful: data match.\n");
	      } else {
	          printf("SD test failed: data mismatch.\n");
	      }

	      HAL_Delay(5);
  }

When using:

while(1){
 HAL_Delay(20);
 HAL_Delay(100);
)

// ticks increment so NVIC and Hal is initialized properly

 

When using :

while (1){

	  // Mount SD card <-- mounting works
	  fres = f_mount(&fs, "0", 1);
	  if (fres != FR_OK) {
		  printf("Mount failed: %d\n", fres);
	  }
	  printf("Mount successful.\n");

	  HAL_Delay(20); <- stuck in endless loop due to uwTick isnt incremented an always 153

	  // Create or overwrite file
	  fres = f_open(&file, "testfile.txt", FA_WRITE | FA_CREATE_ALWAYS);
	  if (fres != FR_OK) {
		  printf("File create failed: %d\n", fres);
	  }

	  HAL_Delay(20);

	  fres = f_write(&file, writeData, strlen(writeData), &bytesWritten);
	  if (fres != FR_OK || bytesWritten == 0) {
		  printf("Write failed: %d\n", fres);
		  f_close(&file);
	  }

	  HAL_Delay(20);

	  f_close(&file);

	  HAL_Delay(20);

	  f_mount(NULL, "", 1);
	  printf("FatFS test complete.\n");
	  HAL_Delay(20);

  }

 

I don't know exaclly why and where the fatfs should change NVIC settings or disable interrupts.

 

Would be nice to get some input from your side.

 

Thanks and kind regards,

Julian

@Julian_ 

You should configure timer (TIM6 for example) as time base when using FrreRTOS because Systick is used by the OS. 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om
Julian_
Associate III

@Saket_Omhi and thanks for the fast reply. What I don't undestand right now is I just want to test the fatfs without RTOS as you mentioned before the seperate HAL,FatFS and RTOS to know where the issue is coming from.

Therefopre I put some FatFs code to the main before the RTOS is inializied and starts the scheduler. As you see in the code snippet from main the first Delay works fine, but after the f_mount the delay doesn't work anymore.

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the System Power */
  SystemPower_Config();

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_GPDMA1_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_CORDIC_Init();
  MX_CRC_Init();
  MX_DAC1_Init();
  MX_DCACHE1_Init();
  MX_DCACHE2_Init();
  MX_DMA2D_Init();
  MX_FDCAN1_Init();
  MX_GPU2D_Init();
  MX_HASH_Init();
  MX_I2C2_Init();
  MX_I2C4_Init();
  MX_ICACHE_Init();
  MX_LPTIM2_Init();
  MX_LTDC_Init();
  MX_OCTOSPI1_Init();
  MX_RNG_Init();
  MX_RTC_Init();
  MX_SPI1_Init();
  MX_SPI2_Init();
  MX_TIM3_Init();
  MX_TIM5_Init();
  MX_TIM6_Init();
  MX_TIM8_Init();
  MX_TIM15_Init();
  MX_I2C1_Init();
  MX_USART1_UART_Init();
  MX_SDMMC1_SD_Init();
  //MX_TouchGFX_Init();
  /* Call PreOsInit function */
  //MX_TouchGFX_PreOSInit();

  /* Initialize interrupts */
  MX_NVIC_Init();
  /* USER CODE BEGIN 2 */
  //MX_SDMMC1_SD_Init();

  char writeData[] = "Testdata written";
  char readBuffer[100];

  FATFS fs;
  FIL file;
  FRESULT fres;
  UINT bytesWritten, bytesRead;

  HAL_Delay(50); //this delay works

  while (1){

	  // Mount SD card
	  fres = f_mount(&fs, "0", 1);
	  if (fres != FR_OK) {
		  printf("Mount failed: %d\n", fres);
		  //return;
	  }
	  printf("Mount successful.\n");

	  HAL_Delay(20); //this delay doesn't work anymore

	  // Create or overwrite file
	  fres = f_open(&file, "testfile.txt", FA_WRITE | FA_CREATE_ALWAYS);
	  if (fres != FR_OK) {
		  printf("File create failed: %d\n", fres);
		  //return;
	  }

	  HAL_Delay(20);

	  fres = f_write(&file, writeData, strlen(writeData), &bytesWritten);
	  if (fres != FR_OK || bytesWritten == 0) {
		  printf("Write failed: %d\n", fres);
		  f_close(&file);
		  //return;
	  }

	  HAL_Delay(20);

	  f_close(&file);

	  HAL_Delay(20);

	  // Reopen and read
	//  fres = f_open(&file, "test.txt", FA_READ);
	//  if (fres != FR_OK) {
	//	  printf("Open for read failed: %d\n", fres);
	//	  return;
	//  }


	  //f_close(&file);

	  // Unmount
	  //f_mount(NULL, "", 1);
	  printf("FatFS test complete.\n");

	  HAL_Delay(20);
	  // Mount SD card
  }


  SD_Init();
  /* USER CODE END 2 */

  /* Init scheduler */
  osKernelInitialize();

  /* Call init function for freertos objects (in app_freertos.c) */
  MX_FREERTOS_Init();

  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

I don't know why or where the fatfs should change the timebase or the systick. Do you have a hint for me? 

 

Thanks and kind regards,

Julian