jjzjj

GD32450Z U盘通信教程——使用USBHS利用内部全速PHY作为USB MSC主机实现U盘通信

河狸打捞员 2023-11-11 原文

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引脚复用
DMPB14USBHS_DM    AF12
DPPB15USBHS_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;
}

有关GD32450Z U盘通信教程——使用USBHS利用内部全速PHY作为USB MSC主机实现U盘通信的更多相关文章

  1. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  2. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  3. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  4. 在VMware16虚拟机安装Ubuntu详细教程 - 2

    在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主

  5. hadoop安装之保姆级教程(二)之YARN的配置 - 2

    1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模

  6. ruby - 在 RUBY 上的 PADRINO 框架上使用 RSPEC 进行测试的教程 - 2

    我是Ruby新手,并被要求在我们的新项目中使用它。我们还被要求使用Padrino(Sinatra)作为后端/框架。我们被要求使用Rspec进行测试。我一直在寻找可以指导在Padrino上使用RspecforRuby的教程。我得到的主要是引用RoR。但是,我需要RubyonPadrino。请在任何入门/指南/引用/讨论等方面指导我。如有不妥之处请指正。可能是我没有针对我的问题搜索正确的词/短语组合。我正在使用Ruby1.9.3和Padrinov.0.10.6。注意:我还提到了SOquestion,但它没有帮助。 最佳答案 我没用过Pa

  7. 区块链入门教程(6)--WeBASE-Front节点前置服务安装 - 2

    文章目录1.任务背景2.任务目标3.相关知识点4.任务实操4.1安装配置JDK4.2启动FISCOBCOS4.3下载解压WeBASE-Front4.4拷贝sdk证书文件4.5启动节点4.6访问节点4.7检查运行状态5.任务总结1.任务背景FISCOBCOS其实是有控制台管理工具,用来对区块链系统进行各种管理操作。但是对于初学者来说,还是可视化界面更友好,本节就来介绍WeBASE管理平台,这是一款微众银行开源的自研区块链中间件平台,可以降低区块链使用的门槛,大幅提高区块链应用的开发效率。微众银行是腾讯牵头设立的民营银行,在国内民营银行里还是比较出名的。微众银行参与FISCOBCOS生态建设,一定

  8. ruby-on-rails - rails 教程 : Putting flash messages in partial yields error "undefined method ` each' for nil:NilClass"? - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:FlashMessagesinPartials(Rails3)我正在做MichaelHartl的Railstutorial和listing7.26将flash消息添加到应用程序布局:...">...这很好用。但是,我试图通过在我的部分文件夹中创建一个_flash.html.erb来清理这段代码...">-->...并且比使用......在我的应用程序布局中,我的所有Rspec测试开始失败,每个测试都显示以下消息:Failure/Error:before{visitsignup_path}ActionView:

  9. ruby-on-rails - Ruby on Rails 教程 - 5.26 - Sublime Text "Unable to Save"新文件 "spec/support/utilities.rb" - 2

    我正在使用SublimeText2,同时遵循MichaelHartl的RubyonRails教程。可以在http://ruby.railstutorial.org/book/ruby-on-rails-tutorial找到我所指的教程的具体部分。(ctrl+F“list5.26”)。我能够创建规范/支持文件。但是,在尝试创建spec/support/utilities.rb文件时,我收到消息“无法保存~/rails_projects/sample_app/spec/support/utilities.rb”。有人知道为什么会这样吗?SublimeText论坛上有人似乎遇到了完全相同的问

  10. 华为ensp详细安装包、安装教程及所遇问题 - 2

    目录一、安装包链接二、安装详细步骤1.安装Wireshark和WinPcap2.安装OracleVMVirtualBox3.安装ensp三、安装后注册四、启动路由器出现40错误怎么解决一、安装包链接二、安装详细步骤链接:https://pan.baidu.com/s/1QbUUYMOMIV2oeIKHWP1SpA?pwd=xftx提取码:xftx1.安装Wireshark和WinPcap找到Wireshark安装包所在文件夹,双击它,按照以下步骤安装。2.安装OracleVMVirtualBox找到OracleVMVirtualBox安装包所在文件夹,双击它,按照以下步骤安装。注:可自定义安装

随机推荐