前面两篇文章分别介绍了 STM32H743 对 eMMC 的读写和 eMMC 虚拟 U 盘的操作,接下来就介绍一下 STM32H743 驱动 eMMC 挂载 FatFs 系统读写文件。FatFs 是一个用于小型嵌入式系统的通用 FAT/exFAT 文件系统模块。FatFs 模块是按照 ANSI C(C89)编写的,与磁盘 I/O 层完全分离。因此,它独立于平台。它可以集成到资源有限的小型微控制器中,如 8051、PIC、AVR、ARM、Z80、RX 等。此外,还提供了用于微型微控制器的 Petit Fatfs 模块。
参考资料:
- FAT 文件系统基础:FatFs – Generic FAT Filesystem Module。
- FatFS 应用笔记(含移植说明):http://elm-chan.org/fsw/ff/doc/appnote.html
- FatFS 中 ffconf.h 文件中各种配置说明:http://elm-chan.org/fsw/ff/doc/config.html
一、开发平台
- 开发环境:MDK5.30
- 移植驱动:STM32Cube_FW_H7_V1.9.0
- 硬件平台:STM32H743VITX + eMMC(型号:MTFC4GACAJCN-1M WT)
二、STM32CubeMX 配置
这部分可以参考下面的文章:
三、FatFs 项目框架解读及操作流程分析
生成工程文件后,可以看出来如图 1 所示的项目文件框架。
- main.c 文件中包含了 FatFs 的应用程序;
- user_diskio.c 文件中是磁盘 I/O 驱动文件;
- sdmmc.c 文件是对 eMMC HAL 文件中对 eMMC 读写等功能的封装,供 user_diskio.c 调用;
- eMMC HAL 文件是 STM32H743 的 HAL 库文件;
- FatFs 文件夹是 FatFs 源码相关文件,这里的文件不需要改动。
以上即是实现 eMMC 挂载 FatFs 文件系统所需要的主要文件,这么多文件具体的调用关系如何呢?可以先来看一下 FatFs 的介绍。
FatFs 各个文件的依赖关系:
驱动一个磁盘或者多个磁盘的框图:
上面两张图清晰的描绘了 FatFs 文件系统各个文件的依赖关系和调用关系,结合工程文件用一张图来说明 STM32H743 驱动 eMMC 挂载 FatFs 系统读写文件的工程文件调用关系:
具体就是:
(1)main.c 文件是 FatFs 文件系统的 Application,也就是应用程序;通过 f_open()、f_read()等函数操作 FatFs 文件系统。
(2)FatFs 文件系统为应用程序提供函数接口,也就是 f_open()、f_read()等函数;对底层驱动使用 disk_read()、disk_write()、get_fattime()等函数来操作,这些函数与磁盘 I/O 完全分离。
(3)user_diskio.c 文件是承接 FatFs 系统和底层驱动的文件,或者说其也是底层驱动的一部分,该文件定义了操作磁盘 I/O 的数据结构,并结合 sdmmc.c 和 emmc HAL 库共同完成底层驱动。
以一个初始化的例子来说明操作流程,例如在 main 函数中创建文件系统:
res = f_mkfs(MMCPath, FM_FAT32, 0, workBuffer, sizeof(workBuffer));
f_mkfs 函数的定义是在 FatFs 文件系统的 ff.c 文件中,在函数 f_mkfs 中会初始化并检查磁盘:
/* Check physical drive status */ stat = disk_initialize(pdrv);
这里又调用了 disk_initialize()函数,该函数的定义则是 FatFs 文件系统中的 diskio.c 中:
/** * @brief Initializes a Drive * @param pdrv: Physical drive number (0..) * @retval DSTATUS: Operation status */ DSTATUS disk_initialize ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { DSTATUS stat = RES_OK; if(disk.is_initialized[pdrv] == 0) { disk.is_initialized[pdrv] = 1; stat = disk.drv[pdrv]->disk_initialize(disk.lun[pdrv]); } return stat; }
其中,
stat = disk.drv[pdrv]->disk_initialize(disk.lun[pdrv]);
会实例化到底层驱动,具体就是 user_diskio.c 中的 DSTATUS USER_initialize();
该函数里面则是具体操作磁盘 I/O 的工作(由于在 STM32CubeMX 配置 FatFs 的时候,这里选择了 User-defined,所以默认这里是空函数),会涉及到 sdmmc.c 文件和 emmc HAL 库文件。至此,完成创建文件系统的工作。
同样,f_open()、f_read()、f_write()、f_close()等函数的操作流程也是这么个过程。
根据上面的分析,弄懂了 FatFs 的工作流程,就可以修改 user_diskio.c 文件,实现相应的磁盘 I/O 操作功能。再在 main 函数中增加应用程序,就可以实现 STM32H743 驱动 eMMC 挂载 FatFs 系统读写文件。main 函数中的应用程序如下:
/*-1- Link the micro SD disk I/O driver*/ if(FATFS_LinkDriver(&USER_Driver, MMCPath) == 0) { /* Create FAT volume */ usb_printf("[CM7]:\tCreating FileSystem...\n"); res = f_mkfs(MMCPath, FM_FAT32, 0, workBuffer, sizeof(workBuffer)); if (res != FR_OK) { usb_printf("[CM7]:\tCreating FileSystem Failed!...\n"); Error_Handler(); } usb_printf("[CM7]:\tFileSystem Created successfully!\n"); /* start the FatFs operations simulaneously with the Core CM4 */ FS_FileOperations(); }
注意:f_mkfs()函数每次程序运行时都会重新创建一次系统,相当于格式化。所以,如果 eMMC 已经创建好 FatFs 文件系统,此语句可屏蔽掉。
FS_FileOperations()函数代码如下:
uint32_t file_size; static void FS_FileOperations(void) { FRESULT res; uint32_t byteswritten, bytesread; /* Register the file system object to the FatFs module */ if(f_mount(&MMCFatFs, (TCHAR const*)MMCPath, 0) != FR_OK) { goto error; } /* open the file "CM7.TXT" in write mode */ res = f_open(&MyFile, CM7_FILE, FA_CREATE_ALWAYS | FA_WRITE); if(res != FR_OK) { goto error; } // LOCK_HSEM(UART_HSEM_ID); usb_printf("[CM7]:\topening '%s' for writting...\r\n", CM7_FILE); // UNLOCK_HSEM(UART_HSEM_ID); /* Write data to the text file */ res = f_write(&MyFile, wtext, sizeof(wtext), (void *)&byteswritten); if((byteswritten == 0) || (res != FR_OK)) { f_close(&MyFile); goto error; } // LOCK_HSEM(UART_HSEM_ID); usb_printf("[CM7]:\t'%s' '%lu' bytes written...\r\n", CM7_FILE, byteswritten); // UNLOCK_HSEM(UART_HSEM_ID); /* Close the open text file */ f_close(&MyFile); /* Open the text file object with read access */ if(f_open(&MyFile, CM7_FILE, FA_READ) != FR_OK) { goto error; } // LOCK_HSEM(UART_HSEM_ID); usb_printf("[CM7]:\treading '%s' content...\r\n", CM7_FILE); // UNLOCK_HSEM(UART_HSEM_ID); /* Read data from the text file */ res = f_read(&MyFile, rtext, sizeof(rtext), (void *)&bytesread); if((bytesread == 0) || (res != FR_OK)) { f_close(&MyFile); goto error; } /* Close the open text file */ f_close(&MyFile); /* Compare read data with the expected data */ if(FatfsBuffercmp(rtext, (uint8_t *)wtext, byteswritten) != 0) { goto error; } else { // LOCK_HSEM(UART_HSEM_ID); usb_printf("[CM7]:\t%s ==> '> %s <'\r\n", CM7_FILE, rtext); // UNLOCK_HSEM(UART_HSEM_ID); while(1) { // BSP_LED_Toggle(LED_RED); HAL_Delay(250); } } error: Error_Handler(); }
代码执行流程为:
在主函数中为 eMMC 创建 FAT32 文件系统,挂载 eMMC,创建 CM7.LOG 文件,往里写数据,写完成之后关闭文件,读文件,关闭文件,比对读写数据是否正确。
编译下载,通过串口查看程序执行过程中打印的信息,如图 5 所示。
另外,也可以运行“STM32H743 实现 eMMC 虚拟 U 盘功能”程序,更直观的查看 STM32H743 在 eMMC 虚拟 U 盘里创建的 log 文件以及内容,如图 6 所示。
扫码关注尚为网微信公众号
原创文章,作者:sunev,如若转载,请注明出处:https://www.sunev.cn/embedded/939.html