62 stm32 usb自定义hid复合设备修改实验

news/2023/12/2 10:05:07

1.引言

        最近因为项目需要,我们希望单片机既能有hid键盘功能,又能有hid设备的功能。即单片机的一个usb接口插入电脑后,电脑能识别出键盘设备和hid设备,两者同时存在的。

        基于项目只是要求实现功能,故本次只是对stm32usb应用方面进行了研究,并未对usb进行更深层次的了解,所以本blog只是记录了,如何基于单片机hid键盘例程,修改成hid键盘+hid设备过程及遇到的问题。

2.目标

        图1单hid设备,在电脑中的显示:

        图2单键盘设备在电脑中的显示:

        图3 hid键盘+hid设备在电脑中的显示:

本次的目标就是,将stm32 usb插入电脑后,电脑能生成设备键盘+hid设备的节点,即图3示。

3.工具介绍

        在修改移植代码中遇到一些问题,或测试数据需要一些工具(下载请自行百度)。

        (1)其中使用bus hound来抓取usb收发的数据内容,通过该工具可以抓到usb设备收发的内容。

        (2)使用hid收发工具,就像串口工具一样,可以测试hid设备的收发

        上述两个工具,网上都可下载到。

4.usb代码修改过程

        在这部分,我们进入具体的移植详情讲解,以及将所遇到的问题进行说明。

步骤1:修改usb_desc.c文件

        因为底层的usb相关协议,原厂以及做好,作为开发应用的我们,暂时只需要关注应用方面。而对于应用方面,我们要第一个要关注的就是usb_desc.c文件。

        当usb插入电脑的时候,usb的host与设备之间就会立即通信,host就会询问usb设备相关信息,入设备类型,支持的协议版本,usb的厂商标识等等。这些内容就在usb_desc.c文件下的设备描述结构体中,用户可根据需要修改这部分的内容。

#define		USB_VID		0x5548
#define		USB_PID		0x6666
#define		DEV_VER		0x0100/* USB Standard Device Descriptor */
const uint8_t CustomHID_DeviceDescriptor[CUSTOMHID_SIZ_DEVICE_DESC] =
{0x12,                       /*bLength */USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/0x00,                       /*bcdUSB */0x02,0x00,                       /*bDeviceClass*/0x00,                       /*bDeviceSubClass*/0x00,                       /*bDeviceProtocol*/0x40,                       /*bMaxPacketSize40*/USB_VID&0xff,					/*idVendor*/USB_VID>>8, 					USB_PID&0xff,					/*idProduct*/USB_PID>>8,DEV_VER&0xff,					/*bcdDevice rel. 1.00*/DEV_VER>>8,1,                          /*Index of string descriptor describingmanufacturer */2,                          /*Index of string descriptor describingproduct*/3,                          /*Index of string descriptor describing thedevice serial number */0x01                        /*bNumConfigurations*/
}; /* CustomHID_DeviceDescriptor */

        上述字段中bMaxPacketSize40字段,是告知usb host本设备能支持传输的最大字节,一般一个标准的hid设备,每次传输的包就是64字节,所以64的十六进制就是0x40。上述结构中,除了关注这个字节数外,还可能常改的就是usb 的VID和PID。

        usb设备描述信息之后便是,usb的设备配置描述结构。该设备配置信息结构向host上报本身详细的配置信息以及设备所支持的能力,原本例程只是hid键盘相关的配置,所以我们需要在此结构添加相关信息。


