打印

国产ARM兆易创新GD32F450的USB传输效率

[复制链接]
2254|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
软件工具

GD没有自己的集成开发环境,更谈不上STM32CubeIDE了,只能使用Keil,好在GD提供的操作说明还比较详细,也就没有什么困难。只是有些细节设置容易掉坑里,但折腾一阵子还是可以爬出来的。GD32F450的开发板自带CMSIS-DAP Debugger,不用再购买编程器,用起来还算方便。
这里GD提供的资料是Keil5以下版本开发的,所以要注意一下将工程文件的后缀修改一下,将*.uvproj和 *.uvopt改为 *.uvprojx和 *.uvoptx,不然在Keil5中添加不了Device。
另外在上位机这边就是libusb-1.0.24,zadig-2.5和VS2019了,我是写c出身,以下都是使用c或c++语言编写。zadig-2.5可以自动生成windows或linux自定义USB设备的驱动,libusb用来写应用了。


使用特权

评论回复
沙发
木木guainv|  楼主 | 2021-7-1 15:46 | 只看该作者
开始

在Keil上新建工程如下图:



main.c,myusb_corc.c是主要的功能文件,其中myusb_corc.c中是USB设备的描述和回调函数,main.c是入口和调试输出。其他文件是从GD提供例子工程CDC_ACM中复制过来的。注意要为先定的端口设置FIFO_HS_SIZE的大小。



使用特权

评论回复
板凳
木木guainv|  楼主 | 2021-7-1 15:46 | 只看该作者
myusb_corc.c中USB的描述段如下:有一个接口,包含一进一出两个Bulk方式的端点

__ALIGN_BEGIN const usb_descriptor_device_struct device_descripter __ALIGN_END =
{
    .Header =
     {
         .bLength = USB_DEVICE_DESC_SIZE,
         .bDescriptorType = USB_DESCTYPE_DEVICE
     },
    .bcdUSB = 0x0200,
    .bDeviceClass = 0xff, // 厂商定义的描述符
    .bDeviceSubClass = 0x00,
    .bDeviceProtocol = 0x00,
    .bMaxPacketSize0 = USB_MAX_EP0_SIZE,
    .idVendor = USBD_VID,
    .idProduct = USBD_PID,
    .bcdDevice = 0x0100,
    .iManufacturer = USBD_MFC_STR_IDX,
    .iProduct = USBD_PRODUCT_STR_IDX,
    .iSerialNumber = USBD_SERIAL_STR_IDX,
    .bNumberConfigurations = USBD_CFG_MAX_NUM
};

/* USB device configuration descriptor */
__ALIGN_BEGIN const usb_descriptor_configuration_set_struct configuration_descriptor __ALIGN_END =
{
    .Config = // 9Bytes
    {
        .Header =
         {
             .bLength = sizeof(usb_descriptor_configuration_struct),
             .bDescriptorType = USB_DESCTYPE_CONFIGURATION
         },
        .wTotalLength = USB_MY_CONFIG_DESC_SIZE, // 32Bytes
        .bNumInterfaces = 0x01,
        .bConfigurationValue = 0x01,
        .iConfiguration = 0x00,
        .bmAttributes = 0xC0,        // 自给电源
        .bMaxPower = 0x32
    },

    .Printer_Interface = // 9Bytes
    {
        .Header =
         {
             .bLength = sizeof(usb_descriptor_interface_struct),
             .bDescriptorType = USB_DESCTYPE_INTERFACE
         },
        .bInterfaceNumber = 0x00,
        .bAlternateSetting = 0x00,
        .bNumEndpoints = 0x02,
        .bInterfaceClass = 0x00,
        .bInterfaceSubClass = 0x00,
        .bInterfaceProtocol = 0x00,
        .iInterface = 0x00
    },

    .Printer_IN_Endpoint = // 7Bytes
    {
        .Header =
         {
             .bLength = sizeof(usb_descriptor_endpoint_struct),
             .bDescriptorType = USB_DESCTYPE_ENDPOINT
         },
        .bEndpointAddress = DEVICE_IN_EP,
        .bmAttributes = 0x02, // Bulk
        .wMaxPacketSize = DEVICE_IN_PACKET,
        .bInterval = 0x00
    },

    .Printer_OUT_Endpoint = // 7Bytes
    {
        .Header =
         {
             .bLength = sizeof(usb_descriptor_endpoint_struct),
             .bDescriptorType = USB_DESCTYPE_ENDPOINT
         },
        .bEndpointAddress = DEVICE_OUT_EP,
        .bmAttributes = 0x02,
        .wMaxPacketSize = DEVICE_OUT_PACKET,
        .bInterval = 0x00
    },
};

