700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > GD32实现USB HID自定义复合设备

GD32实现USB HID自定义复合设备

时间:2020-10-30 18:31:26

相关推荐

GD32实现USB HID自定义复合设备

复合设备是啥,通俗讲就是一个USB物理设备可以实现多个功能,在主机端可以看到多个设备。通常实现HID复合设备有两种方式。第一种是使用同一个接口,修改报告描述符,增加一个功能集合,同时需要使用报告ID来区分哪一个设备,这样主机端和设备端需要增加报告ID处理,但只需要两个端点来实现功能,对于端口资源较少的MCU可以使用;第二种是使用两个接口,每个接口对应不同的报告描述符,不需要特意使用报告ID来区分,但不同接口使用独立的端点。

本文章使用不同接口来实现,基于GD32例程开发。

开发准备

(1)下载官方GD32F1x0_Firmware_Library_v3.1.0,使用GD32F1x0_Firmware_Library_v3.1.0\Projects\USBD\HID_custom工程

(2)keil5添加GD系列

(3)连接GDlink和串口工具

移植代码

2.1 修改控制控制USBDP上拉电阻引脚,(内部无法直接控制上下拉,因此增加此电路,否则主机不会枚举设备)

#define USB_PULLUP GPIOC //PORT

#define USB_PULLUP_PIN GPIO_PIN_13 //PIN

#define RCC_AHBPeriph_GPIO_PULLUP RCU_GPIOC //时钟

2.2 增加串口1输出调试信息,main函数里添加:

/* uart configuration */

gd_eval_com_init(EVAL_COM1);

添加如下代码实现printf功能

int fputc(int ch, FILE *f){usart_data_transmit(EVAL_COM1, (uint8_t)ch);while(RESET == usart_flag_get(EVAL_COM1, USART_FLAG_TBE));return ch;}

