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

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_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中写的是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设备。如下图示:

  • 7
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
STM32F070F6是一款STMicroelectronics的单片机芯片,支持USB自定义HID功能。 USB自定义HID(Human Interface Device)是一种USB设备类别,可以用于实现与计算机主机的交互功能。在STM32F070F6中,可以通过使用USB接口和相应的软件库来实现USB自定义HID功能。 使用USB自定义HID功能,可以将STM32F070F6作为一个外部设备连接到计算机上,并且可以通过编程来实现与计算机的双向通信。通过定义自己的报告描述符,可以实现在计算机上模拟各种输入设备,如鼠标、键盘、游戏手柄等。同时,STM32F070F6还可以接收来自计算机的命令和数据,并进行相应的操作。 在编程方面,可以使用STM32Cube软件包来快速开发应用程序。该软件包提供了针对STM32系列单片机的USB库函数,可以方便地实现USB自定义HID功能。通过配置引脚、中断和USB相关寄存器,可以初始化USB接口并设置报告描述符。然后可以利用库函数来发送和接收数据。 需要注意的是,在使用USB自定义HID功能时,需要考虑USB协议的遵循和报告描述符的定义。同时,还需要了解USB传输的相关知识,例如端点(Endpoint)、传输类型等。这些知识可以帮助开发者更好地掌握和运用STM32F070F6的USB自定义HID功能。 总之,STM32F070F6支持USB自定义HID功能,可以通过软件库和相关配置来实现与计算机的交互。这为开发者提供了一种灵活和方便的方式来创建各种USB外设应用。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值