/* USB Configuration Descriptor */
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
const uint8_t CustomHID_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =
{0x09, /* bLength: Configuration Descriptor size */USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */CUSTOMHID_SIZ_CONFIG_DESC,/* wTotalLength: Bytes returned */0x00,0x02,         /* bNumInterfaces: 1 interface */0x01,         /* bConfigurationValue: Configuration value */0x00,         /* iConfiguration: Index of string descriptor describingthe configuration*/0xC0,         /* bmAttributes: Self powered */0x32,         /* MaxPower 100 mA: this current is used for detecting Vbus *//************** Descriptor of Custom HID interface ****************//* 09 */0x09,         /* bLength: Interface Descriptor size */USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType: Interface descriptor type */0x00,         /* bInterfaceNumber: Number of Interface */0x00,         /* bAlternateSetting: Alternate setting */0x02,         /* bNumEndpoints */0x03,         /* bInterfaceClass: HID */0x01,         /* bInterfaceSubClass : 1=BOOT, 0=no boot */0x01,         /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */0,            /* iInterface: Index of string descriptor *//******************** Descriptor of Custom HID HID ********************//* 18 */0x09,         /* bLength: HID Descriptor size */HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID */0x10,         /* bcdHID: HID Class Spec release number */0x01,0x00,         /* bCountryCode: Hardware target country */0x01,         /* bNumDescriptors: Number of HID class descriptors to follow */0x22,         /* bDescriptorType */sizeof(CustomHID_ReportDescriptor)&0xFF,		  /* wItemLength: Total length of Report descriptor :36 bytes*/(sizeof(CustomHID_ReportDescriptor)>>8)&0xFF,/******************** Descriptor of Custom HID endpoints ******************//* 27 */0x07,          /* bLength: Endpoint Descriptor size */USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */0x81,          /* bEndpointAddress: Endpoint Address (IN) */0x03,          /* bmAttributes: Interrupt endpoint */0x08,          /* wMaxPacketSize: 64 Bytes max */0x00,0x0A,          /* bInterval: Polling Interval (32 ms) *//* 34 */0x07,	/* bLength: Endpoint Descriptor size */USB_ENDPOINT_DESCRIPTOR_TYPE,	/* bDescriptorType: *//*	Endpoint descriptor type */0x01,	/* bEndpointAddress: *//*	Endpoint Address (OUT) */0x03,	/* bmAttributes: Interrupt endpoint */0x08,	/* wMaxPacketSize: 64 Bytes max  */0x00,0x0A,		   /* bInterval: Polling Interval (10 ms) */ //下面是自己添加的hid设备相关描述///************** Descriptor of 2/0 HID interface ****************//* 41 */0x09,		  /* bLength: Interface Descriptor size */USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType: Interface descriptor type */0x01,		  /* bInterfaceNumber: Number of Interface这里改成1 */0x00,		  /* bAlternateSetting: Alternate setting */0x02,		  /* bNumEndpoints这里改成2 */0x03,		  /* bInterfaceClass: HID */0x00,		  /* bInterfaceSubClass : 1=BOOT, 0=no boot */0x00,		  /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */0x00,		  /* iInterface: Index of string descriptor *//******************** Descriptor of 2/0 HID ********************//* 50 */0x09,		  /* bLength: HID Descriptor size */HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID */0x10,		  /*bcdHID: HID Class Spec release number*/0x01,0x00,		  /* bCountryCode: Hardware target country */0x01,		  /* bNumDescriptors: Number of HID class descriptors to follow */0x22,		  /* bDescriptorType */sizeof(HID_driver_ReportDescriptor)&0xFF,		  /* wItemLength: Total length of Report descriptor :36 bytes*/(sizeof(HID_driver_ReportDescriptor)>>8)&0xFF,/******************** Descriptor of 2/0 HID endpoints :3 In, Interrupt, 3 ms******************//* 59 */0x07,		   /* bLength: Endpoint Descriptor size */USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */0x82,		   /* bEndpointAddress: Endpoint Address (IN) */0x03,		   /* bmAttributes: Interrupt endpoint */0x40,		   /* wMaxPacketSize: 10 Bytes max */0x00,0x22,		   /* bInterval: Polling Interval (10 ms) */ /******************** Descriptor of 2/0 HID endpoints :3 Out, Interrupt, 3 ms******************//* 66 */0x07,		   /* bLength: Endpoint Descriptor size */USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */0x02,		   /* bEndpointAddress: Endpoint Address (out) */0x03,		   /* bmAttributes: Interrupt endpoint */0x40,		   /* wMaxPacketSize: 10 Bytes max */0x00,0x0A,		   /* bInterval: Polling Interval (10 ms) */ /* 73 */
///添加结束//
}; /* CustomHID_ConfigDescriptor */

         (1)因为设备usb插入电脑需要被识别成两个设备,所以上述的字段bNumInterfaces,需要改成2。

        (2)根据hid设备相关标准,往结构中添加设备配置描述字段,添加完之后记得修改CUSTOMHID_SIZ_CONFIG_DESC数组大小的宏定义。

        (3)我们添加了新的一个设备接口,而前面的hid键盘是第一个设备,所以我们添加的是第二个设备,因此我们需要将bInterfaceNumber字段,依次累加,第一个设备是0,所以我们这里就是1.

        (4)usb通信的数据收发都是通过端点的,而我们第二个设备使用了两个端点,即一收一发,所以bNumEndpoints 要改成2

        (5)然后我这次增加的设备是无类型的,所以nInterfaceProtocol就是0

        (6)在hid接口配置的信息结构中,需要填上,该设备的描述信息结构大小,即wItemLength应该要调描述信息结构的

        (7)我们增加的hid设备接口,使用的是端口2。端口0是默认的控制交互的端点,端口1是前面hid键盘的通信端点,我们使用的是端点2。

        端点2的输入端点bEndpointAddress字段需要配置成0x82,输出端点需要配置为0x02。原因请看下图详解。

        (8)对于数据的收发,usb中提供了四种方式:中断传输、批量传输、控制传输、 同步传输。对于数据量不大的业务一般使用中断传输就好,所以上述端点bmAttributes字段配置为0x03。更多详解,请看下图。

        (9)我使用的是中断传输数据,但这中断只是usb内部的收发机制吧(具体的没去了解),然后我们收发数据,间隔是通过bInterval字段设置的时间去端点收发数据函数执行代码。故,如果bInterval字段设置间隔太长会影响数据的吞吐量,这个需要看具体需求而定。

        修改完上述的hid接口配置信息后,需要给我们新增的hid设备增加接口的设备描述信息,该描述信息具体的字段文档说明,没找到,我是直接复制现成的。

