GD32450Z U盘通信教程:使用内部全速PHY实现USB MSC主机U盘通信

USB高速(USBHS)支持主机模式、设备模式和OTG模式,并且包含了一个内部的全速USB PHY。对于全速和低速操作,不需要外部的USB PHY。本文为学习记录,介绍了在主机模式下,利用内部PHY实现U盘的通信。

1. USBHS基础知识介绍

1.1 USBHS 信号线描述

在主机或设备模式下,利用内部 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.2 USB库及资料介绍

1、STM32官方程序:GD32F4xx_usb_library (用 USB host MSC模式)

2、文件系统:FatFs为官方R0.13C版。

1.2.1 USBHS 模块固件库架构

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

设备类应用程序接口

1.2.2  FatFs文件系统

FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统。这里我们使用R0.13c版本,一般我们只用到f_mount()、 f_open()、 f_write()、 f_read()就可以实现文件的读写操作。

本文移植过程中,涉及的文件如下:

 

2. USB配置

2.1 USB初始化配置

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
}

2.2  中断设置

开启TIMER2和USB_HS中断:

void TIMER2_IRQHandler(void)
{
    usb_timer_irq();
}


void USBHS_IRQHandler(void)
{
    usbh_isr(&usbh_core);
}

3. USB程序

在usbh_usr.c编写我们需要执行的程序。这里我们参考GD32官方demo案例库,插入u盘后将会枚举信息,之后显示U盘根目录文件,最后向程序中写入测试文件。

#include <string.h>
#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;
}

物联沃分享整理
物联沃-IOTWORD物联网 » GD32450Z U盘通信教程:使用内部全速PHY实现USB MSC主机U盘通信

发表评论