华大MCU之二:USB 驱动 + FatFs 的移植使用详解

cathy的头像

继上一篇华大MCU之一:HC32F460 替换 STM32F411 移植记录完成之后,领导要求启用 USB 相关功能(硬件在设计时已经设计了 USB 接口)。之前的 ST MCU 中 USB 用的还是挺多的,对于 USB 有一些了解!对于 ST 的 USB 驱动库也写过几篇博文!
  
我的项目中使用的是终端通过 USB 读取 U 盘,因此,本文重点关于 USB HOST MSC 部分 + FatFs!

USB 驱动

驱动文件架构
  
移植驱动之前,先要了解一下驱动库的代码文件,有哪些是必须的源代码;有哪些是需要处理的配置文件等等。不过经过我的几番对比发现,无论是驱动文件结构还是内部的函数名称,华大 的 USB 库与 ST 的 USB 库(标准外设版)高度一致!鉴于此,驱动库的文件架构及代码的说明就不用多说了,具体参见博文《 STM32 之三 标准外设版USB驱动库详解(架构+文件+函数+使用说明+示例程序)》 。
  
咱也不知道是 ST 抄袭华大的时候改了一些内容,还是华大抄袭 ST 的时候有些改动。在 USB 驱动源码上,这两者是有一些不同的地方,这也就导致了它俩在某些配置上有一些区别。下面我们会详细的介绍到!

文件移植
  
华大的 USB 驱动库的文件夹组织与 ST 的有些区别。移植的第一步还是根据我们的需要来选择使用的驱动库文件,不多说,直接参照下面的图:

“华大MCU之二:USB

更详细的移植,请参考博文《STM32 之四 标准外设版 USB 驱动 + MSC(Host) + Fatfs 移植

配置
  
USB 驱动库的使用需要我们自己来处理几个文件,主要是一些和配置相关的部分。配置文件严格来说,不属于驱动库源码文件,其属于用户文件。华大的 USB 库与 ST 的一样,配置文件均以模块的形式给出(例如,usb_conf_template.h),我们需要复制模板文件,放到自己的目录中,并更改实现文件中的内容。

“华大MCU之二:USB

usb_bsp

usb_bsp.h 我们不需要做任何修改。在 ST 的驱动库中,这个文件是位于驱动源码目录中的。华大将它拿到了用户目录。我们主要是在 usb_bsp.c中实现一些 USB 需要使用的函数接口。对于这个文件,华大 和 ST 基本可以说没有差别。主要就是以下部分:

1、USB 使用的 MCU 引脚的初始化。这里需要注意,在硬件设计上,某些 USB 引脚我们是不使用的,例如,我的板子上只使用了 DM 和 DP (最简单的情况下只用这两个)

void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE *pdev)
{
    stc_port_init_t stcPortInit;

    /* port config */
    /* Disable digital function for DM DP */
    MEM_ZERO_STRUCT(stcPortInit);
    stcPortInit.enPinMode = Pin_Mode_Ana;
    PORT_Init(USB_DM_PORT, USB_DM_PIN, &stcPortInit);
    PORT_Init(USB_DP_PORT, USB_DP_PIN, &stcPortInit);
    //PORT_SetFunc(PortA, Pin08, Func_UsbF, Disable); //SOF
    //PORT_SetFunc(PortA, Pin10, Func_UsbF, Disable); //ID
    PORT_SetFunc(USB_DM_PORT, USB_DM_PIN, USB_DM_PIN_SOURCE, Disable); //DM
    PORT_SetFunc(USB_DP_PORT, USB_DP_PIN, USB_DP_PIN_SOURCE, Disable); //DP
    // PORT_SetFunc(PortB, Pin08, Func_UsbF, Disable); //DRVVBUS

    USB_RCC_ENABLE();
}

2、USB 中断的配置。注意华大的中断使用时的要求与 ST 的差别还是挺大的。不懂的可以参考博文《华大MCU之一:HC32F460 替换 STM32F411 移植记录》中的中断部分

void USB_OTG_BSP_EnableInterrupt(void)
{
    stc_irq_regi_conf_t stcIrqRegiConf;
    /* Register INT_USBFS_GLB Int to Vect.No.030 */
    stcIrqRegiConf.enIRQn = Int030_IRQn;
    /* Select INT_USBFS_GLB interrupt function */
    stcIrqRegiConf.enIntSrc = INT_USBFS_GLB;
    /* Callback function */
    stcIrqRegiConf.pfnCallback = &USB_IRQ_Handler;
    /* Registration IRQ */
    enIrqRegistration(&stcIrqRegiConf);
    /* Clear Pending */
    NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
    /* Set priority */
    NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);
    /* Enable NVIC */
    NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}
/* ST 的中断处理函数默认不放在此处。*/
void USB_IRQ_Handler(void)
{
	USBH_OTG_ISR_Handler(&USB_OTG_Core);
}

