如何定义STM32 USB HID设备缓冲区地址?请求帮助

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何定义STM32 USB HID设备缓冲区地址?请求帮助相关的知识,希望对你有一定的参考价值。

参考技术A USB HID报告及报告描述符简介相关讨论:

在USB中,USB HOST是通过各种描述符来识别设备的,有设备描述符,
配置描述符,接口描述符,端点描述符,字符串描述符,报告描述符等等。
USB报告描述符(Report Descriptor)是HID设备中的一个描述符,它是比较
复杂的一个描述符。

USB HID设备是通过报告来给传送数据的,报告有输入报告和输出报告。
输入报告是USB设备发送给主机的,例如USB鼠标将鼠标移动和鼠标点击等
信息返回给电脑,键盘将按键数据数据返回给电脑等;输出报告是主机发送
给USB设备的,例如键盘上的数字键盘锁定灯和大写字母锁定灯等。报告是
一个数据包,里面包含的是所要传送的数据。输入报告是通过中断输入端点
输入的,而输出报告有点区别,当没有中断输出端点时,可以通过控制输出
端点0发送,当有中断输出端点时,通过中断输出端点发出。

而报告描述符,是描述一个报告以及报告里面的数据是用来干什么用的。
通过它,USB HOST可以分析出报告里面的数据所表示的意思。它通过控制输入
端点0返回,主机使用获取报告描述符命令来获取报告描述符,注意这个请求
是发送到接口的,而不是到设备。一个报告描述符可以描述多个报告,不同的
报告通过报告ID来识别,报告ID在报告最前面,即第一个字节。当报告描述符中
没有规定报告ID时,报告中就没有ID字段,开始就是数据。更详细的说明请参看
USB HID协议,该协议可从下载。

USB报告描述符可以通过使用HID Descriptor tool来生成,这个工具可以
到下载,为了方便大家,我顺便上传了一份。

下面通过由HID Descriptor tool生成的USB鼠标和USB键盘来说明一下报告
描述符和报告。

code char KeyBoardReportDescriptor[63] =
//表示用途页为通用桌面设备
0x05, 0x01, // USAGE_PAGE (Generic Desktop)

//表示用途为键盘
0x09, 0x06, // USAGE (Keyboard)

//表示应用集合,必须要以END_COLLECTION来结束它,见最后的END_COLLECTION
0xa1, 0x01, // COLLECTION (Application)

//表示用途页为按键
0x05, 0x07, // USAGE_PAGE (Keyboard)

//用途最小值,这里为左ctrl键
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
//用途最大值,这里为右GUI键,即window键
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
//逻辑最小值为0
0x15, 0x00, // LOGICAL_MINIMUM (0)
//逻辑最大值为1
0x25, 0x01, // LOGICAL_MAXIMUM (1)
//报告大小(即这个字段的宽度)为1bit,所以前面的逻辑最小值为0,逻辑最大值为1
0x75, 0x01, // REPORT_SIZE (1)
//报告的个数为8,即总共有8个bits
0x95, 0x08, // REPORT_COUNT (8)
//输入用,变量,值,绝对值。像键盘这类一般报告绝对值,
//而鼠标移动这样的则报告相对值,表示鼠标移动多少
0x81, 0x02, // INPUT (Data,Var,Abs)
//上面这这几项描述了一个输入用的字段,总共为8个bits,每个bit表示一个按键
//分别从左ctrl键到右GUI键。这8个bits刚好构成一个字节,它位于报告的第一个字节。
//它的最低位,即bit-0对应着左ctrl键,如果返回的数据该位为1,则表示左ctrl键被按下,
//否则,左ctrl键没有按下。最高位,即bit-7表示右GUI键的按下情况。中间的几个位,
//需要根据HID协议中规定的用途页表(HID Usage Tables)来确定。这里通常用来表示
//特殊键,例如ctrl,shift,del键等

//这样的数据段个数为1
0x95, 0x01, // REPORT_COUNT (1)
//每个段长度为8bits
0x75, 0x08, // REPORT_SIZE (8)
//输入用,常量,值,绝对值
0x81, 0x03, // INPUT (Cnst,Var,Abs)

//上面这8个bit是常量,设备必须返回0

//这样的数据段个数为5
0x95, 0x05, // REPORT_COUNT (5)
//每个段大小为1bit
0x75, 0x01, // REPORT_SIZE (1)
//用途是LED,即用来控制键盘上的LED用的,因此下面会说明它是输出用
0x05, 0x08, // USAGE_PAGE (LEDs)
//用途最小值是Num Lock,即数字键锁定灯
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
//用途最大值是Kana,这个是什么灯我也不清楚^_^
0x29, 0x05, // USAGE_MAXIMUM (Kana)
//如前面所说,这个字段是输出用的,用来控制LED。变量,值,绝对值。
//1表示灯亮,0表示灯灭
0x91, 0x02, // OUTPUT (Data,Var,Abs)

//这样的数据段个数为1
0x95, 0x01, // REPORT_COUNT (1)
//每个段大小为3bits
0x75, 0x03, // REPORT_SIZE (3)
//输出用,常量,值,绝对
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
//由于要按字节对齐,而前面控制LED的只用了5个bit,
//所以后面需要附加3个不用bit,设置为常量。

