USB高速(USBHS)支持主机模式、设备模式和OTG模式,并且包含了一个内部的全速USB PHY。对于全速和低速操作,不需要外部的USB PHY。本文为学习记录,介绍了在主机模式下,利用内部PHY实现U盘的通信。
在主机或设备模式下,利用内部 PHY 的连接示意图如下所示。
上图信号线的作用如下表所示。
I/O端口 | 类型 | 描述 |
VBUS | 输入 | 总线电源端口 |
DM | 输入/输出 | 差分信号线 - 端口 |
DP | 输入/输出 | 差分信号线 + 端口 |
在主机模式下,由于USBHS并不检测VBUS引脚的电平状态, VBUS引脚可以忽略。我们只需要配置DP、DM两个信号引脚,查询《GD32F450xx_Datasheet》,两引脚的配置如下:
接口 | CPU引脚 | 复用 |
DM | PB14 | USBHS_DM AF12 |
DP | PB15 | USBHS_DP AF12 |
1、STM32官方程序:GD32F4xx_usb_library (用 USB host MSC模式)
2、文件系统:FatFs为官方R0.13C版。
GD32F4xx 系列 MCU 的 USBFS/USBHS 接口模块固件库架构如下图所示,固件库分为三层,顶层为用户可修改的应用接口层,中间层为USB_Hos,底层为USB_Drivers,中间层和底层统称为 USB 固件库驱动,该驱动层用户不可修改。
使用主机模式,所需要的文件及其说明如下表所示:
固件库 | 文件名称 | 说明 |
底层 | usb_core.h/.c |
USB 内核驱动 |
usb_reg.h |
USB 寄存器操作 |
|
中间层 | usbh_core.h/.c |
USB 主机状态机处理函数 |
usbh_ctrl.h/.c |
USB 主机控制传输处理函数 |
|
usbh_hcs.h/.c |
USB 主机通道处理函数 |
|
usbh_int.h/.c |
USB 主机模式中断处理函数 |
|
usbh_std.h/.c |
USB 主机枚举标准处理函数 | |
顶层 | main.c |
主应用程序接口 |
usbh_usr.c |
用户应用程序接口 | |
usb_delay.c |
延迟函数实现接口 |
|
application class |
设备类应用程序接口 |
FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统。这里我们使用R0.13c版本,一般我们只用到f_mount()、 f_open()、 f_write()、 f_read()就可以实现文件的读写操作。
本文移植过程中,涉及的文件如下:
void Usb_PeriphInit(void)
{
/*----------GPIO_config---------*/
rcu_periph_clock_enable(RCU_SYSCFG);
rcu_periph_clock_enable(RCU_GPIOB);
gpio_af_set(GPIOB, GPIO_AF_12, GPIO_PIN_14 | GPIO_PIN_15);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14 | GPIO_PIN_15);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14 | GPIO_PIN_15);
/*----------48M时钟设置---------*/
rcu_pll48m_clock_config(RCU_PLL48MSRC_PLLQ);
rcu_ck48m_clock_config(RCU_CK48MSRC_PLL48M);
rcu_periph_clock_enable(RCU_USBHS);
/*--------USB延迟函数设置--------*/
nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x00U);
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable((uint8_t)TIMER2_IRQn, 1U, 0U);
rcu_periph_clock_enable(RCU_TIMER2);
usbh_class_register(&usb_host_msc, &usbh_msc);
usbh_init(&usb_host_msc,&usbh_core, USB_CORE_ENUM_HS,&usr_cb); //register device class
/*-----------中断设置----------*/
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable((uint8_t)USBHS_IRQn, 2U, 0U); //开启USBHS_IRQn
}
开启TIMER2和USB_HS中断:
void TIMER2_IRQHandler(void)
{
usb_timer_irq();
}
void USBHS_IRQHandler(void)
{
usbh_isr(&usbh_core);
}
在usbh_usr.c编写我们需要执行的程序。这里我们参考GD32官方demo案例库,插入u盘后将会枚举信息,之后显示U盘根目录文件,最后向程序中写入测试文件。
#include
#include "usbh_usr.h"
#include "drv_usb_hw.h"
#include "usbh_msc_core.h"
#include "usbh_msc_scsi.h"
#include "usbh_msc_bbb.h"
#include "ff.h"
extern usb_core_driver usbh_core;
extern usbh_host usb_host_msc;
FATFS fatfs;
FIL file;
uint8_t line_idx = 0;
uint8_t usbh_usr_application_state = USBH_USR_FS_INIT;
/* points to the DEVICE_PROP structure of current device */
usbh_user_cb usr_cb =
{
usbh_user_init,
usbh_user_deinit,
usbh_user_device_connected,
usbh_user_device_reset,
usbh_user_device_disconnected,
usbh_user_over_current_detected,
usbh_user_device_speed_detected,
usbh_user_device_desc_available,
usbh_user_device_address_assigned,
usbh_user_configuration_descavailable,
usbh_user_manufacturer_string,
usbh_user_product_string,
usbh_user_serialnum_string,
usbh_user_enumeration_finish,
usbh_user_userinput,
usbh_usr_msc_application,
usbh_user_device_not_supported,
usbh_user_unrecovered_error
};
static uint8_t explore_disk (char* path, uint8_t recu_level);
static void toggle_leds (void);
/*!
\brief user operation for host-mode initialization
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_init(void)
{
static uint8_t startup = 0U;
if (0U == startup) {
startup = 1U;
printf("> USB host library started\n");
}
}
/*!
\brief user operation for device attached
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_device_connected(void)
{
printf("> Device Attached.\n");
}
/*!
\brief user operation when unrecoveredError happens
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_unrecovered_error (void)
{
printf("> UNRECOVERED ERROR STATE.\n");
}
/*!
\brief user operation for device disconnect event
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_device_disconnected (void)
{
printf("> Device Disconnected.\n");
}
/*!
\brief user operation for reset USB Device
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_device_reset(void)
{
/* users can do their application actions here for the USB-Reset */
printf("> Reset the USB device.\n");
}
/*!
\brief user operation for detectting device speed
\param[in] device_speed: device speed
\param[out] none
\retval none
*/
void usbh_user_device_speed_detected(uint32_t device_speed)
{
if (PORT_SPEED_HIGH == device_speed) {
printf("> High speed device detected.\r\n");
} else if(PORT_SPEED_FULL == device_speed) {
printf("> Full speed device detected.\r\n");
} else if(PORT_SPEED_LOW == device_speed) {
printf("> Low speed device detected.\r\n");
} else {
printf("> Device Fault.\r\n");
}
}
/*!
\brief user operation when device descriptor is available
\param[in] device_desc: device descriptor
\param[out] none
\retval none
*/
void usbh_user_device_desc_available(void *device_desc)
{
usb_desc_dev *pDevStr = device_desc;
printf("\r\n > VID: %04Xh ",(uint32_t)pDevStr->idVendor);
printf("\r\n > PID: %04Xh ",(uint32_t)pDevStr->idProduct);
}
/*!
\brief usb device is successfully assigned the Address
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_device_address_assigned(void)
{
}
/*!
\brief user operation when configuration descriptor is available
\param[in] cfg_desc: pointer to configuration descriptor
\param[in] itf_desc: pointer to interface descriptor
\param[in] ep_desc: pointer to endpoint descriptor
\param[out] none
\retval none
*/
void usbh_user_configuration_descavailable(usb_desc_config *cfg_desc,
usb_desc_itf *itf_desc,
usb_desc_ep *ep_desc)
{
usb_desc_itf *id = itf_desc;
if (0x08U == (*id).bInterfaceClass) {
printf("\r\n > Mass storage device connected.\n");
} else if (0x03U == (*id).bInterfaceClass) {
printf("\r\n > HID device connected.\n");
} else {
}
}
/*!
\brief user operation when manufacturer string exists
\param[in] manufacturer_string: manufacturer string of usb device
\param[out] none
\retval none
*/
void usbh_user_manufacturer_string(void *manufacturer_string)
{
printf("\r\n > manufacture string is : %s ",(uint8_t *)manufacturer_string);
}
/*!
\brief user operation when product string exists
\param[in] product_string: product string of usb device
\param[out] none
\retval none
*/
void usbh_user_product_string(void *product_string)
{
printf("\r\n > product string is : %s ",(uint8_t *)product_string);
}
/*!
\brief user operatin when serialNum string exists
\param[in] serial_num_string: serialNum string of usb device
\param[out] none
\retval none
*/
void usbh_user_serialnum_string(void *serial_num_string)
{
printf("\r\n > Serial Number string is : %s ",(uint8_t *)serial_num_string);
}
/*!
\brief user response request is displayed to ask for application jump to class
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_enumeration_finish(void)
{
printf("\r\n > Enumeration completed.");
printf("\r\n > ------------------------------------");
printf("\r\n > To start the MSC class operations: ");
}
/*!
\brief user operation when device is not supported
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_device_not_supported(void)
{
printf("\r\n > Device not supported.");
}
/*!
\brief user action for application state entry
\param[in] none
\param[out] none
\retval user response for user key
*/
usbh_user_status usbh_user_userinput(void)
{
usbh_user_status usbh_usr_status = USBH_USER_NO_RESP;
/*Key USER is in polling mode to detect user action */
usbh_usr_status = USBH_USER_RESP_OK;
return usbh_usr_status;
}
/*!
\brief user operation for device overcurrent detection event
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_over_current_detected (void)
{
printf("\r\n > Overcurrent detected.");
}
/*!
\brief demo application for mass storage
\param[in] pudev: pointer to device
\param[in] id: no use here
\param[out] none
\retval status
*/
int usbh_usr_msc_application(void)
{
FRESULT res;
msc_lun info;
uint8_t WriteTextBuff[] = "GD32 Connectivity line Host Demo application using FAT_FS ";
uint16_t bytesWritten, bytesToWrite;
switch(usbh_usr_application_state)
{
case USBH_USR_FS_INIT:
/* initializes the file system*/
if (FR_OK != f_mount(&fatfs, "0:/", 0)) {
printf("\r\n > Cannot initialize File System.");
return(-1);
}
printf("\r\n > File System initialized.");
if (USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, 0, &info)){
printf("\r\n > Disk capacity: %ud Bytes.", info.capacity.block_nbr * info.capacity.block_size);
}
usbh_usr_application_state = USBH_USR_FS_READLIST;
break;
case USBH_USR_FS_READLIST:
printf("\r\n > Exploring disk flash ...");
printf("\r\n > ------To see the root content of disk-----");
/*Key TAMPER in polling*/
while (usbh_core.host.connect_status == 0) { }
explore_disk("0:/", 1);
line_idx = 0;
usbh_usr_application_state = USBH_USR_FS_WRITEFILE;
break;
case USBH_USR_FS_WRITEFILE:
usb_mdelay(100);
printf("\r\n > ------------- write file test -----------");
/*key WAKEUP in polling*/
while (usbh_core.host.connect_status==0) { }
printf("\r\n > Writing File to disk flash ...\r\n");
/* register work area for logical drives */
f_mount(&fatfs, "0:/", 1);
if (FR_OK == f_open(&file, "0:GD32.TXT", FA_CREATE_ALWAYS | FA_WRITE)) {
/* write buffer to file */
bytesToWrite = sizeof(WriteTextBuff);
res = f_write (&file, WriteTextBuff, bytesToWrite, (void *)&bytesWritten);
/* EOF or error */
if ((0U == bytesWritten) || (FR_OK != res)) {
printf("\r\n > GD32.TXT CANNOT be written.");
} else {
printf("\r\n > GD32.TXT created in the disk.");
}
/* close file and file system */
f_close(&file);
f_mount(NULL, "0:/", 1);
} else {
printf("\r\n > GD32.TXT created in the disk.");
}
usbh_usr_application_state = USBH_USR_FS_DEMOEND;
printf("\r\n > The MSC host demo is end.");
break;
case USBH_USR_FS_DEMOEND:
break;
default:
break;
}
return(0);
}
/*!
\brief displays disk content
\param[in] path: pointer to root path
\param[in] recu_level: recursive level
\param[out] none
\retval status
*/
static uint8_t explore_disk (char* path, uint8_t recu_level)
{
FRESULT res;
FILINFO fno;
DIR dir;
char *fn;
res = f_opendir(&dir, path);
if (res == FR_OK) {
while ((usbh_core.host.connect_status)) {
res = f_readdir(&dir, &fno);
if (FR_OK != res || 0U == fno.fname[0]) {
break;
}
if ('.' == fno.fname[0]) {
continue;
}
fn = fno.fname;
line_idx++;
if (line_idx > 4) {
line_idx = 0;
// printf("\r\n > Press User Key to continue....");
/*key USER in polling*/
// while ((usbh_core.host.connect_status) && \
// (SET == gd_eval_key_state_get (KEY_USER))) {
// toggle_leds();
// }
}
if(1U == recu_level){
printf("\r\n |__");
}else if(2U == recu_level){
printf("\r\n | |__");
}
if(AM_DIR == fno.fattrib){
printf("\r\n %s", fno.fname);
}else{
printf("\r\n %s", fno.fname);
}
if((AM_DIR == fno.fattrib) && (1U == recu_level)){
explore_disk(fn, 2);
}
}
}
return res;
}
/*!
\brief toggle leds to shows user input state
\param[in] none
\param[out] none
\retval none
*/
static void toggle_leds(void)
{
static uint32_t i;
if (0x10000U == i++) {
// gd_eval_led_toggle(LED2);
// gd_eval_led_toggle(LED3);
i = 0;
}
}
/*!
\brief deinit user state and associated variables
\param[in] none
\param[out] none
\retval none
*/
void usbh_user_deinit(void)
{
usbh_usr_application_state = USBH_USR_FS_INIT;
}