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

2023-10-04 04:40

本文主要是介绍国产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上新建工程如下图:这些是GD提供的库和开发板定义
在这里插入图片描述
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传输效率的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1294

相关文章

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

五大特性引领创新! 深度操作系统 deepin 25 Preview预览版发布

《五大特性引领创新!深度操作系统deepin25Preview预览版发布》今日,深度操作系统正式推出deepin25Preview版本,该版本集成了五大核心特性:磐石系统、全新DDE、Tr... 深度操作系统今日发布了 deepin 25 Preview,新版本囊括五大特性:磐石系统、全新 DDE、Tree

国产游戏崛起:技术革新与文化自信的双重推动

近年来,国产游戏行业发展迅猛,技术水平和作品质量均得到了显著提升。特别是以《黑神话:悟空》为代表的一系列优秀作品,成功打破了过去中国游戏市场以手游和网游为主的局限,向全球玩家展示了中国在单机游戏领域的实力与潜力。随着中国开发者在画面渲染、物理引擎、AI 技术和服务器架构等方面取得了显著进展,国产游戏正逐步赢得国际市场的认可。然而,面对全球游戏行业的激烈竞争,国产游戏技术依然面临诸多挑战,未来的

国产游戏行业的崛起与挑战:技术创新引领未来

国产游戏行业的崛起与挑战:技术创新引领未来 近年来,国产游戏行业蓬勃发展,技术水平不断提升,许多优秀作品在国际市场上崭露头角。从画面渲染到物理引擎,从AI技术到服务器架构,国产游戏已实现质的飞跃。然而,面对全球游戏市场的激烈竞争,国产游戏技术仍然面临诸多挑战。本文将探讨这些挑战,并展望未来的机遇,深入分析IT技术的创新将如何推动行业发展。 国产游戏技术现状 国产游戏在画面渲染、物理引擎、AI

bash: arm-linux-gcc: No such file or directory

ubuntu出故障重装了系统,一直用着的gcc使用不了,提示bash: arm-linux-gcc: No such file or directorywhich找到的命令所在的目录 在google上翻了一阵发现此类问题的帖子不多,后来在Freescale的的LTIB环境配置文档中发现有这么一段:     # Packages required for 64-bit Ubuntu

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte

Usb Audio Device Descriptor(10) Hid Device

对于 Standard Interface Descriptor, 当 bInterfaceClass=0x03时,即为HID设备。Standard Interface Descriptor如下 struct usb_standard_interface_descriptor{U8 bLength; /*Size of this descriptor in bytes*/U8 bDescrip

stl的sort和手写快排的运行效率哪个比较高?

STL的sort必然要比你自己写的快排要快,因为你自己手写一个这么复杂的sort,那就太闲了。STL的sort是尽量让复杂度维持在O(N log N)的,因此就有了各种的Hybrid sort algorithm。 题主你提到的先quicksort到一定深度之后就转为heapsort,这种是introsort。 每种STL实现使用的算法各有不同,GNU Standard C++ Lib

Cortex-A7:ARM官方推荐的嵌套中断实现机制

0 参考资料 ARM Cortex-A(armV7)编程手册V4.0.pdf ARM体系结构与编程第2版 1 前言 Cortex-M系列内核MCU中断硬件原生支持嵌套中断,开发者不需要为了实现嵌套中断而进行额外的工作。但在Cortex-A7中,硬件原生是不支持嵌套中断的,这从Cortex-A7中断向量表中仅为外部中断设置了一个中断向量可以看出。本文介绍ARM官方推荐使用的嵌套中断实现机