打印
[STM32H7]

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

[复制链接]
134|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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

为了简化只在CM7核心使用

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


FS_EXFAT:支持exFAT文件系统

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



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



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


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

static DSTATUS USER_CheckStatus(BYTE lun)
{
  Stat = STA_NOINIT;
  while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  {
  }

  if((BSP_MMC_GetCardState(0) == BSP_ERROR_NONE))
  {
    Stat &= ~STA_NOINIT;
  }

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

  return Stat;
}

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

  while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  {
  }

  if(BSP_MMC_Init(0) == BSP_ERROR_NONE)
  {
    HAL_HSEM_Release(EMMC_HSEM_ID, 0);
    Stat = USER_CheckStatus(pdrv);
  }
#else
  Stat = USER_CheckStatus(pdrv);

#endif
  return Stat;
  /* USER CODE END INIT */
}


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


4、USER_read读函数
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 = RES_ERROR;
  ReadStatus = 0;
#if (ENABLE_MMC_DMA_CACHE_MAINTENANCE == 1)
  uint32_t alignedAddr;
#endif


  while (HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  {
  }

        //printf("USER_read\r\n");
  if(BSP_MMC_ReadBlocks(0, (uint32_t*)buff,
                        (uint32_t) (sector),
                        count) == BSP_ERROR_NONE)
  {
    while(BSP_MMC_GetCardState(0) != BSP_ERROR_NONE)
    {
    }
      res = RES_OK;
#if (ENABLE_MMC_DMA_CACHE_MAINTENANCE == 1)
      /*
      the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address,
      adjust the address and the D-Cache size to invalidate accordingly.
      */
      alignedAddr = (uint32_t)buff & ~0x1F;
      SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
#endif
  }
  else
  {
    res = RES_NOTRDY;
  }

  HAL_HSEM_Release(EMMC_HSEM_ID, 0);
  return res;
  /* USER CODE END READ */
}
5、USER_write写函数
#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 */
  /* USER CODE HERE */
  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)
  {
  
               
        //printf("USER_write\r\n");
  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);
  return res;
  /* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
6、USER_ioctl发送IO控制函数
#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 = RES_ERROR;
  BSP_MMC_CardInfo CardInfo;

  if (Stat & STA_NOINIT) return RES_NOTRDY;


  while ( HAL_HSEM_FastTake(EMMC_HSEM_ID) != HAL_OK)
  {
  }

  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 :
    BSP_MMC_GetCardInfo(0, &CardInfo);
    *(DWORD*)buff = CardInfo.LogBlockNbr;
    res = RES_OK;
    break;

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

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

  default:
    res = RES_PARERR;
  }

  while(BSP_MMC_GetCardState(0) != BSP_ERROR_NONE)
  {
  }

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

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

static void FS_listDirectory(void)
{
        FRESULT res;
        DIR dir;
        FILINFO fno;
        uint32_t bytesread;
        uint16_t i;
        
        //printf("[Directory list]:\r\n");
  if(f_mount(&MMCFatFs, (TCHAR const*)MMCPath, 0) == FR_OK)
  {
                        //printf("[mount MMC success]\r\n");
                        res = f_opendir(&dir, "/");
                        if (res == FR_OK)
                        {
                                //printf("[open directory success]\r\n");
                                printf("\r\n[list directory / files ......]:\r\n");
        for (;;) {
                                                i++;
            res = f_readdir(&dir, &fno);
            if (res != FR_OK || fno.fname[0] == 0) break;
            if (fno.fname[0] == '.') continue;
                                       
                                        printFileInfo(&fno);
                                       
        }
                        
        f_closedir(&dir);
                }
               
                                if(f_open(&MyFile, "STM32.TXT", FA_READ) == FR_OK)
                                {
                                                                /* Read data from the text file */
                                                memset(rtext,0,sizeof(rtext));
                                                res = f_read(&MyFile, ( void *)rtext, sizeof(rtext), (void *)&bytesread);

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


三、运行效果

1、FATFS获得信息通过串口打印输出

2、命令行dir对比

3、资源管理器查看U盘内容对比


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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

46

主题

94

帖子

0

粉丝