const uint8_t HID_driver_ReportDescriptor[HID_driver_SIZ_REPORT_DESC] =
{         //vendor0x06, 0x00, 0xFF,  	//Usage Page (Vendor-Defined 29) 0x09, 0x92,  		//Usage (Vendor-Defined 146) 0xA1, 0x01,			//Collection (Application)   0x19, 0x00,  		//Usage Minimum (Undefined) 0x2A, 0xFF, 0x00,	//Usage Maximum (Vendor-Defined 255)   0x15, 0x00,			//Logical Minimum (0)   0x26, 0xFF, 0x00,	//Logical Maximum (255)   0x75, 0x08,			//Report Size (8)   0x95, 0x40, 		//Report Count (63)  0x91, 0x00, 		//Output (Data,Ary,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)  0x19, 0x00,  		//Usage Minimum (Undefined) 0x2a, 0xFF, 0x00,	//Usage Maximum (Vendor-Defined 255)   0x81, 0x00,  		//Input (Data,Ary,Abs) 0xC0, 				//End Collection 
}; 

         记得按照实际定义HID_driver_SIZ_REPORT_DESC宏的大小。

步骤2:修改usb_desc.h文件

        该步骤实为对步骤1所修改的,在头文件修改宏,或增加定义。

//在头文件修改CUSTOMHID_SIZ_CONFIG_DESC宏定义大小
//自己去算算CustomHID_ConfigDescriptor结构大小就知道是73
#define CUSTOMHID_SIZ_CONFIG_DESC               73 //在头文件新增
#define HID_driver_SIZ_REPORT_DESC              31#define HID_driver_OFF_HID_DESC					50extern const uint8_t HID_driver_ReportDescriptor[HID_driver_SIZ_REPORT_DESC];

 步骤3:修改usb_endp.c文件

        因为我们新增了一个设备接口,使用端点2进行数据传输,所以我们要在usb_endp.c仿制端点1的数据收发,实现端点2函数。


