[STM32H7] 【STM32H745I-DISCO试用】6、FATFS访问EMMC

[复制链接]
2007|21
 楼主| sujingliang 发表于 2025-1-25 14:19 | 显示全部楼层 |阅读模式
本帖最后由 sujingliang 于 2025-1-25 14:42 编辑

用USB MSC+ eMMC实现U盘,本质上是实现电脑访问开发板上eMMC设备进行文件访问。
本文将通过FATFS实现访问eMMC。

这样就实现了电脑端和MCU都可以访问eMMC,那么就可以用eMMC交换文件,比如电脑拷贝文件到eMMC,MCU打开eMMC中的文件。

在前文(【STM32H745I-DISCO试用】5、eMMC+USB MSC=U盘https://bbs.21ic.com/icview-3429458-1-1.html)
实现功能基础上继续配置:


一、STM32CuteMX
38.png
为了简化只在CM7核心使用

模式选择:User-defined(因为上面4个mode都不适合eMMC)
函数选择看需要吧,常用的都选上。
USE_LFN:支持长文件名

39.png
FS_EXFAT:支持exFAT文件系统

二、程序修改
为了使FATFS支持eMMC,需要在user_diskio.c实现几个接口函数,这个和配置eMMC+USB MSC类似。

4719367947dee10596.png

为了简便,引入了bsp下的mmc支持文件,这样可以调用其中的函数:
41.png


  1. Diskio_drvTypeDef  USER_Driver =
  2. {
  3.   USER_initialize,
  4.   USER_status,
  5.   USER_read,
  6. #if  _USE_WRITE
  7.   USER_write,
  8. #endif  /* _USE_WRITE == 1 */
  9. #if  _USE_IOCTL == 1
  10.   USER_ioctl,
  11. #endif /* _USE_IOCTL == 1 */
  12. };


1、先定义一个USER_CheckStatus,用来检查MMC设置状态,后面几个函数都要调用

  1. static DSTATUS USER_CheckStatus(BYTE lun)
  2. {
  3.   Stat = STA_NOINIT;
  4.   while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  5.   {
  6.   }

  7.   if((BSP_MMC_GetCardState(0) == BSP_ERROR_NONE))
  8.   {
  9.     Stat &= ~STA_NOINIT;
  10.   }

  11.         //printf("USER_CheckStatus\r\n");
  12.   HAL_HSEM_Release(EMMC_HSEM_ID, 0);

  13.   return Stat;
  14. }

2、USER_initialize
需要定义DISABLE_MMC_INIT,即不在这个初始化函数中初始化EMMC,EMMC在main函数中统一初始化。
  1. DSTATUS USER_initialize (
  2.         BYTE pdrv           /* Physical drive nmuber to identify the drive */
  3. )
  4. {
  5.   /* USER CODE BEGIN INIT */
  6.         printf("USER_initialize\r\n");
  7. #if !defined(DISABLE_MMC_INIT)

  8.   while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  9.   {
  10.   }

  11.   if(BSP_MMC_Init(0) == BSP_ERROR_NONE)
  12.   {
  13.     HAL_HSEM_Release(EMMC_HSEM_ID, 0);
  14.     Stat = USER_CheckStatus(pdrv);
  15.   }
  16. #else
  17.   Stat = USER_CheckStatus(pdrv);

  18. #endif
  19.   return Stat;
  20.   /* USER CODE END INIT */
  21. }


3、USER_status获得eMMC状态函数
  1. DSTATUS USER_status (
  2.         BYTE pdrv       /* Physical drive number to identify the drive */
  3. )
  4. {
  5.   /* USER CODE BEGIN STATUS */
  6.     return USER_CheckStatus(pdrv);
  7.   /* USER CODE END STATUS */
  8. }


4、USER_read读函数
  1. DRESULT USER_read (
  2.         BYTE pdrv,      /* Physical drive nmuber to identify the drive */
  3.         BYTE *buff,     /* Data buffer to store read data */
  4.         DWORD sector,   /* Sector address in LBA */
  5.         UINT count      /* Number of sectors to read */
  6. )
  7. {
  8.   /* USER CODE BEGIN READ */
  9. DRESULT res = RES_ERROR;
  10.   ReadStatus = 0;
  11. #if (ENABLE_MMC_DMA_CACHE_MAINTENANCE == 1)
  12.   uint32_t alignedAddr;
  13. #endif


  14.   while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  15.   {
  16.   }

  17.         //printf("USER_read\r\n");
  18.   if(BSP_MMC_ReadBlocks(0, (uint32_t*)buff,
  19.                         (uint32_t) (sector),
  20.                         count) == BSP_ERROR_NONE)
  21.   {
  22.     while(BSP_MMC_GetCardState(0) != BSP_ERROR_NONE)
  23.     {
  24.     }
  25.       res = RES_OK;
  26. #if (ENABLE_MMC_DMA_CACHE_MAINTENANCE == 1)
  27.       /*
  28.       the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address,
  29.       adjust the address and the D-Cache size to invalidate accordingly.
  30.       */
  31.       alignedAddr = (uint32_t)buff & ~0x1F;
  32.       SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
  33. #endif
  34.   }
  35.   else
  36.   {
  37.     res = RES_NOTRDY;
  38.   }

  39.   HAL_HSEM_Release(EMMC_HSEM_ID, 0);
  40.   return res;
  41.   /* USER CODE END READ */
  42. }
5、USER_write写函数
  1. #if _USE_WRITE == 1
  2. DRESULT USER_write (
  3.         BYTE pdrv,          /* Physical drive nmuber to identify the drive */
  4.         const BYTE *buff,   /* Data to be written */
  5.         DWORD sector,       /* Sector address in LBA */
  6.         UINT count          /* Number of sectors to write */
  7. )
  8. {
  9.   /* USER CODE BEGIN WRITE */
  10.   /* USER CODE HERE */
  11.   DRESULT res = RES_ERROR;
  12.   WriteStatus = 0;
  13. /*
  14.   * since the MPU is configured as write-through, see main.c file, there isn't any need
  15.   * to maintain the cache as its content is always coherent with the memory.
  16.   * If needed, check the file "Middlewares/Third_Party/FatFs/src/drivers/sd_diskio_dma_template.c"
  17.   * to see how the cache is maintained during the write operations.
  18.   */

  19.   while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  20.   {
  21.   
  22.                
  23.         //printf("USER_write\r\n");
  24.   if(BSP_MMC_WriteBlocks(0, (uint32_t*)buff,
  25.                             (uint32_t)(sector),
  26.                             count) == BSP_ERROR_NONE)
  27.   {
  28.     while(BSP_MMC_GetCardState(0) != BSP_ERROR_NONE)
  29.     {
  30.     }
  31.     res = RES_OK;
  32.   }
  33.         }
  34.   HAL_HSEM_Release(EMMC_HSEM_ID, 0);
  35.   return res;
  36.   /* USER CODE END WRITE */
  37. }
  38. #endif /* _USE_WRITE == 1 */
6、USER_ioctl发送IO控制函数
  1. #if _USE_IOCTL == 1
  2. DRESULT USER_ioctl (
  3.         BYTE pdrv,      /* Physical drive nmuber (0..) */
  4.         BYTE cmd,       /* Control code */
  5.         void *buff      /* Buffer to send/receive control data */
  6. )
  7. {
  8.   /* USER CODE BEGIN IOCTL */
  9.   DRESULT res = RES_ERROR;
  10.   BSP_MMC_CardInfo CardInfo;

  11.   if (Stat & STA_NOINIT) return RES_NOTRDY;


  12.   while ( HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  13.   {
  14.   }

  15.   switch (cmd)
  16.   {
  17.   /* Make sure that no pending write process */
  18.   case CTRL_SYNC :
  19.     res = RES_OK;
  20.     break;

  21.   /* Get number of sectors on the disk (DWORD) */
  22.   case GET_SECTOR_COUNT :
  23.     BSP_MMC_GetCardInfo(0, &CardInfo);
  24.     *(DWORD*)buff = CardInfo.LogBlockNbr;
  25.     res = RES_OK;
  26.     break;

  27.   /* Get R/W sector size (WORD) */
  28.   case GET_SECTOR_SIZE :
  29.     BSP_MMC_GetCardInfo(0, &CardInfo);
  30.     *(WORD*)buff = CardInfo.LogBlockSize;
  31.     res = RES_OK;
  32.     break;

  33.   /* Get erase block size in unit of sector (DWORD) */
  34.   case GET_BLOCK_SIZE :
  35.     BSP_MMC_GetCardInfo(0, &CardInfo);
  36.     *(DWORD*)buff = CardInfo.LogBlockSize / MMC_DEFAULT_BLOCK_SIZE;
  37.         res = RES_OK;
  38.     break;

  39.   default:
  40.     res = RES_PARERR;
  41.   }

  42.   while(BSP_MMC_GetCardState(0) != BSP_ERROR_NONE)
  43.   {
  44.   }

  45.   HAL_HSEM_Release(EMMC_HSEM_ID, 0);
  46.   return res;
  47.   /* USER CODE END IOCTL */
  48. }
  49. #endif /* _USE_IOCTL == 1 */
7、main函数
  1.   MX_SDMMC1_MMC_Init();        //初始化eMMC
  2.   MX_USB_DEVICE_Init();        //初始化USB Device
  3.   MX_USART3_UART_Init();        //初始化USART3
  4.   MX_FATFS_Init();                //初始化FATFS
8、测试函数
功能:列出文件名、打印STM32.TXT文件内容
  1. void printFileInfo(FILINFO *fileInfo) {
  2.     WORD fdate = fileInfo->fdate;
  3.     WORD ftime = fileInfo->ftime;
  4.     TCHAR *name = fileInfo->fname;
  5.                 FSIZE_t        fsize=fileInfo->fsize;

  6.     int year = ((fdate >> 9) & 0x7F) + 1980; // 年份从1980年开始计算
  7.     int month = (fdate >> 5) & 0x0F; // 月份
  8.     int day = fdate & 0x1F; // 日期
  9.     int hour = (ftime >> 11) & 0x1F; // 小时
  10.     int minute = (ftime >> 5) & 0x3F; // 分钟
  11.     int second = ftime & 0x1F; // 秒(注意:这里只使用了最低的5位,可能需要根据实际情况调整)
  12.                 if (fileInfo->fattrib & AM_DIR) {
  13.                         printf("%04d/%02d/%02d %02d:%02d    <DIR>          %s\r\n", year, month, day, hour, minute, name);
  14.                 }
  15.                 else
  16.                 {
  17.                         printf("%04d/%02d/%02d %02d:%02d         %9d %s\r\n", year, month, day, hour, minute,(int)fsize, name);
  18.                 }
  19. }

  20. static void FS_listDirectory(void)
  21. {
  22.         FRESULT res;
  23.         DIR dir;
  24.         FILINFO fno;
  25.         uint32_t bytesread;
  26.         uint16_t i;
  27.         
  28.         //printf("[Directory list]:\r\n");
  29.   if(f_mount(&MMCFatFs, (TCHAR const*)MMCPath, 0) == FR_OK)
  30.   {
  31.                         //printf("[mount MMC success]\r\n");
  32.                         res = f_opendir(&dir, "/");
  33.                         if (res == FR_OK)
  34.                         {
  35.                                 //printf("[open directory success]\r\n");
  36.                                 printf("\r\n[list directory / files ......]:\r\n");
  37.         for (;;) {
  38.                                                 i++;
  39.             res = f_readdir(&dir, &fno);
  40.             if (res != FR_OK || fno.fname[0] == 0) break;
  41.             if (fno.fname[0] == '.') continue;
  42.                                        
  43.                                         printFileInfo(&fno);
  44.                                        
  45.         }
  46.                         
  47.         f_closedir(&dir);
  48.                 }
  49.                
  50.                                 if(f_open(&MyFile, "STM32.TXT", FA_READ) == FR_OK)
  51.                                 {
  52.                                                                 /* Read data from the text file */
  53.                                                 memset(rtext,0,sizeof(rtext));
  54.                                                 res = f_read(&MyFile, ( void *)rtext, sizeof(rtext), (void *)&bytesread);

  55.                                                 if((bytesread > 0) && (res == FR_OK))
  56.                                                 {
  57.                                                         printf("\r\n[Display STM32.TXT file content]:\r\n%s\r\n",rtext);
  58.                                                                                 /* Close the open text file */
  59.                                                                                 f_close(&MyFile);
  60.                                                 }
  61.                                 }
  62.         }               
  63. }


三、运行效果

1、FATFS获得信息通过串口打印输出
41.png
2、命令行dir对比
42.png
3、资源管理器查看U盘内容对比
43.png

这样eMMC既是u盘,也是FATFS的设备,可以通过U盘功能copy文本,图片、音频、视频到eMMC,然后MCU这边编写相应程序访问eMMC进行查看,播放。

loutin 发表于 2025-4-19 10:32 | 显示全部楼层
在STM32H745I-DISCO开发板上,通过FATFS文件系统访问eMMC存储器,可以实现高效的嵌入式文件管理。
yorkbarney 发表于 2025-4-19 11:27 | 显示全部楼层
在所有FATFS函数调用后,检查返回值FRESULT,并根据错误码进行相应处理。
robincotton 发表于 2025-4-19 13:32 | 显示全部楼层
在user_diskio.c文件中实现几个接口函数,如USER_initialize()、USER_status()、USER_read()、USER_write()和USER_ioctl()等,以便FATFS能够通过这些函数与eMMC设备进行交互。
lzmm 发表于 2025-4-20 10:12 | 显示全部楼层
配置SDMMC接口、初始化FATFS文件系统,并实现磁盘I/O接口
hilahope 发表于 2025-4-20 10:41 | 显示全部楼层
通过STM32CubeMX配置eMMC相关引脚
yeates333 发表于 2025-4-20 11:03 | 显示全部楼层
可以参考STM32CubeH7的官方文档和示例代码。
benjaminka 发表于 2025-4-20 11:27 | 显示全部楼层
STM32H745I-DISCO开发板自带eMMC接口,无需额外硬件连接。
iyoum 发表于 2025-4-20 11:42 | 显示全部楼层
在STM32H745I-DISCO开发板上使用FATFS访问EMMC存储的一般步骤。
ulystronglll 发表于 2025-4-20 12:20 | 显示全部楼层
已经下载并安装了STM32CubeH7软件包。
mickit 发表于 2025-4-20 12:32 | 显示全部楼层
可以在STM32H745I-DISCO开发板上成功实现FATFS访问eMMC,为嵌入式应用提供高效的文件管理功能。
kkzz 发表于 2025-4-20 13:04 | 显示全部楼层
检查FATFS的配置是否正确,特别是卷数和路径。
claretttt 发表于 2025-4-20 13:39 | 显示全部楼层
使用FATFS提供的f_getfree()函数检查剩余空间
benjaminka 发表于 2025-4-20 14:16 | 显示全部楼层
通过FATFS文件系统,实现STM32H745I-DISCO开发板对eMMC设备的访问,使电脑和MCU都能读写eMMC中的文件。
mmbs 发表于 2025-4-20 15:57 | 显示全部楼层
使用STM32的HAL库或LL库中的函数来初始化eMMC
bestwell 发表于 2025-4-20 17:44 | 显示全部楼层
可以在STM32H745I-DISCO开发板上使用FATFS库访问EMMC,实现文件的存储和管理。
uiint 发表于 2025-4-20 18:05 | 显示全部楼层
eMMC初始化失败、FATFS挂载失败、读写文件错误
cashrwood 发表于 2025-4-20 18:37 | 显示全部楼层
在文件操作中,检查返回值并妥善处理错误
febgxu 发表于 2025-4-20 20:10 | 显示全部楼层
配置SDMMC              
primojones 发表于 2025-4-20 21:15 | 显示全部楼层
SDMMC时钟频率应根据EMMC规格进行配置,避免过高的时钟频率导致通信错误。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

84

主题

147

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部