cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H745I-DISCO eMMC, FATFS, HAL... almost works.

HTD
Senior III

From the example available at

https://github.com/STMicroelectronics/STM32CubeH7/tree/master/Projects/STM32H745I-DISCO/Examples/MMC/MMC_ReadWrite_DMA

I figured out how to configure SDMMC1 using STM32CubeIDE.

I set the clock for MMC for 200MHz, then set up the MMC clock divider to 2. It's identical like in the example.

I run MX_SDMMC1_MMC_Init(); and it completes without errors.

Then I run following code:

void MMCFormat()
{
  debug("Formatting MMC...");
  static TCHAR workBuffer[_MAX_SS];
  FRESULT fr = f_mkfs(MMCPath, FM_EXFAT, 0, workBuffer, sizeof(workBuffer));
  if (fr == FR_OK)
    debug("eMMC formatted successfully.");
  else
    debug("ERROR: eMMC f_mkfs().");
}

That makes the MMC_write() function to fail with TX buffer underrun error.

Here's the original function from mmc_diskio.c file:

DRESULT MMC_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
  DRESULT res = RES_ERROR;
  WriteStatus = 0;
 /*
  * since the MPU is configured as write-through, see main.c file, there isn't any need
  * to maintain the cache as its content is always coherent with the memory.
  * If needed, check the file "Middlewares/Third_Party/FatFs/src/drivers/sd_diskio_dma_template.c"
  * to see how the cache is maintained during the write operations.
  */
 
  while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  {
  }
  if(BSP_MMC_WriteBlocks(0, (uint32_t*)buff,
                            (uint32_t)(sector),
                            count) == BSP_ERROR_NONE)
  {
    while(BSP_MMC_GetCardState(0) != BSP_ERROR_NONE)
    {
    }
    res = RES_OK;
  }
  HAL_HSEM_Release(EMMC_HSEM_ID, 0);
  if (res != 0)
  {
    debug("ERROR: MMC_write().");
  }
  return res;
}

As I read the BSP_MMC_WriteBlocks() function is unreliable, it gets buffer underrun errors, so I replaced it with BSP_MMC_WriteBlocks_DMA().

Then the interesting thing happens, the first call passes, but another one fails on following check:

if (hmmc->State == HAL_MMC_STATE_READY)

The state is HAL_MMC_STATE_BUSY.

Why is that? Where to go from there?

UPDATE:

I've done something completely silly! I overwritten the hmmc->State with HAL_MMC_STATE_READY. Just like that, just look at the MMC_write() function:

DRESULT MMC_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
  DRESULT res = RES_ERROR;
  WriteStatus = 0;
 /*
  * since the MPU is configured as write-through, see main.c file, there isn't any need
  * to maintain the cache as its content is always coherent with the memory.
  * If needed, check the file "Middlewares/Third_Party/FatFs/src/drivers/sd_diskio_dma_template.c"
  * to see how the cache is maintained during the write operations.
  */
 
  while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  {
  }
//  while (hsd_sdmmc[0].State == HAL_MMC_STATE_BUSY) { osDelay(1); }
  hsd_sdmmc[0].State = HAL_MMC_STATE_READY;
  if(BSP_MMC_WriteBlocks_DMA(0, (uint32_t*)buff,
                            (uint32_t)(sector),
                            count) == BSP_ERROR_NONE)
  {
    while(BSP_MMC_GetCardState(0) != BSP_ERROR_NONE)
    {
    }
    res = RES_OK;
  }
  HAL_HSEM_Release(EMMC_HSEM_ID, 0);
  if (res != 0)
  {
    debug("ERROR: MMC_write().");
  }
  return res;
}

Of course I've changed also the MMC_read function accordingly.

f_disk() passes. But well, f_mount() returns FR_NO_FILESYSTEM then. Still not there. 

1 REPLY 1
HTD
Senior III

OK, I've solved the case.

Here are steps to make it work:

  1. Configure SDMMC1 to use 8-bit bus, 50MHz clock after division (in my case - 200MHz clock, divider is 2).
  2. Configure FATFS middleware for any device, MMC is not available, USER option can be selected. The key part is to check all the option allowing to use long file names and UTF, optionally exFAT. I just added MMC binding manually, without USER option.
  3. Configure MDMA for SDMMC1 transfers. I just added all available, IDK if it's necessary.
  4. Get the diskio.c template for DMA / RTOS from https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Middlewares/Third_Party/FatFs/src/drivers/sd_diskio_dma_rtos_template_bspv2.c
  5. REPLACE ALL BSP API functions with HAL functions, paying close attention to different return values for BSP and HAL API.
  6. Done. It works.

Of course it's valid only when using RTOS, that is a default option for TouchGFX and that board. The FatFS functions are available when RTOS kernel is running, I mount the eMMC FS in RTOS task. I mounted actually 2 file systems, to access the files I need to provide full path, like "0:/file.dat" or "1:/file.dat", because when I used the file name alone it looked for it on wrong file system (USB disk that was not present at the time).

Parts I missed that costed me over 100 hours of banging my head against the wall:

  1. Check if I use the I/O function on the right handle.
  2. Use only DMA transfers, polling mode fails miserably.
  3. Check what HAL_MMC_GetCardState() function returns.
  4. Check if BSP_MMC_ReadCpltCallback() and BSP_MMC_WriteCpltCallback() functions are called. They should be implemented and awaited after I/O.

When f_mount() or f_mkfs() is called, the first function in the driver that is called is MMC_initialize(). The template has DISABLE_MMC_INIT macro to skip internal initialization. That is crucial when the SDMMC1 is already initialized in main(). This macro MUST be defined. On MMC_initialize() only HAL_MMC_GetCardState() should be called.