/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*//*** @brief  EP1 OUT Callback Routine.*/
void EP1_OUT_Callback(void)
{/* Enable the receive of data on EP1 */SetEPRxStatus(ENDP1, EP_RX_VALID);
}/*** @brief  EP1 IN Callback Routine.*/
void EP1_IN_Callback(void)
{}void EP2_OUT_Callback(void)
{	USER_USB_HIDEP2_RX_LEN = USB_GetEpRxCnt(ENDP2);USB_CopyPMAToUserBuf((unsigned char*)USER_USB_HIDEP2_RX_Buf, ENDP2_RXADDR, USER_USB_HIDEP2_RX_LEN);	HtClient_RcvBufDataIn(MODE_HID2, USER_USB_HIDEP2_RX_Buf, USER_USB_HIDEP2_RX_LEN);/* Enable the receive of data on EP2 */SetEPRxStatus(ENDP2, EP_RX_VALID);
}void EP2_IN_Callback(void)
{}

 步骤4:修改usb_conf.h文件

        修改完usb_endp.c文件,我们需要屏蔽usb_conf.h文件中对EP2_IN_CallbackEP2_OUT_Callback函数的定义。

        另外,我们需要定义usb收发数据的buf地址。因为usb的工作机制是,数据来了先缓存到usb接收缓存buf,然后按照到了usb数据轮询查看时间,发现有数据后,通过回调EPx_OUT_Callback函数,将数据从该buf拷贝出来,也就是说EPx_OUT_Callback就是usb接收数据的地方。

        然后usb有对应的buf用以存放接收的数据,但是需要我们手动设置端点存放数据的起始地址,又因为hid固定是发64Byte一帧数据,所以收发地址依次递增64Byte就行。

         以下就是我修改后的usb_conf.h文件。

#ifndef __USB_CONF_H__
#define __USB_CONF_H__/*-------------------------------------------------------------*/
/* EP_NUM */
/* defines how many endpoints are used by the device */
/*-------------------------------------------------------------*/
#define EP_NUM 				(3) //这里要修改为3 端点0+端点1+端点2 =3个端点/*-------------------------------------------------------------*/
/* --------------   Buffer Description Table  -----------------*/
/*-------------------------------------------------------------*/
/* buffer table base address */
/* buffer table base address */
#define BTABLE_ADDRESS 		(0x00)/* EP0  */
/* rx/tx buffer base address */
#define ENDP0_RXADDR        (0x18)//这个起始地址好像可以随意?具体没深入研究
#define ENDP0_TXADDR        (0x58)//依次递增64Byte就系,也就是0x40/* EP1  */
/* tx buffer base address */
#define ENDP1_TXADDR        (0x98)
#define ENDP1_RXADDR        (0xD8)/* EP2  */
/* tx buffer base address */
#define ENDP2_TXADDR        (0x118) //ENDP2_TXADDR+0x40开始
#define ENDP2_RXADDR        (0x158)/*-------------------------------------------------------------*/
/* -------------------   STS events  -------------------------*/
/*-------------------------------------------------------------*/
/* IMR_MSK */
/* mask defining which events has to be handled */
/* by the device application software *///#define IMR_MSK (CTRL_CTRSM  | CTRL_WKUPM | CTRL_SUSPDM | CTRL_ERRORM  | CTRL_SOFM \
//                 | CTRL_ESOFM | CTRL_RSTM )
#define IMR_MSK (CTRL_CTRSM | CTRL_RSTM)
/* CTR service routines */
/* associated to defined endpoints */
//#define EP1_IN_Callback USB_ProcessNop
//#define EP2_IN_Callback USB_ProcessNop //我注释的
#define EP3_IN_Callback USB_ProcessNop
#define EP4_IN_Callback USB_ProcessNop
#define EP5_IN_Callback USB_ProcessNop
#define EP6_IN_Callback USB_ProcessNop
#define EP7_IN_Callback USB_ProcessNop//#define  EP1_OUT_Callback   USB_ProcessNop
//#define EP2_OUT_Callback USB_ProcessNop //我注释的
#define EP3_OUT_Callback USB_ProcessNop
#define EP4_OUT_Callback USB_ProcessNop
#define EP5_OUT_Callback USB_ProcessNop
#define EP6_OUT_Callback USB_ProcessNop
#define EP7_OUT_Callback USB_ProcessNop#endif /*__USB_CONF_H__*/

 步骤5:修改usb_prop.c文件

        (1)我们在usb_des.c增加了新的hid接口以及相关的接口配置信息和设备描述信息,这些信息都将会在usb_prop.c调用到。仿制原有的hid键盘设备的代码,在usb_prop.c文件中,我们新增了两个变量。

