Connecting eMMC to SDMMC1 on the STM32H7
Hi.
I am using SDMMC1-eMMC(16GB), SDMMC2-SD Card connected on a custom board.
Both eMMCs and SD-Cards use FatFS as their file system.
Both work fine when used alone.
However, if you use f_open or f_write on FatFS on eMMC, you sometimes get an error and the FatFS becomes unusable afterward.
My settings are as follows.





I tried to find the cause by disabling all device drivers and features and then enabling them one by one.
The problem I found was that FatFS on eMMC often caused problems if the ability to receive UART data via DMA was enabled.
void MPU_Config(void) {
MPU_Region_InitTypeDef MPU_InitStruct = {0};
/* Disables the MPU */
HAL_MPU_Disable();
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0xD0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.BaseAddress = 0x30000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_4KB;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
#if 1
HAL_MPU_ConfigRegion(&MPU_InitStruct);
#endif
/* Enables the MPU */
HAL_MPU_Enable(MPU_HFNMI_PRIVDEF);
}
MPU_REGION_NUMBER 0 is the MPU setting for the external SDRAM region to use the LTDC screen.
MPU_REGION_NUMBER 1 is the MPU setting for the DMA receive buffer region in the SRAM region of D2.
MPU_REGION_NUMBER 2 is the MPU setting for D1 SRAM.
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
/*RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K*/
RAM_D2_UART (xrw) : ORIGIN = 0x30000000, LENGTH = 1K
RAM_D2_SPI (xrw) : ORIGIN = 0x30000400, LENGTH = 1K
RAM_D2_EXT (xrw) : ORIGIN = 0x30000800, LENGTH = 1K
RAM_D2 (xrw) : ORIGIN = 0x30001000, LENGTH = (288K-3K)
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
SDRAM (xrw) : ORIGIN = 0xD0E00000, LENGTH = 2048K
}This is the mapping address for my memory region.
So I tried turning off the MPU setting for the D1 SRAM, in which case FatFS works fine, but I have problems receiving UART.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file MMC_diskio.c
* @brief This file includes a diskio driver skeleton to be completed by the user.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/*
* Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
* To be suppressed in the future.
* Kept to ensure backward compatibility with previous CubeMx versions when
* migrating projects.
* User code previously added there should be copied in the new user sections before
* the section contents can be deleted.
*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif
/* USER CODE BEGIN DECL */
/* Includes ------------------------------------------------------------------*/
#include "ff_gen_drv.h"
#include "bsp_driver_mmc.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define MMC_DEFAULT_BLOCK_SIZE 512
/* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;
/* USER CODE END DECL */
/* Private function prototypes -----------------------------------------------*/
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */
Diskio_drvTypeDef USER_Driver =
{
USER_initialize,
USER_status,
USER_read,
#if _USE_WRITE
USER_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes a Drive
* @PAram pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
/* USER CODE BEGIN INIT */
// unused parameter
UNUSED(pdrv);
// set state
Stat = STA_NOINIT;
// initialize bsp
if (BSP_MMC_Init() == MMC_OK)
// check status
if (BSP_MMC_GetCardState() == MMC_TRANSFER_OK)
// reset state
Stat &= ~STA_NOINIT;
// result
return Stat;
/* USER CODE END INIT */
}
/**
* @brief Gets Disk Status
* @PAram pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_status (
BYTE pdrv /* Physical drive number to identify the drive */
)
{
/* USER CODE BEGIN STATUS */
// unused parameter
UNUSED(pdrv);
// set state
Stat = STA_NOINIT;
// check status
if (BSP_MMC_GetCardState() == MMC_TRANSFER_OK)
// reset state
Stat &= ~STA_NOINIT;
// result
return Stat;
/* USER CODE END STATUS */
}
/**
* @brief Reads Sector(s)
* @PAram pdrv: Physical drive number (0..)
* @PAram *buff: Data buffer to store read data
* @PAram sector: Sector address (LBA)
* @PAram count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT USER_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */
)
{
/* USER CODE BEGIN READ */
DRESULT res;
// read blocks
if (BSP_MMC_ReadBlocks((uint32_t *) buff, (uint32_t) sector, count) == MMC_OK) {
// check card state
while (BSP_MMC_GetCardState() != MMC_TRANSFER_OK) {
}
// set result
res = RES_OK;
} else {
// set error
res = RES_NOTRDY;
}
// unused parameter
UNUSED(pdrv);
return res;
/* USER CODE END READ */
}
/**
* @brief Writes Sector(s)
* @PAram pdrv: Physical drive number (0..)
* @PAram *buff: Data to be written
* @PAram sector: Sector address (LBA)
* @PAram count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
DRESULT USER_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
/* USER CODE BEGIN WRITE */
DRESULT res;
// write blocks
if (BSP_MMC_WriteBlocks((uint32_t *) buff, (uint32_t) sector, count) == MMC_OK) {
// check card state
while (BSP_MMC_GetCardState() != MMC_TRANSFER_OK) {
}
// set result
res = RES_OK;
} else {
// set error
res = RES_NOTRDY;
}
// unused parameter
UNUSED(pdrv);
return res;
/* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
/**
* @brief I/O control operation
* @PAram pdrv: Physical drive number (0..)
* @PAram cmd: Control code
* @PAram *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
/* USER CODE BEGIN IOCTL */
DRESULT res;
HAL_MMC_CardInfoTypeDef info;
// check state
if (Stat & STA_NOINIT)
// not ready
return RES_NOTRDY;
// set result
res = RES_OK;
// check command
switch (cmd) {
case CTRL_SYNC: // Make sure that no pending write process
break;
case GET_SECTOR_COUNT: // Get number of sectors on the disk (DWORD)
// get card info
BSP_MMC_GetCardInfo(&info);
// set data
*(DWORD *) buff = info.LogBlockNbr;
break;
case GET_SECTOR_SIZE: // Get R/W sector size (WORD)
// get card info
BSP_MMC_GetCardInfo(&info);
// set data
*(DWORD *) buff = info.LogBlockSize;
break;
case GET_BLOCK_SIZE: // Get erase block size in unit of sector (DWORD)
// get card info
BSP_MMC_GetCardInfo(&info);
// set data
*(DWORD *) buff = info.LogBlockSize / MMC_DEFAULT_BLOCK_SIZE;
break;
default:
// set error
res = RES_PARERR;
break;
}
// check card state
while (BSP_MMC_GetCardState() != MMC_TRANSFER_OK) {}
// unused parameter
UNUSED(pdrv);
return res;
/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */
The following code implements a disk file for eMMC. Note that we have not used DMA.
I'm guessing it's a cache issue with memory between SDMMC1 and the UART, but I don't know the exact cause.
Can you tell me what I need to fix?