/* USB language ID Descriptor */
__ALIGN_BEGIN const usb_descriptor_language_id_struct usbd_language_id_desc __ALIGN_END =
{
    .Header =
     {
         .bLength = sizeof(usb_descriptor_language_id_struct),
         .bDescriptorType = USB_DESCTYPE_STRING
     },
    .wLANGID = ENG_LANGID
};

__ALIGN_BEGIN uint8_t* usbd_strings[] __ALIGN_END =
{
    [USBD_LANGID_STR_IDX] = (uint8_t *)&usbd_language_id_desc,
    [USBD_MFC_STR_IDX] = USBD_STRING_DESC("GigaDevice"),
    [USBD_PRODUCT_STR_IDX] = USBD_STRING_DESC("GD32 USB in HS Mode"),
    [USBD_SERIAL_STR_IDX] = USBD_STRING_DESC("GD32F4xx-1.0.0-5f9e10dma")
};


使用特权

评论回复
地板
木木guainv|  楼主 | 2021-7-1 15:47 | 只看该作者
myusb_corc.c中USB的初始化和回调函数如下:注意usbd_ep_rx这个库函数,USB设备的每次读交易transaction,都要调用一次用来初始化一些寄存器,不然不能进行下一次transaction,这是一个坑。usbd_rxnum_get函数是自己增加的函数,库中的usbd_rxcount_get只能得到最后接收包的大小,这个坑很大,导致我开始总是收不到完整的数据。

/*!
    \brief      initialize the printer device
    \param[in]  pudev: pointer to usb device instance
    \param[in]  config_index: configuration index
    \param[out] none
    \retval     usb device operation status
*/
uint8_t mydevice_init (void *pudev, uint8_t config_index)
{
    /* initialize Tx endpoint */
    usbd_ep_init(pudev, &(configuration_descriptor.Printer_IN_Endpoint));

    /* initialize Rx endpoint */
    usbd_ep_init(pudev, &(configuration_descriptor.Printer_OUT_Endpoint));

    /* prepare to receive data */
    usbd_ep_rx(pudev, DEVICE_OUT_EP, data_buffer_in, DATA_IN_BUFSIZE);

    return USBD_OK;

}

/*!
    \brief      de-initialize the printer device
    \param[in]  pudev: pointer to usb device instance
    \param[in]  config_index: configuration index
    \param[out] none
    \retval     usb device operation status
*/
uint8_t mydevice_deinit (void *pudev, uint8_t config_index)
{
    /* deinitialize HID endpoints */
    usbd_ep_deinit (pudev, DEVICE_IN_EP);
    usbd_ep_deinit (pudev, DEVICE_OUT_EP);
    usbd_ep_rx(pudev, DEVICE_OUT_EP, data_buffer_in, DATA_IN_BUFSIZE);

    return USBD_OK;
}

uint8_t mydevice_req_handler (void *pudev, usb_device_req_struct *req)
{
    return USBD_OK;
}

