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

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

本文介绍如何让STM32单片机通过一个USB接口模拟出键盘和HID设备两个独立设备的方法。通过详细步骤指导如何修改USB描述符、配置端点以及实现数据收发等关键环节。

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 describing
																							manufacturer */
		2,                          /*Index of string descriptor describing
																						 product*/
		3,                          /*Index of string descriptor describing the
																						 device 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 describing
																 the 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] =
{         
	//vendor
	0x06, 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					50

extern 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_Callback和EP2_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中写的是1
			else 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中写的是1
			else 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;
		else
			tempSize = 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;
		else
			tempSize = 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设备。如下图示:

STM32F429阿波罗用Cubemx建一个USBHID工程,实现数据收发 最近想做一个USBHID数据收发,查找了很多文章,其中两篇文章作者写的很详细,值得参考 https://www.cnblogs.com/xingboy/p/9913963.html https://blog.csdn.net/guanlizhongxintishi/article/details/87882207 1.配置时钟树 选择外部时钟源 外部晶振为25M,设置M,N,P,Q,使USB时钟必须是48M 2.设置USB 中断会自动勾选,不用管 配置USB,选择Custom Human Interf 阅读详情

相关推荐

STM32 USB HID 复合设备开发:1个USB接口同时模拟键盘与鼠标

本文详细介绍了基于STM32的USB HID复合设备开发,通过单USB接口同时模拟键盘与鼠标功能。内容涵盖接口描述符配置、报告描述符设计、数据发送机制及调试技巧,提供完整的开发流程和实战案例,帮助开发者快速实现免驱人机交互设备。

weixin_34405557的博客 322

《Linux总线、设备与驱动》USBHID设备驱动

转载: http://blog.csdn.net/tankai19880619/article/details/17447791 说明:本分析基于mstar801平台Linux2.6.35.11内核,其他内核版本仅供参考。 一、HID虚拟总线驱动加载 drivers/hid/hid-core.c [cpp] view plaincopy

老孙的android6探索之旅 1008

STM32F1自定义HID实战:寄存器级USB协议栈开发

HID(Human Interface Device)是USB协议中最基础且广泛应用的设备类,其核心在于报告描述符定义、主机-设备状态协同与实时数据传输机制。理解HID不仅需掌握Descriptor语法规范,更需深入USB控制传输流程、端点缓冲管理及中断时序约束。在资源受限平台如STM32F1系列上,因缺乏专用DMA、EP0缓冲仅64字节、中断延迟敏感等硬件限制,标准HAL库易引发枚举失败、报告错位或蓝屏等问题。本文聚焦寄存器级开发范式,结合USB协议栈分层架构、HID报告描述符语义验证、Windows注册

weixin_34367257的博客 184

USB-HID 设备扫盲

这是一篇关于USB协议的分析,重点分析HID(human interface device)类设备。本文档是在开发 CH567 HID 设备之后成文。USB速度有4种,分为低速(Low-Speed)、全速(Full-Speed)、高速(Hi-Speed)和超高速(SuperSpeed)。其中的总线速度依次大约是1.5Mb/s, 12Mb/s和480Mb/s。这并不是指实际的数据传输速率。USB的HOST,其D+与D-会有15K的电阻下拉,在没有设备的时候会是低电平。低速设备D-有1.5k电阻上拉。全速设备D

qq_29832469的博客 4605

ESP32-P4 USB Host鼠标实战:从协议解析到工业级低延迟

USB Host是嵌入式系统实现外设主动控制的关键能力,其本质是在MCU端完整运行USB协议栈,完成设备枚举、描述符解析、中断传输调度等全过程。ESP32-P4凭借原生USB 2.0 OTG PHY与专用Host DMA引擎,成为少数支持真正USB Host模式的低成本MCU;结合ESP-IDF v5.3+的usb_host组件,可稳定驱动标准HID鼠标等低速设备。技术价值体现在免驱兼容性、亚10ms实时响应及资源受限下的协议栈可靠性,广泛适用于USB传感器接入、工业HID设备桥接与嵌入式人机交互终端。本文

weixin_34380948的博客 297

USB/HID设备+Qt数据收发

用Qt写上位机时,需要用摇杆来控制伺服等设备,大部分使用虚拟摇杆在软件上控制,有时候也需要实体摇杆映射数据来控制伺服运动。开发环境:Qt5.14.2+MSVC2017 Windows环境。

m0_58635225的博客 1693

STM32虚拟U盘实战:USB MSC+FATFS+W25Q64实现即插即用存储

在嵌入式产品开发中,设备数据导出与参数配置常受限于串口协议复杂、上位机维护成本高等问题。借助USB Mass Storage类设备协议,可以将微控制器模拟成标准U盘,实现即插即用的文件交换体验。其核心思路是以SPI NOR Flash作为存储介质,通过文件系统层对其读写进行抽象管理,再经USB MSC底层接口与上位机通信。本文从存储介质选型、文件系统移植、磁盘抽象层适配到USB描述符配置,梳理了基于STM32F103实现虚拟U盘的完整技术链路,重点剖析了W25Q64在512字节扇区与4KB擦除块间的读改写策

weixin_34185320的博客 360

HID数据收发工具 V1.3.4

->本工具编写语言为C#,IDE是VS2013; ->如果无法运行,请检查系统是否已经安装.net framework 4.0(或以上版本); ->本工具软件绿色小巧,无毒无害; ------------------------版本更新信息----------------------------- V1.3.4 - 2020年11月14日 1.新增 记录窗口大小和位置功能; 记录位置:注册表 计算机\HKEY_CURRENT_USER\SOFTWARE\HIDtool V1.3.3 - 2019年8月25日 1.设置--数据收发--接收:加入仅显示一帧前N字节的选项,仅“十六进制显示”有效,设置小于64字节(有数据被隐藏)时,显示省略号提示; 2.记录上次连接的HID设备,下次开启软件不用再进入HID选择界面;且可以直接点击“发送”按钮以发送一次数据; 3.设置--数据显示 改为 --显示; 4.初次运行会清空以前的设置。

MicroPython `pyb` 模块完全指南:pyboard 板级时间、复位、中断、电源与 USB 控制

`pyb` 是 MicroPython 中专门面向 pyboard(以及 STM32 系列开发板)的板级模块,提供与开发板硬件直接相关的函数与类:毫秒/微秒级延时与计时、硬复位与 bootloader 触发、中断屏蔽(IRQ 临界区)、CPU 主频与总线时钟配置、低功耗睡眠(stop/standby),以及 USB 模式(VCP/MSC/HID)配置、硬件随机数、唯一 ID 等能力。读完本文,你将

gitblog_00701的博客 518

自动驾驶仿真工具链集成:ECU TEST与VTD、VERISTAND的深度配置指南

本文详细介绍了自动驾驶仿真测试中ECU TEST、VTD与VERISTAND三大核心工具的深度集成与连接配置方法。通过解析工具链的角色分工,提供从单机到分布式部署的实战指南,并重点阐述了Test Bench Configuration与Test Configuration的关键参数设置、SSH免密登录配置,以及在线与离线工作模式的切换技巧,旨在帮助测试工程师高效构建稳定、自动化的硬件在环测试环境。

wind6的博客 790

零基础学simulink仿真建模--基于Simulink进行农业机械用开关磁阻电机的仿真建模

特点结构简单:无电刷、换向器等易损件,维护成本低。坚固耐用:适合恶劣的工作环境,如农业机械作业现场。宽调速范围:通过适当的控制策略可以实现较宽的速度调节范围。挑战非线性特性:SRM的电磁特性和转矩特性具有高度非线性。复杂性:涉及电气、机械动力学以及控制系统的设计与集成。实时性要求:需要快速响应以适应不断变化的操作条件。多变量优化:包括电流控制、速度控制等多个方面需综合考虑。使用Simscape Electrical库来构建一个开关磁阻电机模型。

amy_mhd的博客 909

嵌入式软硬不分家:STM32+FreeRTOS全栈能力构建指南

嵌入式系统本质是软硬件深度耦合的有机体,其核心在于将物理信号可靠转化为可调度、可验证的软件服务。理解ARM Cortex-M架构的寄存器级控制、FreeRTOS任务调度与中断机制、时序约束与电源完整性设计,是突破学习瓶颈的关键。技术价值体现在解决真实耦合故障——如SysTick时钟树配置错误导致延时失准、DMA缓冲区与RTOS内存管理冲突、GPIO初始化顺序引发硬件锁定等。典型应用场景覆盖工业控制、智能硬件及汽车电子,尤其在STM32裸机到ARM-Linux跨栈开发中,需同步掌握原理图解读、示波器波形分析、

weixin_34160277的博客 400

使用PaddleOCR识别查验平台验证码

PaddleOCR识别国税查验平台验证码

m0_63932300的博客 1269

AnyPS5:PS5外设协议模拟与硬件能力再组织实践指南

PS5作为基于AMD Oberon APU的嵌入式Linux系统,其USB-C接口、M.2 PCIe 4.0插槽和HDMI 2.1通道在出厂时即开放了丰富的底层访问能力。通过合规的外设协议模拟(如USB Device Mode枚举、NVMe Admin Command拦截)和硬件资源重映射(如EDID/SCDC帧注入、SSD健康度虚拟化),用户可在不越狱、不刷写固件、不破坏保修的前提下,拓展区域适配、存储扩容、调试接入与影音定制等场景。这类实践并非黑产工具,而是依托Linux USB gadget框架、标准

weixin_29164497的博客 127

手把手拆解DGX GB200 NVL72:1个机柜如何塞进72块GPU?详解NVLink Switch铜缆背板设计

本文深度解析英伟达DGX GB200 NVL72系统如何通过NVLink Switch铜缆背板设计,在标准42U机柜中集成72块B200 GPU,实现1800GB/s的超高带宽。文章详细介绍了模块化架构、铜缆互连技术、液冷散热方案和高密度供电系统等核心创新,为AI算力需求提供突破性解决方案。

red88的博客 1585

【Unity 反向运动学插件】Final IK 一套完整的IK解决方案

Final IK是Unity中一套完整的反向运动学解决方案,提供多种IK算法(CCD、FABRIK等)和功能模块,包括全身IK、VR驱动、武器瞄准、视线跟随等。其核心是通过数学方法计算关节旋转,使末端骨骼精确到达目标位置。系统支持多约束求解、旋转限制、动画烘焙及交互系统,采用模块化设计,兼顾性能与扩展性,适用于动作游戏、VR等需要高质量角色控制的场景,是Unity生态中最成熟的IK工具链之一。

2403_88403568的博客 1004

使用CubeMX 生成 USB从机 Custom HID 工程(STM32F407ZE )

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 使用CubeMX 生成 USB从机 HID 自定义设备工程(STM32F407ZE )前言一、目标二、步骤1.创建cubemx工程,2.选择USB OTG配置3.配置RCC4.配置USB DEVICE5.检查管脚6.配置时钟7.生成工程并试验8.添加自定义代码9.USB枚举和收发测试总结 前言 stm32提供的cubemx可谓是开发人员一大福音,特别是对于需要快速完成原型开发的项目来说。 提示:在阅读本篇文章正文内容之前,希望读者对以下

u014285530的博客 4909

YOLOv11 改进 - C2PSA | C2PSA融合TSSA(Token Statistics Self-Attention)令牌统计自注意力,优化遮挡目标感知

本文介绍了Token Statistics Self-Attention(TSSA)机制,并将其集成到YOLOv11中。传统自注意力计算复杂度高,TSSA进行了范式转变,基于token统计特征实现高效注意力交互。它通过“算法展开”推导得出,以“最大编码率降低”为目标,实现特征学习。TSSA包含动态分组和低秩投影优化两步创新,具备线性复杂度。我们将TSSA代码集成到YOLOv11的C2PSA模块中。实验表明,改进后的YOLOv11在目标检测任务中表现良好,验证了TSSA机制的有效性。

魔改工程师的博客 669

STM32进行USB HID 改 BULK小记

STM32进行USB HID 改 BULK小记 chenhaogxfc2019-07-18 12:31:202753已收藏10 分类专栏:嵌入式文章标签:USBSTM32STM32CubeMX 版权 本博客参考了以下同仁的精彩文章,在此表示感谢。 http://www.stm32cube.com/article/178 https://blog.csdn.net/chengwenyang/article/details/51039741 本文纯工程实践,无原理协议分析...

qq_20312079的博客 2709

15、分布式查询处理详解

本博文深入探讨了分布式环境下的查询处理与优化技术。从查询处理的基本概念出发,分析了在分布式系统中执行查询所面临的额外挑战,并通过实例比较了不同的执行策略。文章详细介绍了查询优化的组成要素,包括搜索空间、成本模型和搜索策略,同时涵盖了查询处理的多个层次以及数据本地化的核心方法。此外,还讨论了连接排序优化、分布式成本模型以及多种查询优化方法,如静态优化、动态优化和混合优化。最后,提出了自适应查询处理的概念,旨在提高分布式查询的灵活性和效率。通过综合应用这些技术,可以有效提升分布式数据库的查询性能,降低通信和计算

gan8painter的博客 73

人工智能算法面试大总结-总目录

涉及秋招、春招、社招

啥都生的博客 2万+

【USB设备设计】--复合设备,双HID高速(64Byte 和 1024Byte)

usb 复合设备开发:双HID高速设备

weixin_38426553的博客 2240

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

分享一个超强的HID调试助手

HID调试助手、网络调试助手介绍

drinow的博客 1万+

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

STM32实现自定义HID复合设备

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

awsawzj的博客 4343

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

推荐一个好用的HID调试工具

HIDAssist是一款实用的HID设备调试工具,支持复合设备识别和三种传输方式(中断/控制/特征报告)。其特色功能包括:通过VID/PID快速搜索设备、开放式的关键词高亮自定义、多字符串循环发送(支持延时和重复设置)、内置测试文件生成工具。界面友好,操作直观,能满足HID调试的基本需求,适合开发人员使用。

2301_80174350的博客 7514
上一篇: 61 stm32 USB键盘一直在发送数据
下一篇: 63 USB复合设备多设备数据乱码问题及ENDP0_RXADDR起始地址计算方式
Chasing_Chasing
博客等级 码龄10年 233粉丝 87原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值