//hid设备描述信息
USB_OneDescriptor HID_driver_Report_Descriptor = {(uint8_t*)HID_driver_ReportDescriptor, HID_driver_SIZ_REPORT_DESC};//hid设备接口描述信息
USB_OneDescriptor HID_driver_Hid_Descriptor = {(uint8_t*)CustomHID_ConfigDescriptor + HID_driver_OFF_HID_DESC, CUSTOMHID_SIZ_HID_DESC};

        然后增加获取hid设备接口信息和设备描述信息的函数。

uint8_t *HID_driver_GetReportDescriptor(uint16_t Length)
{return Standard_GetDescriptorData(Length, &HID_driver_Report_Descriptor);
}uint8_t *HID_driver_GetHIDDescriptor(uint16_t Length)
{return Standard_GetDescriptorData(Length, &HID_driver_Hid_Descriptor);
}

        (2)修改获取设备信息函数。下面通过pInformation->USBwIndex0来区分获取不同的设备信息,而pInformation->USBwIndex0跟我们在usb_desc.c下的CustomHID_ConfigDescriptor结构中的bInterfaceNumber有关。一般bInterfaceNumber从0开始,所以下面的pInformation->USBwIndex0是从0-1遍历。

/*** @brief  Handle the data class specific requests.* @param Request Nb.*/
USB_Result CustomHID_Data_Setup(uint8_t RequestNo)
{uint8_t* (*CopyRoutine)(uint16_t);// if (pInformation->USBwIndex != 0)//return UnSupport;CopyRoutine = NULL;if ((RequestNo == GET_DESCRIPTOR) && (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))&& (pInformation->USBwIndex0 < 3)){if (pInformation->USBwValue1 == REPORT_DESCRIPTOR){//原来的hid键盘设备if (pInformation->USBwIndex0 == 0){CopyRoutine = CustomHID_GetReportDescriptor;}//我们新增的hid设备,为什么是1.是我们在hid接口描述bInterfaceNumber中写的是1else if (pInformation->USBwIndex0 == 1){CopyRoutine = HID_driver_GetReportDescriptor;}}else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE){//原来的hid键盘设备if (pInformation->USBwIndex0 == 0){CopyRoutine = CustomHID_GetHIDDescriptor;}//我们新增的hid设备,为什么是1.是我们在hid接口描述bInterfaceNumber中写的是1else if (pInformation->USBwIndex0 == 1){CopyRoutine = HID_driver_GetHIDDescriptor;}}} /* End of GET_DESCRIPTOR *//*** GET_PROTOCOL, GET_REPORT, SET_REPORT ***/else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))){switch (RequestNo){case GET_PROTOCOL:CopyRoutine = CustomHID_GetProtocolValue;break;case SET_REPORT:CopyRoutine = CustomHID_SetReport_Feature;Request     = SET_REPORT;break;default:break;}}if (CopyRoutine == NULL){return UnSupport;}pInformation->Ctrl_Info.CopyData    = CopyRoutine;pInformation->Ctrl_Info.Usb_wOffset = 0;(*CopyRoutine)(0);return Success;
}

        (3)修改usb的CustomHID_Reset函数。注意USB_SetEpTxCnt函数,一定要设为0,否则usb会定时上报上次发送的数据,这是一个小坑。

