本帖最后由 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进行查看,播放。
|