/*!
    \brief      handle data stage
    \param[in]  pudev: pointer to usb device instance
    \param[in]  rx_tx: the flag of Rx or Tx
    \param[in]  ep_id: the endpoint ID
    \param[out] none
    \retval     usb device operation status
*/
uint8_t mydevice_data_handler(void *pudev, usb_dir_enum rx_tx, uint8_t ep_id)
{
        uint32_t revnum = 0;
       
        if ((USB_TX == rx_tx) && ((DEVICE_IN_EP & 0x7F) == ep_id))
        {        // 当本次发送数据小于缓冲区大小时,表示本次交易结束,或者当待发数据如果是整数倍缓冲区,则发送空包作为结束
                usb_ep_struct *ep = &((usb_core_handle_struct *)pudev)->dev.out_ep[DEVICE_OUT_EP];
        if ((ep->xfer_len % ep->endp_mps == 0) && (ep->xfer_len != 0))
                {
            usbd_ep_tx(pudev, ep_id, NULL, 0U); // 发送空包
        }
                else
                {
            bSent = 1; // 缓冲区没有待发数据,可进行下次交易
        }
        return USBD_OK;
    }
        else if ((USB_RX == rx_tx) && ((DEVICE_OUT_EP & 0x7F) == ep_id))
        {
                bReceived = 1; // 数据到达,等待处理
                revnum = usbd_rxnum_get(ep_id);
                usbd_ep_rx(pudev, DEVICE_OUT_EP, data_buffer_in, DATA_IN_BUFSIZE);
                total += revnum;
                revcnt++;
                       
        return USBD_OK;
    }
    return USBD_FAIL;
}

/*!
    \brief      receive CDC ACM data
    \param[in]  pudev: pointer to USB device instance
    \param[out] none
    \retval     USB device operation status
*/
void cdc_acm_data_receive(void *pudev)
{
    bReceived = 0;
    usbd_ep_rx(pudev, DEVICE_OUT_EP, (uint8_t*)data_buffer_in, DATA_IN_BUFSIZE);
}

/*!
    \brief      send CDC ACM data
    \param[in]  pudev: pointer to USB device instance
    \param[out] none
    \retval     USB device operation status
*/
void cdc_acm_data_send (void *pudev, uint32_t data_len)
{
        bSent = 0;
        usbd_ep_tx(pudev, DEVICE_IN_EP, (uint8_t*)data_buffer_out, data_len);
}

// 修改库函数得到每次接收到的字节数
uint16_t usbd_rxnum_get (uint8_t ep_id)
{
        uint32_t xfer_size = 0;
        uint16_t revnum = 0;
       
        //uint16_t pcnt = (USB_DOEPxLEN(ep_id) & DEPLEN_PCNT) >> 19U;
        xfer_size = USB_DOEPxLEN(ep_id) & DEPLEN_TLEN;
        revnum = DATA_IN_BUFSIZE - xfer_size;
       
    return revnum;
}


使用特权

评论回复
5
木木guainv|  楼主 | 2021-7-1 15:47 | 只看该作者
main.c中是初始化及应用:当接收完100MB的数据后亮灯

#include "usb_delay.h"
#include "myusb_core.h"
#include "string.h"

usb_core_handle_struct usbhs_core_dev =
{
    .dev =
    {
        .dev_desc = (uint8_t *)&device_descripter,
        .config_desc = (uint8_t *)&configuration_descriptor,
        .strings = usbd_strings,
        .class_init = mydevice_init,
        .class_deinit = mydevice_deinit,
        .class_req_handler = mydevice_req_handler,
        .class_data_handler = mydevice_data_handler
    },
    .udelay = usb_udelay,
    .mdelay = usb_mdelay
};

extern int revcnt;
extern int total;
extern uint8_t bSent, bReceived;

extern uint8_t data_buffer_out[DATA_OUT_BUFSIZE];

//void system_clock_config(void);
void usb_clock_config(void);
void usb_gpio_config(void);
void usb_interrupt_config(void);