3、USB 使用的一些延时函数:void USB_OTG_BSP_uDelay(const uint32_t t) 和 void USB_OTG_BSP_mDelay(const uint32_t msec)。分别为 微妙延时 和 毫秒延时。具体怎么实现就需要根据自己的程序了。在默认的 ST 驱动库示例中,可选择使用一个定时或者软件延时(死循环)。

4、根据需要配置 VBUS。

usb_conf.h
  
这个文件是 USB 驱动库的顶级配置文件。定义了我们使用的 USB CORE 是 HS 还是 FS,PHY 的类型等等。这个文件和 ST 的基本也是一模一样!但是,华大中的配置存在一些问题,下面我们具体来说一说。

1、根据自己的 MCU 选择使用的 USB 核。这里我的 MCU 只有个 全速 核,因此选择了 USE_USB_OTG_FS。HS 部分直接注释!

“华大MCU之二:USB

上面这些与 ST 一模一样。

2、根据选择的 MCU 修改一些缓冲区的大小。同样针对不同的核,配置是分开的。这里我们只使用了 USE_USB_OTG_FS。

“华大MCU之二:USB

至于这些宏对应的值,就需要去MCU手册中查找了!
  
这部分里面有个 USB_OTG_HS_INTERNAL_DMA_ENABLED 明显是搞错了,我的 全速 USB 核的配置 USB_OTG_HS_INTERNAL_DMA_ENABLED干啥玩意。直接注释掉!

“华大MCU之二:USB

3、由于我们板子上并没有使用 USB 的 VBUS 引脚,因此将 驱动库中关于 VBUS 相关的操作关闭

“华大MCU之二:USB

4、选择 USB 的工作模式。由于我的项目中使用的是终端通过 USB 读取 U 盘,也就是我的 USB 是个 HOST。但是,在配置只启用 USB HOST 时,缺出现了编译报错!

“华大MCU之二:USB

很明显,USB 驱动库中并没有处理好 USE_DEVICE_MODE 这个宏!

5、华大配置文件多了一块内容,如下:

“华大MCU之二:USB

在 ST 的 配置文件中,这部分内容全部在 本节所述的第二部分,不知道为啥华大非得自己拿出来单独写一边。而且RX_FIFO_FS_SIZE 和 RX_FIFO_HS_SIZE 这两个宏与上面的是有重复的。

“华大MCU之二:USB

我把 RX_FIFO_FS_SIZE 和 RX_FIFO_HS_SIZE 注释掉了!

6、最后一部分则是用于高速 USB 核的内存对齐在不同编译器下的处理,我们不需要关心。但是需要注意的是,华大和ST的这部分的名字不同,华大在 ST 的名称的基础上,增加了 _USB 开头。

“华大MCU之二:USB

由于名称不同,我们在定义变量的时候需要注意,直接用 ST 的是报错的!

usblib_config.h

该文件没有任何用处!

usbh_conf.h
  
这个文件在华大的 USB 库中,将其放到了 USB 驱动源码中。在 ST 中,该文件也是用户的配置文件。可能是由于华大 MCU 中的 USB 核心都是一样的,不需要再配置吧!但是根据我的理解,最好还是需要根据需要来配置一下该文件的!
  
这个文件是 USB Host 的顶级配置文件。当我们的 USB 工作在 Host 模式时,该文件定义了一些 Host 模式下使用的参数。参数很少,就 端点数量 和 接口数量这两个东西,还有个就是 MSC 的缓冲区大小(不用 MSC 的话这个宏可以忽略)。华大 和 ST 的该文件一致。

“华大MCU之二:USB

ST 的 USB 驱动中的宏 LCD_ErrLog(str) 应该是 ST 驱动的遗留问题。 USB 驱动和 LCD 有半毛钱关系啊,ST 驱动库可能在测试时使用了 LCD ,后来没删除!
  
_USE_IOCTL 这个宏则是 USB 驱动使用 FatFs 时的一个配置选项。使用 FatFs 时是必须的!华大的为啥没有?搜索一番发现,华大将它放到了其他文件中,后面我们会有说明!

hc32f46x_usbfs.h 和 ddl_config.h
  
ddl_config.h 文件是华大的外设驱动库的配置文件,里面定义了驱动库启用那些外设。对于 USB ,则有 #define DDL_USBFS_ENABLE (DDL_OFF) 。对应的 USB 头文件 hc32f46x_usbfs.h(从华大的 USB 示例中可以获取该文件)。我们可以选择直接使用 USB 驱动中的文件,而无需关心这些配置!

usbh_usr
  
usbh_usr.h/c 这两个文件就是 USB 驱动库放出的供用户实现自己实际功能的接口文件。对于不同的 USB CLASS ,该文件是有区别的,具体参见 STM32 之三 标准外设版USB驱动库详解(架构+文件+函数+使用说明+示例程序)

至此,USB 驱动部分就移植完了!

FatFs
  