/*** @brief  Custom HID reset routine.*/	
void CustomHID_Reset(void)
{/* Set CustomHID_DEVICE as not configured */pInformation->CurrentConfiguration = 0;pInformation->CurrentInterface     = 0; /*the default Interface*//* Current Feature initialization */pInformation->CurrentFeature = CustomHID_ConfigDescriptor[7];USB_SetBuftab(BTABLE_ADDRESS);/* Initialize Endpoint 0 */USB_SetEpType(ENDP0, EP_CONTROL);SetEPTxStatus(ENDP0, EP_TX_STALL);USB_SetEpRxAddr(ENDP0, ENDP0_RXADDR);USB_SetEpTxAddr(ENDP0, ENDP0_TXADDR);USB_ClrStsOut(ENDP0);USB_SetEpRxCnt(ENDP0, Device_Property.MaxPacketSize);USB_SetEpRxValid(ENDP0);/* Initialize Endpoint 1 */USB_SetEpType(ENDP1, EP_INTERRUPT);USB_SetEpTxAddr(ENDP1, ENDP1_TXADDR);USB_SetEpRxAddr(ENDP1, ENDP1_RXADDR);USB_SetEpTxCnt(ENDP1, 0);//这里设置需要设置为0,不然usb会自己一直发上次发送的数据USB_SetEpRxCnt(ENDP1, HIDEP1_REPORT_COUNT);SetEPRxStatus(ENDP1, EP_RX_VALID);SetEPRxStatus(ENDP1, EP_TX_VALID);/* Initialize Endpoint 2 */USB_SetEpType(ENDP2, EP_INTERRUPT);USB_SetEpTxAddr(ENDP2, ENDP2_TXADDR);	USB_SetEpRxAddr(ENDP2, ENDP2_RXADDR);USB_SetEpTxCnt(ENDP2, 0);//这里设置需要设置为0,不然usb会自己一直发上次发送的数据USB_SetEpRxCnt(ENDP2, HIDEP2_REPORT_COUNT);SetEPTxStatus(ENDP2, EP_TX_VALID);SetEPRxStatus(ENDP2, EP_RX_VALID);/* Set this device to response on default address */USB_SetDeviceAddress(0);bDeviceState = ATTACHED;
}

 步骤6:修改usb_prop.h文件

        在头文件添加一下函数的声明就好了。

//添加上函数的声明
uint8_t *HID_driver_GetReportDescriptor(uint16_t Length);
uint8_t *HID_driver_GetHIDDescriptor(uint16_t Length);

  步骤7:修改hid数据发送函数(可选)

        hid的数据发送,可以通过端点的发送函数,发送,也可以自己调用相关接口发送,以下是分享我的发送函数格式。

#define HIDEP1_REPORT_COUNT						(8)
#define HIDEP2_REPORT_COUNT						(64)uint8_t USER_USB_HIDEP1_TX_LEN = 0;
uint8_t USER_USB_HIDEP1_RX_LEN = 0;
uint8_t USER_USB_HIDEP1_TX_Buf[HIDEP1_REPORT_COUNT] ={0};
uint8_t USER_USB_HIDEP1_RX_Buf[HIDEP1_REPORT_COUNT] ={0};uint8_t USER_USB_HIDEP2_TX_LEN = 0;
uint8_t USER_USB_HIDEP2_RX_LEN = 0;
uint8_t USER_USB_HIDEP2_TX_Buf[HIDEP2_REPORT_COUNT] ={0};
uint8_t USER_USB_HIDEP2_RX_Buf[HIDEP2_REPORT_COUNT] ={0};int USBHID1_SendData(uint8_t *data, int dataSize)		 
{uint16_t delayMsCnt = 0;uint16_t tempSize = 0;uint16_t sendSize = 0;if(bDeviceState != CONFIGURED)return -1;if(dataSize == 0)return -2;do{memset(USER_USB_HIDEP1_TX_Buf, 0, HIDEP1_REPORT_COUNT);if(dataSize>HIDEP1_REPORT_COUNT)tempSize = HIDEP1_REPORT_COUNT;elsetempSize = dataSize;memcpy(USER_USB_HIDEP1_TX_Buf, (uint8_t*)(data + sendSize), tempSize);USER_USB_HIDEP1_TX_LEN = tempSize;dataSize -= tempSize;sendSize += tempSize;USB_SilWrite(ENDP1, USER_USB_HIDEP1_TX_Buf, HIDEP1_REPORT_COUNT); USB_SetEpTxValid(ENDP1);}while(dataSize>0);//数据发完之后清零delay_ms(20);memset(USER_USB_HIDEP1_TX_Buf, 0, HIDEP1_REPORT_COUNT);USB_SilWrite(ENDP1, USER_USB_HIDEP1_TX_Buf, HIDEP1_REPORT_COUNT); USB_SetEpTxValid(ENDP1);return sendSize;  
}int USBHID2_SendData(uint8_t *data, int dataSize)		 
{uint16_t delayMsCnt = 0;uint16_t tempSize = 0;uint16_t sendSize = 0;if(bDeviceState != CONFIGURED)return -1;if(dataSize == 0)return -2;do{			memset(USER_USB_HIDEP2_TX_Buf, 0, HIDEP2_REPORT_COUNT);if(dataSize>HIDEP2_REPORT_COUNT)tempSize = HIDEP2_REPORT_COUNT;elsetempSize = dataSize;memcpy(USER_USB_HIDEP2_TX_Buf, (uint8_t*)(data + sendSize), tempSize);USER_USB_HIDEP2_TX_LEN = tempSize;dataSize -= tempSize;sendSize += tempSize;USB_SilWrite(ENDP2, USER_USB_HIDEP2_TX_Buf, HIDEP2_REPORT_COUNT); USB_SetEpTxValid(ENDP2);}while(dataSize>0);return sendSize;  
}

        修改到此结束。 