/*!
    \brief      main routine will construct a USB keyboard
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
        int i;
       
        /* LED initialize */
        gd_eval_led_init(LED1);
       
    /* configure USB GPIO */
    usb_gpio_config();

    /* configure USB clock */
    usb_clock_config();

    /* USB device stack configure */
    usbd_init(&usbhs_core_dev,
#ifdef USE_USBFS
    USB_FS_CORE_ID
#elif defined(USE_USBHS)
    USB_HS_CORE_ID
#endif
    );

    /* USB interrupt configure */
    usb_interrupt_config();

    /* check if USB device is enumerated successfully */
    while (usbhs_core_dev.dev.status != USB_STATUS_CONFIGURED) {}

        revcnt = 0;
        gd_eval_led_off(LED1);
    while (1)
        {
                if (total == 1024 * 1000 * 100)
                {
                        gd_eval_led_on(LED1);
                        revcnt = total = 0;
                }
    }
}


使用特权

评论回复
6
木木guainv|  楼主 | 2021-7-1 15:47 | 只看该作者
PC驱动安装及使用

使用zadig完成驱动生成:



使用特权

评论回复
7
木木guainv|  楼主 | 2021-7-1 15:48 | 只看该作者
安装好libusb库,在VS中新建C++工程:

#include <iostream>
#include "libusb.h"
#include "string.h"

#define USBD_VID        0x28E9
#define USBD_PID        0x028D
#define EP_IN        0x82
#define EP_OUT        0x02
#define BUF_SIZE 512 * 16 // 8k

int main()
{
    libusb_context *usb_context = nullptr;
    libusb_device **dev_list;
    libusb_device *dev;
    libusb_device_handle *dev_handle = NULL;
    struct libusb_device_descriptor desc;
    libusb_config_descriptor* config_descriptor;
    ssize_t cnt;
    int i(0), ret(0), length(0), config(0), total(0);
    unsigned char* usb_data_buffer;
    DWORD start, end;

    if (ret = libusb_init(&usb_context) < 0)
    {
        goto ERR_EXIT;
    }
    if (ret = libusb_get_device_list(usb_context, &dev_list) < 0)
    {
        goto ERR_EXIT;
    }
    while ((dev = dev_list[i++]) != NULL)
    {
        ret = libusb_get_device_descriptor(dev, &desc);
        if (ret < 0)
        {
            goto ERR_EXIT;
        }
        if (desc.idVendor == USBD_VID && desc.idProduct == USBD_PID)
        {
            ret = libusb_open(dev, &dev_handle);
            if (ret != 0)
            {
                libusb_free_device_list(dev_list, 1);
                goto ERR_EXIT;
            }
            break;
        }
    }
    libusb_free_device_list(dev_list, 1);

    if (dev_handle == nullptr)
    {
        goto ERR_EXIT;
    }
    //dev_handle = libusb_open_device_with_vid_pid(usb_context, USBD_VID, USBD_PID);
    ret = libusb_kernel_driver_active(dev_handle, 0);
    if (ret == 1)
    {
        libusb_detach_kernel_driver(dev_handle, 0);
        goto ERR_EXIT;
    }
    if (ret = libusb_claim_interface(dev_handle, 0) < 0)
    {
        goto ERR_EXIT;
    }
    //ret = libusb_get_configuration(dev_handle, &config);
    //dev = libusb_get_device(dev_handle);
    //libusb_get_config_descriptor(dev, 0, &config_descriptor);

    start = GetTickCount();
    usb_data_buffer = new unsigned char[BUF_SIZE];
    memset(usb_data_buffer, 0x55, BUF_SIZE);
    for (i = 0; i < 10000; i++)
    {
        //memcpy(usb_data_buffer, &i, sizeof(int));
        ret = libusb_bulk_transfer(dev_handle, EP_OUT, usb_data_buffer, BUF_SIZE, &length, 200);
        total += length;

        if (ret < 0)
        {
            goto ERR_EXIT;
        }
    }
    end = GetTickCount();
    printf("i = %d, time=%d, total = %d\n", i, end - start, total);
    delete[] usb_data_buffer;

ERR_EXIT:
    printf("err:%s\r\n", libusb_strerror(ret));

    libusb_close(dev_handle);
    libusb_exit(usb_context);

    getchar();
    return 0;
}


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

134

主题

3870

帖子

5

粉丝