华大 USB 示例中的 FatFs 版本非常旧(0.07e),而我之前的 ST 项目中使用的都比较新(0.13c),且经过了现场的大量验证。因此,最终绝对使用相对较新的版本。在之前的某个版本中, FatFs 改动相对较大,因此在更换新版后,直接编译会报一些错误,下面我们挨个解决!

关于 FatFs 的介绍及移植,详见博文《FatFs 之一 R0.13c版源码目录文件、函数、全配置项详解及移植说明

error: #20: identifier “BYTE” is undefined
  
这个问题应该是是由于没有正确包含头文件导致的。在 R0.13c 的时候,作者更新: Supported stdint.h for C99 and later. (integer.h was included in ff.h),然后删除了原来的 integer.h。这样,diskio.h 中也删除了对于该文件的包含。这样就导致了 diskio.h 中某些数据类型未定义!如下图:

“华大MCU之二:USB

解决方法就是在 diskio.h 中包含数据类型的定义文件。根据 00history.txt 中的更新说明,现在的数据类型定义就在 ff.h 中。

error: #147-D: declaration is incompatible
  
这个问题是由于旧版的接口与新版的不兼容导致的!在博文《FatFs 之一 R0.13c版源码目录文件、函数、全配置项详解及移植说明》中我们介绍过,USB 驱动中默认实现了 diskio.c 中的内容,实现文件名为 usbh_msc_fatfs.c。这也是为什么我使用了 USB + FatFs 缺不需要 diskio.c 的原因。

“华大MCU之二:USB

解决方法也很简单,我们不得不更改 usbh_msc_fatfs.c 中不兼容的部分,如上图!

Error: L6218E: Undefined symbol disk_ioctl (referred from ff.o).
  
这个问题是由于我们没有正确配置宏值导致的!在 ST 的 USB 驱动中的 usbh_conf.h 中,定义了宏 _USE_IOCTL。但是,在 华大的 USB 库中,并没有该宏的配置。这是因为,华大 USB 示例中更改了 diskio.h,将宏 _USE_IOCTL 放到了 diskio.h 中。如下图

“华大MCU之二:USB

_USE_IOCTL 这个宏需要特殊说明一下。这个宏值是 ST 的驱动添加的。其是与 FatFs 相关的。在 FatFs 中有个配置项 FF_USE_TRIM,这个宏如果被定义为 1,则 FatFs 要求必须在 diskio.c 中定义函数 disk_ioctl()。但是可能由于文件包含关系,导致 ST 在 USB 库中不能直接使用 FF_USE_TRIM。所以才添加了 _USE_IOCTL 。
  
更进一步,这个宏必须被定义为 1,因为在 FatFs 中存在一个 BUG:FatFs 并没有处理好对函数 disk_ioctl() 的使用,如下:

#if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Synchronize filesystem and data on the storage                        */
/*-----------------------------------------------------------------------*/

static FRESULT sync_fs (	/* Returns FR_OK or FR_DISK_ERR */
	FATFS* fs		/* Filesystem object */
)
{
	FRESULT res;


	res = sync_window(fs);
	if (res == FR_OK) {
		if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {	/* FAT32: Update FSInfo sector if needed */
			/* Create FSInfo structure */
			mem_set(fs->win, 0, sizeof fs->win);
			st_word(fs->win + BS_55AA, 0xAA55);
			st_dword(fs->win + FSI_LeadSig, 0x41615252);
			st_dword(fs->win + FSI_StrucSig, 0x61417272);
			st_dword(fs->win + FSI_Free_Count, fs->free_clst);
			st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);
			/* Write it into the FSInfo sector */
			fs->winsect = fs->volbase + 1;
			disk_write(fs->pdrv, fs->win, fs->winsect, 1);
			fs->fsi_flag = 0;
		}
		/* Make sure that no pending write process in the lower layer */
		if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;
	}

	return res;
}

#endif

上面总是会调用函数 disk_ioctl(),如果不定义 _USE_IOCTL为 1 则将导致函数未定义 。处理上面这个调用,其他地方都是有相应的宏来控制对于该函数的读取的。如下:

“华大MCU之二:USB

diskio.h 中除了 _USE_IOCTL 还有个 _READONLY,这个宏我搜了一下,这应该是华大自己添加的。其主要就是移除写相关的接口的。这个宏容易引起歧义。如果这里定义为 1 ,将导致错误。因为这个宏只是在 下面用了,FatFs 中并没有添加该宏。导致 FatFs 与这里配置不一致。

“华大MCU之二:USB

至于解决方法,我们选择不修改 FatFs 的源码,只能选择在合适的位置定义宏 _USE_IOCTL 即可!至于 _READONLY 这个玩意,删除或者不用管就行了。最终,我将 diskio.h 改为了如下所示:

“华大MCU之二:USB

参考

ST 的 USB 库手册

相关阅读:
华大MCU之一:HC32F460 替换 STM32F411 移植记录

版权声明:本文为CSDN博主「ZC·Shou」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ZCShouCSDN/article/details/109982952
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。