本文主要是介绍国产ARM兆易创新GD32F450的USB传输效率,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
国产ARM兆易创新GD32F450的USB传输效率
由于美国与咱闹翻了,国家电网要求二次设备采用国产芯片替代国外产品,所以选用了与STM32兼容的GD32单片机做替换。期间涉及到使用USB传输采样数据有个带宽的问题,所以就准备测试一下该芯片USBHS方式下的带宽。先说结果,主机到设备30MB/s以上的速度,设备到主机25MB/s以上的速度,可以满足我的需求。但我感觉还可以优化来提高速度。
USB基础
推荐:
从零开始学USB(七、端点、管道、接口、配置、设备)
从零开始学USB(十、USB的描述符)
这两篇文章对USB的软设置方面做了详细描述,避免对USB协议耗时费力的学习,当然不懂的地方还是要有针对性的看USB2.0协议的原版
软件工具
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用来写应用了。
开始
在Keil上新建工程如下图:
main.c,myusb_corc.c是主要的功能文件,其中myusb_corc.c中是USB设备的描述和回调函数,main.c是入口和调试输出。其他文件是从GD提供例子工程CDC_ACM中复制过来的。注意要为先定的端口设置FIFO_HS_SIZE的大小。
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")
};
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;
}
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_USBFSUSB_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;}}
}
PC驱动安装及使用
使用zadig完成驱动生成:
安装好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 // 8kint 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;
}
这篇关于国产ARM兆易创新GD32F450的USB传输效率的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!