//报告个数为6
0x95, 0x06, // REPORT_COUNT (6)
//每个段大小为8bits
0x75, 0x08, // REPORT_SIZE (8)
//逻辑最小值0
0x15, 0x00, // LOGICAL_MINIMUM (0)
//逻辑最大值255
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
//用途页为按键
0x05, 0x07, // USAGE_PAGE (Keyboard)
//使用最小值为0
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
//使用最大值为0x65
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
//输入用,变量,数组,绝对值
0x81, 0x00, // INPUT (Data,Ary,Abs)
//以上定义了6个8bit宽的数组,每个8bit(即一个字节)用来表示一个按键,所以可以同时
//有6个按键按下。没有按键按下时,全部返回0。如果按下的键太多,导致键盘扫描系统
//无法区分按键时,则全部返回0x01,即6个0x01。如果有一个键按下,则这6个字节中的第一
//个字节为相应的键值(具体的值参看HID Usage Tables),如果两个键按下,则第1、2两个
//字节分别为相应的键值,以次类推。

//关集合,跟上面的对应
0xc0 // END_COLLECTION
;

通过上面的分析,我们知道这个报告中只有一个报告,所以没有报告ID,
因此返回的都是实际使用的数据。总共有8字节输入,1字节输出。其中输入的
第一字节用来表示特殊按键,第二字节保留,后面的六字节为普通按键。如果
只有左ctrl键按下,则返回01 00 00 00 00 00 00 00(十六进制),如果
只有数字键1 按下,则返回00 00 59 00 00 00 00 00,如果数字
键1 和2 同时按下,则返回00 00 59 5A 00 00 00 00,如果
再按下左shift 键,则返回02 00 59 5A 00 00 00 00,
然后再释放1 键,则返回02 00 5A 00 00 00 00 00,
然后全部按键释放,则返回00 00 00 00 00 00 00 00。
这些数据(即报告)都是通过中断端点返回的。当按下Num Lock键时,PC会发送
输出报告,从报告描述符中我们知道,Num Lock的LED对应着输出报告的最低位,
当数字小键盘打开时,输出xxxxxxx1(二进制,打x的由其它的LED状态决定);
当数字小键盘关闭时,输出xxxxxxx0(同前)。取出最低位就可以控制数字键锁定LED了。

下面这个报告描述符是USB鼠标报告描述符,比起键盘的来说要简单些。
它描述了4个字节,第一个字节表示按键,第二个字节表示x轴(即鼠标左右移动,
0表示不动,正值表示往右移,负值表示往左移),第三个字节表示y轴(即鼠标
上下移动,0表示不动,正值表示往下移动,负值表示往上移动),第四个字节
表示鼠标滚轮(正值为往上滚动,负值为往下滚动)。

code char MouseReportDescriptor[52] =
//通用桌面设备
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
//鼠标
0x09, 0x02, // USAGE (Mouse)
//集合
0xa1, 0x01, // COLLECTION (Application)
//指针设备
0x09, 0x01, // USAGE (Pointer)
//集合
0xa1, 0x00, // COLLECTION (Physical)
//按键
0x05, 0x09, // USAGE_PAGE (Button)
//使用最小值1
0x19, 0x01, // USAGE_MINIMUM (Button 1)
//使用最大值3。1表示左键,2表示右键,3表示中键
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
//逻辑最小值0
0x15, 0x00, // LOGICAL_MINIMUM (0)
//逻辑最大值1
0x25, 0x01, // LOGICAL_MAXIMUM (1)
//数量为3
0x95, 0x03, // REPORT_COUNT (3)
//大小为1bit
0x75, 0x01, // REPORT_SIZE (1)
//输入,变量,数值,绝对值
//以上3个bit分别表示鼠标的三个按键情况,最低位(bit-0)为左键
//bit-1为右键,bit-2为中键,按下时对应的位值为1,释放时对应的值为0
0x81, 0x02, // INPUT (Data,Var,Abs)
//填充5个bit,补足一个字节
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)

//用途页为通用桌面
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
//用途为X
0x09, 0x30, // USAGE (X)
//用途为Y
0x09, 0x31, // USAGE (Y)
//用途为滚轮
0x09, 0x38, // USAGE (Wheel)
//逻辑最小值为-127
0x15, 0x81, // LOGICAL_MINIMUM (-127)
//逻辑最大值为+127
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
//大小为8个bits
0x75, 0x08, // REPORT_SIZE (8)
//数量为3个,即分别代表x,y,滚轮
0x95, 0x03, // REPORT_COUNT (3)
//输入,变量,值,相对值
0x81, 0x06, // INPUT (Data,Var,Rel)

//关集合
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
;
通过对上面的报告分析,我们知道报告返回4个字节,没有报告ID。如果鼠标左键按下,
则返回01 00 00 00(十六进制值),如果右键按下,则返回02 00 00 00,如果中键按下,
则返回04 00 00 00,如果三个键同时按下,则返回07 00 00 00。如果鼠标往右移动则
第二字节返回正值,值越大移动速度越快。其它的类推。
你的串号我已经记下,采纳后我会帮你制作

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设备。如下图示:

以上是关于如何定义STM32 USB HID设备缓冲区地址?请求帮助的主要内容,如果未能解决你的问题,请参考以下文章

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

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

STM32CubeMX学习笔记(46)——USB接口使用(HID自定义设备)

STM32CubeMX学习笔记(46)——USB接口使用(HID自定义设备)

记录一下 开发STM32 USB HID踩过的坑

记录一下 开发STM32 USB HID踩过的坑