int main(void){/* system clocks configuration */rcu_config();/* keys configuration */key_config();/* leds configuration */led_config();//gd_eval_led_on(LED1);/* GPIO configuration */gpio_config();/* uart configuration */gd_eval_com_init(EVAL_COM1);/* USB device configuration */usbd_core_init(&usb_device_dev);/* NVIC configuration */nvic_config();/* enabled USB pull-up */gpio_bit_reset(USB_PULLUP, USB_PULLUP_PIN);//使能上拉/* now the usb device is connected */usb_device_dev.status = USBD_CONNECTED;printf("a usart transmit test example!");while (1){};}

2.3 修改USB相关描述符

2.3.1 设备描述符:不需要修改

const usb_descriptor_device_struct device_descripter ={.Header = {.bLength = USB_DEVICE_DESC_SIZE, .bDescriptorType = USB_DESCTYPE_DEVICE},.bcdUSB = 0x0200,.bDeviceClass = 0x00,.bDeviceSubClass = 0x00,.bDeviceProtocol = 0x00,.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,.idVendor = USBD_VID,.idProduct = USBD_PID,.bcdDevice = 0x0200,.iManufacturer = USBD_MFC_STR_IDX,.iProduct = USBD_PRODUCT_STR_IDX,.iSerialNumber = USBD_SERIAL_STR_IDX,.bNumberConfigurations = USBD_CFG_MAX_NUM};

2.3.2 配置描述符:需要增加第二个接口的接口描述符,端点描述符

const usb_descriptor_configuration_set_struct configuration_descriptor = {.Config = {.Header = {.bLength = sizeof(usb_descriptor_configuration_struct), .bDescriptorType = USB_DESCTYPE_CONFIGURATION },.wTotalLength = CUSTOMHID_CONFIG_DESC_SIZE,.bNumInterfaces = 0x02,//==接口数修改为2==.bConfigurationValue = 0x01,.iConfiguration = 0x00,.bmAttributes = 0xc0,.bMaxPower = 0x32},.HID_Interface = {.Header = {.bLength = sizeof(usb_descriptor_interface_struct), .bDescriptorType = USB_DESCTYPE_INTERFACE },.bInterfaceNumber = 0x00, //==接口序号为0,表示第一个接口==.bAlternateSetting = 0x00,.bNumEndpoints = 0x02, //==使用两个接口==.bInterfaceClass = 0x03,.bInterfaceSubClass = 0x00,.bInterfaceProtocol = 0x00,.iInterface = 0x00},.HID_VendorHID = {.Header = {.bLength = sizeof(usb_hid_descriptor_hid_struct),.bDescriptorType = CUSTOMHID_DESC_TYPE },.bcdHID = 0x0111,.bCountryCode = 0x00,.bNumDescriptors = 0x01,.bDescriptorType = CUSTOMHID_REPORT_DESCTYPE,.wDescriptorLength = CUSTOMHID_REPORT_DESC_SIZE, //==报告描述符的长度==},.HID_ReportINEndpoint = {.Header = {.bLength = sizeof(usb_descriptor_endpoint_struct), .bDescriptorType = USB_DESCTYPE_ENDPOINT },.bEndpointAddress = CUSTOMHID_IN_EP,.bmAttributes = 0x03,.wMaxPacketSize = CUSTOMHID_IN_PACKET,.bInterval = 0x20},.HID_ReportOUTEndpoint = {.Header = {.bLength = sizeof(usb_descriptor_endpoint_struct), .bDescriptorType = USB_DESCTYPE_ENDPOINT},.bEndpointAddress = CUSTOMHID_OUT_EP,.bmAttributes = 0x03,.wMaxPacketSize = CUSTOMHID_OUT_PACKET,.bInterval = 0x20},#if 1/*第二个接口的接口描述符和端点描述符*/.HID1_Interface = {.Header = {.bLength = sizeof(usb_descriptor_interface_struct), .bDescriptorType = USB_DESCTYPE_INTERFACE },.bInterfaceNumber = 0x01, //==接口序号为1,表示第二个接口==.bAlternateSetting = 0x00,.bNumEndpoints = 0x02,.bInterfaceClass = 0x03,.bInterfaceSubClass = 0x00,.bInterfaceProtocol = 0x00,.iInterface = 0x00},.HID1_VendorHID = {.Header = {.bLength = sizeof(usb_hid_descriptor_hid_struct),.bDescriptorType = CUSTOMHID_DESC_TYPE },.bcdHID = 0x0111,.bCountryCode = 0x00,.bNumDescriptors = 0x01,.bDescriptorType = CUSTOMHID_REPORT_DESCTYPE,.wDescriptorLength = CUSTOMHID1_REPORT_DESC_SIZE,},.HID1_ReportINEndpoint = {.Header = {.bLength = sizeof(usb_descriptor_endpoint_struct), .bDescriptorType = USB_DESCTYPE_ENDPOINT },.bEndpointAddress = CUSTOMHID1_IN_EP,.bmAttributes = 0x03,.wMaxPacketSize = CUSTOMHID1_IN_PACKET,.bInterval = 0x14},.HID1_ReportOUTEndpoint = {.Header = {.bLength = sizeof(usb_descriptor_endpoint_struct), .bDescriptorType = USB_DESCTYPE_ENDPOINT},.bEndpointAddress = CUSTOMHID1_OUT_EP,.bmAttributes = 0x03,.wMaxPacketSize = CUSTOMHID_OUT_PACKET,.bInterval = 0x20}#endif};

2.3.3 报告描述符,用户自定义功能,可以根据自己需求更改

const uint8_t customhid_report_descriptor[CUSTOMHID_REPORT_DESC_SIZE] ={/* USER CODE BEGIN 0 */ 0x06,0xA0,0xFF, //用法页(FFA0h, vendor defined)0x09, 0x01, //用法(vendor defined)0xA1, 0x01, //集合(Application)0x09, 0x02 ,//用法(vendor defined)0xA1, 0x00, //集合(Physical)0x06,0xA1,0xFF, //用法页(vendor defined)//输入报告//0x85,0x01,0x09, 0x03 ,//用法(vendor defined)0x09, 0x04, //用法(vendor defined)0x15, 0x80, //逻辑最小值(0x80 or -128)0x25, 0x7F, //逻辑最大值(0x7F or 127)0x35, 0x00, //物理最小值(0)0x45, 0xFF, //物理最大值(255)0x75, 0x08, //报告长度Report size (8位)0x95, 63, //报告数值(64 fields)0x81, 0x02, //输入(data, variable, absolute)//输出报告// 0x85, 0x02,0x09, 0x05, //用法(vendor defined)0x09, 0x06, //用法(vendor defined)0x15, 0x80, //逻辑最小值(0x80 or -128)0x25, 0x7F, //逻辑最大值(0x7F or 127)0x35, 0x00, //物理最小值(0)0x45, 0xFF, //物理最大值(255)0x75, 0x08, //报告长度(8位)0x95, 63, //报告数值(64 fields)0x91, 0x02, //输出(data, variable, absolute)0xC0, //集合结束(Physical)0xC0, //集合结束(Physical)};

2.4 相关函数修改

2.4.1修改端点初始化

usbd_status_enum custom_hid_init (void *pudev, uint8_t config_index){/* initialize Tx endpoint */usbd_ep_init(pudev, ENDP_SNG_BUF, &(configuration_descriptor.HID_ReportINEndpoint));/* initialize Rx endpoint */usbd_ep_init(pudev, ENDP_SNG_BUF, &(configuration_descriptor.HID_ReportOUTEndpoint));/* initialize Tx endpoint */usbd_ep_init(pudev, ENDP_SNG_BUF, &(configuration_descriptor.HID1_ReportINEndpoint));//增加的端点初始化/* initialize Rx endpoint */usbd_ep_init(pudev, ENDP_SNG_BUF, &(configuration_descriptor.HID1_ReportOUTEndpoint));/* prepare receive data */usbd_ep_rx(pudev, CUSTOMHID_OUT_EP, report_buf, 64);//设置输出报告bufusbd_ep_rx(pudev, CUSTOMHID1_OUT_EP, report1_buf, 64);return USBD_OK;}

2.4.2修改端点释放

usbd_status_enum custom_hid_deinit (void *pudev, uint8_t config_index){/* deinitialize HID endpoints */usbd_ep_deinit (pudev, CUSTOMHID_IN_EP);usbd_ep_deinit (pudev, CUSTOMHID_OUT_EP);usbd_ep_deinit (pudev, CUSTOMHID1_IN_EP);usbd_ep_deinit (pudev, CUSTOMHID1_OUT_EP);return USBD_OK;}

2.4.3修改HID请求处理函数,主要是增加接口判断,req->wIndex指示为那个接口

usbd_status_enum custom_hid_req_handler (void *pudev, usb_device_req_struct *req){uint16_t len = 0;uint8_t *pbuf = NULL;uint8_t usbd_customhid_report_length = 0;switch (req->bmRequestType & USB_REQ_MASK) {case USB_CLASS_REQ:switch (req->bRequest) {#if 0case GET_REPORT:if (req->wLength == 2 ) {report_buf[0] = 0;report_buf[1] = 0;usbd_ep_tx (pudev, EP0_IN, report_buf, 2);}break;#endifcase GET_IDLE:usbd_ep_tx (pudev, EP0_IN, (uint8_t *)&usbd_customhid_idlestate, 1);break;case GET_PROTOCOL:usbd_ep_tx (pudev, EP0_IN, (uint8_t *)&usbd_customhid_protocol, 1);break;case SET_REPORT:flag = 1;//usbd_customhid_report_id = (uint8_t)(req->wValue);usbd_customhid_interface_id = (uint8_t)(req->wIndex);//增加接口判断usbd_customhid_report_length = (uint8_t)(req->wLength);printf("usbd_customhid_interface_id%d",usbd_customhid_interface_id);if(usbd_customhid_interface_id == 0)usbd_ep_rx (pudev, EP0_OUT, report_buf, usbd_customhid_report_length);elseusbd_ep_rx (pudev, EP0_OUT, report1_buf, usbd_customhid_report_length);break;case SET_IDLE:usbd_customhid_idlestate = (uint8_t)(req->wValue >> 8);break;case SET_PROTOCOL:usbd_customhid_protocol = (uint8_t)(req->wValue);break;default:usbd_enum_error (pudev, req);return USBD_FAIL;}break;case USB_STANDARD_REQ:/* standard device request */switch(req->bRequest) {case USBREQ_GET_DESCRIPTOR:switch(req->wValue >> 8) {case CUSTOMHID_REPORT_DESCTYPE:if(0==req->wIndex)//请求接口1的报告描述符{len = MIN(CUSTOMHID_REPORT_DESC_SIZE, req->wLength);pbuf = (uint8_t *)customhid_report_descriptor;}else{len = MIN(CUSTOMHID1_REPORT_DESC_SIZE, req->wLength);pbuf = (uint8_t *)customhid1_report_descriptor;}break;case CUSTOMHID_DESC_TYPE:if(0==req->wIndex)//请求第一个接口的hid描述符{len = MIN(USB_CUSTOMHID_DESC_SIZE, req->wLength);pbuf = (uint8_t *)(&(configuration_descriptor.HID_VendorHID));}else{len = MIN(USB_CUSTOMHID_DESC_SIZE, req->wLength);pbuf = (uint8_t *)(&(configuration_descriptor.HID1_VendorHID));}break;default:break;}usbd_ep_tx (pudev, EP0_IN, pbuf, len);break;case USBREQ_GET_INTERFACE:usbd_ep_tx (pudev, EP0_IN, (uint8_t *)&usbd_customhid_altset, 1);break;case USBREQ_SET_INTERFACE:usbd_customhid_altset = (uint8_t)(req->wValue);break;}break;}return USBD_OK;}

2.4.4修改数据处理函数,根据ep_id进行不同的处理,可自己添加处理函数

usbd_status_enum custom_hid_data_handler (void *pudev, usbd_dir_enum rx_tx, uint8_t ep_id){if(USBD_TX == rx_tx) {return USBD_OK;} else if ((USBD_RX == rx_tx)) {if( CUSTOMHID_OUT_EP == ep_id){printf("HID%x %x\r\n",report_buf[0],report_buf[1]);usbd_ep_rx(pudev, CUSTOMHID_OUT_EP, report_buf,64);return USBD_OK;}else{printf("HID1%x %x\r\n",report_buf[0],report_buf[1]);usbd_ep_rx(pudev, CUSTOMHID_OUT_EP, report_buf,64);return USBD_OK;}}return USBD_FAIL;}

2.4.5修改发送报告处理函数,根据ep_id进行不同的处理,可自己添加处理函数

uint8_t custom_hid_report_send (usbd_core_handle_struct *pudev, uint8_t *report, uint16_t len){if( CUSTOMHID_OUT_EP == ep_id){usbd_ep_tx (pudev, CUSTOMHID_IN_EP, report, len);return USBD_OK;}if( CUSTOMHID1_OUT_EP == ep_id){usbd_ep_tx (pudev, CUSTOMHID1_IN_EP, report, len);return USBD_OK;}}

编译下载,插入电脑,检测到两个设备,大功告成

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。