5 结束

        好了,经过上述的修改,基本上就可以完成实验的目标,一个usb接口模拟出多个usb设备。如下图示:


http://www.ppmy.cn/news/131460.html

相关文章

week5

systemctl get-default systemctl set-default graphical.target 由命令行模式更改为图形界面模式systemctl set-default multi-user.target 由图形界面模式更改为命令行模式 systemctl restart network.service&#xff1a;重启网卡 ascii vim /etc/sysconfig/network-script…

<5>第五期

七、AVA反射 1.-反射的基本概念 一般情况下&#xff0c;我们知道一个类&#xff0c;那可以通过这个类创建对象&#xff1b; 但是如果要求通过一个对象找到一个类&#xff0c;这时候反射就派上用场了。 java反射实现的核心就是Class类 java.lang包下的。 2.java反射-java C…

# Chapter 5

Chapter 5 Gradient Temporal-Difference Learning with Linear Function Approximation 本章提供了线性函数近似情况下梯度-TD算法的核心思想和理论结果。在这里&#xff0c;我们在Baird(1995&#xff1b;1999)的工作基础上&#xff0c;探讨了用于线性函数逼近的时差学习的真…

【C++】课节笔记及梳理总结---EP5

一、课节笔记 第四章 函数与预处理 Ⅰ、概述 1.模块化程序设计   (1)基本思想&#xff1a;将一个大程序按照功能分割成若干个小模块   (2)开发方法&#xff1a;自上而下&#xff0c;逐步分解&#xff0c;分而治之  2.※ C 是模块化程序设计语言 ※ Ⅱ、定义函数的一般形式…

USB Composite 组合设备之多路CDC实现

USB Composite 组合设备之多路CDC实现 USB复合设备与组合设备区别效果展示修改相关配置修改配置项修改设备描述符修改配置、接口、端点描述符接口修改FIFO配置 知识点FIFO分配 注意事项 USB复合设备与组合设备区别 其实多个接口组合在一起有2种情况 第一种叫做USB复合设备&…

Ep5 线性模型with Pytorch

1、流程 确定数据集、 设计模型(算出预测值)、 构建损失函数&#xff08;最终为一个标量值&#xff0c;只有标量才能用backward&#xff09;和优化器、 训练周期&#xff08;forward算loss&#xff0c;backward算grad&#xff0c;update更新wi&#xff09; 2、numpy的广播…

HBase Shell 常用命令练习

HBase Shell 常用命令练习 前言一、HBase Shell是什么&#xff1f;二、HBase Shell使用步骤1.启动HBase2.启用HBase Shell3.键入HBase Shell命令操作HBase 三、常用HBase Shell实例1.常用的HBase Shell命令2.一个运用上述命令的综合实例&#xff1a; 总结 前言 提示&#xff1…

侃侃算法EP5·二叉树及其遍历

1. 前言 这个板块旨在记录一些日常中或是面试中会问到的算法和数据结构相关的内容&#xff0c;更多是给自己总结和需要的人分享。在内容部分可能由于我的阅历和实战经历不足&#xff0c;会有忽视或是写错的点&#xff0c;还望轻喷。 2. 内容 关于什么是树、子树、根节点、叶…

ES6、ES7、ES8、ES9、ES10新特性及其兼容性

