继上次实现了USB的Gamepad手柄之后,见贴(https://bbs.21ic.com/icview-3220444-1-1.html)。本次实现了键盘鼠标手柄一体的复合设备。
效果如下:
板上的2个按键模拟了3个设备发送数据。
下面简单说下实现过程:
首先修改增加配置描述符:
const uint8_t g_hidConfigDescriptor[] =
{
// configuration_descriptor hid_configuration_descriptor
0x09, // Length
0x02, // Type
HID_CONFIG_DESCRIPTOR_SIZE&0xff,HID_CONFIG_DESCRIPTOR_SIZE>>8,// Totallength (= 9+9+9+7+7)
0x03, // NumInterfaces ?????
0x01, // bConfigurationValue
0x00, // iConfiguration ??????????
0x80, // bmAttributes
0xc0, // MaxPower (in 2mA units)
// interface_descriptor hid_interface_descriptor
0x09, // bLength
0x04, // bDescriptorType
0x00, // bInterfaceNumber ???,???????????
0x00, // bAlternateSetting ???????
0x02, // bNumEndpoints ??????????,?0?????????0
0x03, // bInterfaceClass (3 = HID)
0x00, // bInterfaceSubClass
0x00, // bInterfaceProcotol
0x04, // iInterface Index of string descriptor
// class_descriptor hid_descriptor
0x09, // bLength
0x21, // bDescriptorType
0x01,0x01, // bcdHID
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType
HID_GAMEPAD_REPORT_DESCRIPTOR_SIZE,0X00, // wItemLength (tot. len. of report descriptor)
// IN endpoint (mandatory for HID)
// endpoint1_descriptor hid_endpoint_in_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x81, // bEndpointAddress
0x03, // bmAttributes 00-????,01-????,10-???,11-????
0x40,0x00, // MaxPacketSize (LITTLE ENDIAN)
10, // bInterval
// OUT endpoint (optional for HID)
// endpoint1_descriptor hid_endpoint_out_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x01, // bEndpointAddress
0x03, // bmAttributes
0x40,0x00, // MaxPacketSize (LITTLE ENDIAN)
10, // bInterval
// interface_descriptor hid_interface_descriptor 222222222222222222222222222222
0x09, // bLength
0x04, // bDescriptorType
0x01, // bInterfaceNumber ???,???????????
0x00, // bAlternateSetting ???????
0x02, // bNumEndpoints ??????????,?0?????????0
0x03, // bInterfaceClass (3 = HID)
0x01, // bInterfaceSubClass 0-nosub,1-bootclass,2-reserved
0x01, // bInterfaceProcotol 0-No,1-keyboard,2-mouse,
0x05, // iInterface Index of string descriptor
// class_descriptor hid_descriptor
0x09, // bLength
0x21, // bDescriptorType
0x01,0x01, // bcdHID
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType
HID_KEYBOARD_REPORT_DESCRIPTOR_SIZE,0x00,// wItemLength (tot. len. of report descriptor)
// IN endpoint (mandatory for HID)
// endpoint1_descriptor hid_endpoint_in_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x82, // bEndpointAddress
0x03, // bmAttributes 00-????,01-????,10-???,11-????
0x40,0x00, // MaxPacketSize (LITTLE ENDIAN)
0x0a, // bInterval
// OUT endpoint (optional for HID)
// endpoint1_descriptor hid_endpoint_out_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x02, // bEndpointAddress
0x03, // bmAttributes
0x40,0x00, // MaxPacketSize (LITTLE ENDIAN)
0x0a, // bInterval
/************** Descriptor of Mouse HID interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
0x04, /*bDescriptorType: Interface descriptor type*/
0x02, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x01, /*bNumEndpoints*/
0x03, /*bInterfaceClass: CUSTOM_HID*/
0x01, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x02, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0X06, /*iInterface: Index of string descriptor*/
/******************** Descriptor of CUSTOM_HID *************************/
/* 18 */
0x09, /*bLength: CUSTOM_HID Descriptor size*/
0x21, /*bDescriptorType: CUSTOM_HID*/
0x11, /*bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
0x22, /*bDescriptorType*/
HID_MOUSE_REPORT_DESCRIPTOR_SIZE,0x00,/*wItemLength: Total length of Report descriptor*/
/******************** Descriptor of Custom HID endpoints ********************/
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
0x05, /*bDescriptorType:*/
0x83, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
0x40,0x00, /*wMaxPacketSize: 4 Byte max */
0x08, /*bInterval: Polling Interval (20 ms)*/
/* 34 */
};
其次就是3个设备的报告描述符
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] HID report descriptor
*/
const uint8_t g_hidGamepadReportDescriptor[] =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Game Pad)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x33, // USAGE (RX)
0x09, 0x34, // USAGE (RY)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x04, // REPORT_COUNT (4)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0x09, 0x36, // USAGE (Silder)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x39, // USAGE (Hat switch)
0x15, 0x01, // LOGICAL_MINIMUM (1)
0x25, 0x08, // LOGICAL_MAXIMUM (8)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x42, // INPUT (Data,Var,Abs,Null)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x10, // USAGE_MAXIMUM (Button 16)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x10, // REPORT_COUNT (16)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined Page 1)
0x09, 0x01, // USAGE (Vendor Usage 1)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x04, // REPORT_COUNT (4)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0xc0 // END_COLLECTION
};
const uint8_t g_hidKeyboardReportDescriptor[]=
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xE0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xE7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0xdd, // USAGE_MAXIMUM (Keyboard Application)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0xdd, // LOGICAL_MAXIMUM (164)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0xc0 // END_COLLECTION
};
const uint8_t g_hidMouseReportDescriptor[]=
{
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)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0
};
/** Device descriptor */
const USB_Descriptor_T s_deviceDescriptor = {g_hidDeviceDescriptor, HID_DEVICE_DESCRIPTOR_SIZE};
/** Config descriptor */
const USB_Descriptor_T s_configDescriptor = {g_hidConfigDescriptor, HID_CONFIG_DESCRIPTOR_SIZE};
/** String descriptor */
const USB_Descriptor_T s_stringDescriptor[] =
{
{g_hidLandIDString, HID_LANGID_STRING_SIZE},
{g_hidVendorString, HID_VENDOR_STRING_SIZE},
{g_hidProductString, HID_PRODUCT_STRING_SIZE},
{g_hidSerialString, HID_SERIAL_STRING_SIZE},
{g_Class1StringDesc, 16},
{g_Class2StringDesc, 18},
{g_Class3StringDesc, 12},
};
const USB_Descriptor_T s_reportDescriptor[] =
{
{g_hidGamepadReportDescriptor, HID_GAMEPAD_REPORT_DESCRIPTOR_SIZE},
{g_hidKeyboardReportDescriptor, HID_KEYBOARD_REPORT_DESCRIPTOR_SIZE},
{g_hidMouseReportDescriptor, HID_MOUSE_REPORT_DESCRIPTOR_SIZE},
};
接着就是USB端点的初始化。本次要使用端点1,端点2的输入输出端点,端点3的输入端点。
初始化如下:
void HidMouse_Reset(void)
{
uint8_t i;
s_usbConfigStatus = 0; //usb not config,disable send data
USBD_SetBufferTable(USB_BUFFER_TABLE_ADDR);
//endpoint 0 init
USBD_ResetEPKind(USBD_EP_0);
USBD_SetEPType(USBD_EP_0, USBD_EP_TYPE_CONTROL);
USBD_SetEPTxStatus(USBD_EP_0, USBD_EP_STATUS_STALL);
USBD_SetEPTxAddr(USBD_EP_0, USB_EP0_TX_ADDR);
USBD_SetEPRxStatus(USBD_EP_0, USBD_EP_STATUS_VALID);
USBD_SetEPRxAddr(USBD_EP_0, USB_EP0_RX_ADDR);
USBD_SetEPRxCnt(USBD_EP_0, USB_EP_PACKET_SIZE);
//ep1 gamepad
USBD_SetEPType(USBD_EP_1, USBD_EP_TYPE_INTERRUPT);
USBD_SetEPTxAddr(USBD_EP_1, USB_EP1_TX_ADDR);
USBD_SetEPTxCnt(USBD_EP_1, 8);
USBD_SetEPRxAddr(USBD_EP_1, USB_EP1_RX_ADDR);
USBD_SetEPRxCnt(USBD_EP_1, 4);
USBD_SetEPRxTxStatus(USBD_EP_1, USBD_EP_STATUS_VALID, USBD_EP_STATUS_VALID);
//EP2 keyboard
USBD_SetEPType(USBD_EP_2, USBD_EP_TYPE_INTERRUPT);
USBD_SetEPTxAddr(USBD_EP_2, USB_EP2_TX_ADDR);
USBD_SetEPTxCnt(USBD_EP_2, 8);
USBD_SetEPRxAddr(USBD_EP_2, USB_EP2_RX_ADDR);
USBD_SetEPRxCnt(USBD_EP_2, 1);
USBD_SetEPRxTxStatus(USBD_EP_2, USBD_EP_STATUS_VALID, USBD_EP_STATUS_VALID);
//EP3 mouse
USBD_SetEPType(USBD_EP_3, USBD_EP_TYPE_INTERRUPT);
USBD_SetEPTxAddr(USBD_EP_3, USB_EP3_TX_ADDR);
USBD_SetEPTxCnt(USBD_EP_3, 4);
USBD_SetEPRxAddr(USBD_EP_3, USB_EP3_RX_ADDR);
USBD_SetEPRxCnt(USBD_EP_3, 8);
USBD_SetEPRxTxStatus(USBD_EP_3,USBD_EP_STATUS_VALID, USBD_EP_STATUS_DISABLE );
//endpoint addr
for(i = 0; i < USB_EP_NUM; i++)
{
USBD_SetEpAddr((USBD_EP_T)i, i);
}
USBD_SetDeviceAddr(0);
USBD_Enable();
}
下面说一下端点Buffer地址设置,这里容易搞错。
/** EP0 Tx address */
#define USB_EP0_TX_ADDR (0X40)
/** EP0 Rx address */
#define USB_EP0_RX_ADDR (0X80)
/** EP1 Tx address */
#define USB_EP1_TX_ADDR (0XC0)
/** EP1 Rx address */
#define USB_EP1_RX_ADDR (0XD0)
/** EP2 Tx address */
#define USB_EP2_TX_ADDR (0XE0)
/** EP2 Rx address */
#define USB_EP2_RX_ADDR (0XF0)
/** EP3 Tx address */
#define USB_EP3_TX_ADDR (0X100)
/** EP3 Rx address */
#define USB_EP3_RX_ADDR (0X110)
首先端点buffer地址为什么要从0x40开始,因为这个MCU有8个端点,每个端点又分输入输出。
而且每个端点有buffer起始地址和数据长度空间都占4字节。使用1个输入端点就要增加8字节,1个输出端点也要8字节。
本次使用了端点0,1,2,3。所以地址至少要7*8=56字节即0x38。后面地址就要根据每个端点的数据包长度来修改buffer地址。
最后就是按键模拟,及USB数据发送过程处理
void HidMouse_Proc(void)
{
static int8_t x = 0;
static int8_t y = 0;
uint8_t buffer[8] = {0, 0, 0, 0};
uint8_t buffer1[8] = {0, 0, 0, 0};
uint8_t buffer2[4] = {0, 0, 0, 0};
if(!s_usbConfigStatus)
{
return;
}
if(g_time_tick == 0)
{
g_time_tick = 10;
/** Right key */
if(!GPIO_ReadInputBit(GPIOA, GPIO_PIN_0))
{
GPIOE->ODATA ^= GPIO_PIN_5;
x -= 10;
buffer[5] = 3;
buffer[6] |= 0x01;
buffer1[2] = 0x20;
buffer2[1] = -5;
}else
{
buffer[6] &= ~0x01;
buffer1[2] = 0;
buffer2[1] = 0;
}
/** Left key */
if(!GPIO_ReadInputBit(GPIOA, GPIO_PIN_1))
{
GPIOE->ODATA ^= GPIO_PIN_6;
x += 10;
buffer[5] = 7;
buffer[6] |= 0x02;
buffer1[3] = 0x21;
buffer2[1] = 5;
}else
{
buffer[6] &= ~0x02;
buffer1[3] = 0;
}
buffer[0] = x;
buffer[1] = y;
if(s_statusEP_in[1])
{
s_statusEP_in[1] = 0; //send over flag
USBD_WriteDataToEP(USBD_EP_1, buffer, sizeof(buffer)); //write data to buffer
USBD_SetEPTxStatus(USBD_EP_1, USBD_EP_STATUS_VALID); //send data valid
}
if(s_statusEP_in[2])
{
s_statusEP_in[2] = 0; //send over flag
USBD_WriteDataToEP(USBD_EP_2, buffer1, sizeof(buffer1)); //write data to buffer
USBD_SetEPTxStatus(USBD_EP_2, USBD_EP_STATUS_VALID); //send data valid
}
if(s_statusEP_in[3])
{
s_statusEP_in[3] = 0; //send over flag
USBD_WriteDataToEP(USBD_EP_3, buffer2, sizeof(buffer2)); //write data to buffer
USBD_SetEPTxStatus(USBD_EP_3, USBD_EP_STATUS_VALID); //send data valid
}
}
}
程序:
|
|