强烈推荐阅读一篇文章&#xff0c;也是自己为了做保存把地址贴到自己博客&#xff0c;大家一起学习&#xff1a; ECMAScript 6 入门教程——阮一峰 盘点ES7、ES8、ES9、ES10新特性

es_01

字段&#xff1a;等于一个属性 文档&#xff1a;行数据等于多个字段组成 映射&#xff1a;mapping表结构 索引&#xff1a;index 数据库 存文档 类型&#xff1a;忽略 正排索引&#xff1a; 需要按照key来搜索每个key下的value&#xff0c;要收到全部的数据&#xff0c;就要进…

es(八)

单字符串串多字段查询:Dis Max Query 想在百度搜索一个单字符 should是如何算分过程 查询 should 语句句中的两个查询加和两个查询的评分乘以匹配语句句的总数除以所有语句句的总数

JS高级+ES678

js高级 数据类型 基本(值)类型 Number: 任意数值String: 任意文本Boolean: true/falseundefined: undefinednull: null 对象(引用)类型 Object: 任意对象 主要用来包含无序复杂的数据Array: 特别的对象类型(下标/内部数据有序)Function: 特别的对象类型(可执行)&#xff0c;F…

阿里云ECS部署ES

背景 最近越来越多的公司把业务搬迁到云上&#xff0c;公司也有这个计划&#xff0c;自己抽时间在阿里云和Azure上做了一些小的尝试&#xff0c;现在把阿里云上部署ES和kibana记录下来。为以后做一个参考&#xff0c;也希望对其他人有帮助。 这里以阿里云为例&#xff0c;由于测…

ES-08-ElasticSearch数据分片(shard)

说明 ElasticSearch数据分片&#xff08;shard&#xff09;创建多分片索引、更改多分片索引副本分片数量、路由计算和分片控制官方文档&#xff1a;https://www.elastic.co/cn/ 核心概念 》什么是数据分片&#xff08;shard&#xff09;&#xff1f; 一个分片是一个底层的工…

ES-09-ElasticSearch分词器

说明 ElasticSearch分词器默认分词器&#xff08;标准分词器&#xff09;、ik分词器、ik分词器扩展字典自定义词语关键词&#xff1a;keyword、text、ik_max_word、ik_smart、词条、词典、倒排表官方文档&#xff1a;https://www.elastic.co/cn/ik分词器文档&#xff1a;https…

Elasticsearch8.0

Elastic 中国社区官方博客_CSDN博客-Elastic,Elasticsearch,Kibana领域博主Elastic 中国社区官方博客擅长Elastic,Elasticsearch,Kibana,等方面的知识https://elasticstack.blog.csdn.net/ ✅ 启动 elasticsearch # cd /usr/local/elastic/elasticsearch/ # ./bin/elasticsearc…

ES6/ES7/ES8/ES9/ES10

ES10 ES10 功能完全指南 好犯困啊 我来打打字 string.prototype.matchAll() ‘Hello’.match(‘l’) eg:判断字符串中存在几个某元素 yannanna’.match(/n/g).length 扁平化多维数组&#xff08;想不出啥时候会用到&#xff09; let array [1, 2, 3, 4, 5]; array.map(x &g…

ES

文章目录 1. 什么是ElasticSearch&#xff1f;为什么要使用Elasticsearch?——克服模糊查询的缺点、查询速度快2. ES中的倒排索引是什么&#xff1f;——词→文章3. ES是如何实现master选举的&#xff1f;——各节点分别排序投票4. 如何解决ES集群的脑裂问题——增大最少候选节…

es 客户端

ES客户端&#xff1a;Elasticsearch Clients 语言无关性 Java REST ClientJava APIPython APIGo API.Net APIPHP APIJavaScripts APIRuby APIPerl APIElandRustCommunity Contributed Clients Java API 生命周期&#xff08;生卒年&#xff1a;ES 0.9 - ES 7.x&#xff09;…

ES7,ES8,ES10新特性

ES7 ES7在ES6的基础上增加了三项内容 求幂运算符 ** console.log(3 ** 2 ) // 9 Array.prototype.includes()方法 includes()的作用是查找一个值在不在数组中&#xff0c;接受两个参数&#xff1a;搜索值和搜索的开始索引。如果没有传递参数默认的索引是0 // 下面的这两种方…
最新文章