2024年7月22日发(作者:宁含巧)
STM32 USB-HID复合设备(鼠标+键盘 双接口) 的实现方法
Author :卢晓铭
联系方法:lsj9383@
1.首先更改配置描述符,在usb_desc.c中.这是我使用的配置描述符,第一个接口是键盘功能,
第二个接口是鼠标功能
const u8 Joystick_ConfigDescriptor[JOYSTICK_SIZ_CONFIG_DESC] =
{
/***************配置描述符***********************/
0x09, //bLength字段
USB_CONFIGURATION_DESCRIPTOR_TYPE, //bDescriptorType字段
//wTotalLength字段
JOYSTICK_SIZ_CONFIG_DESC,
/* wTotalLength: Bytes returned */
0x00,
0x02, //bNumInterfaces字段
0x01, //bConfiguration字段
0x00, //iConfigurationz字段
0x80, //bmAttributes字段
0x32, //bMaxPower字段
/*******************第一个接口描述符*********************/
0x09, //bLength字段
0x04, //bDescriptorType字段
0x00, //bInterfaceNumber字段
0x00, //bAlternateSetting字段
0x02, //bNumEndpoints字段
0x03, //bInterfaceClass字段
0x01, //bInterfaceSubClass字段
0x01, //bInterfaceProtocol字段
0x00, //iConfiguration字段
/******************HID描述符************************/
0x09, //bLength字段
0x21, //bDescriptorType字段
0x10, //bcdHID字段
0x01,
0x21, //bCountyCode字段
0x01, //bNumDescriptors字段
0x22, //bDescriptorType字段
//bDescriptorLength字段。
//下级描述符的长度。下级描述符为键盘报告描述符。
sizeof(KeyboardReportDescriptor)&0xFF,
(sizeof(KeyboardReportDescriptor)>>8)&0xFF,
/**********************输入端点描述符***********************/
0x07, //bLength字段
0x05, //bDescriptorType字段
0x81, //bEndpointAddress字段
0x03, //bmAttributes字段
0x10, //wMaxPacketSize字段
0x00,
0x0A, //bInterval字段
/**********************输出端点描述符***********************/
0x07, //bLength字段
0x05, //bDescriptorType字段
0x01, //bEndpointAddress字段
0x03, //bmAttributes字段
0x10, //wMaxPacketSize字段
0x00,
0x0A, //bInterval字段
/*******************第二个接口描述符*********************/
0x09, //bLength字段
0x04, //bDescriptorType字段
0x01, //bInterfaceNumber字段
0x00, //bAlternateSetting字段
0x01, //bNumEndpoints字段
0x03, //bInterfaceClass字段
0x01, //bInterfaceSubClass字段
0x02, //bInterfaceProtocol字段
0x00, //iConfiguration字段
/******************HID描述符************************/
0x09, //bLength字段
0x21, //bDescriptorType字段
0x10, //bcdHID字段
0x01,
0x21, //bCountyCode字段
0x01, //bNumDescriptors字段
0x22, //bDescriptorType字段
sizeof(MouseReportDescriptor)&0xFF, //bDescriptorLength字段
(sizeof(MouseReportDescriptor)>>8)&0xFF,
/**********************输入端点描述符***********************/
0x07, //bLength字段
0x05, //bDescriptorType字段
0x82, //bEndpointAddress字段
0x03, //bmAttributes字段。D1~D0为端点传输类型选择
0x40, //wMaxPacketSize字段
0x00,
0x0A //bInterval字段
};
2.写键盘和鼠标报告描述符
//USB键盘报告描述符的定义
const u8 KeyboardReportDescriptor[KP_ReportDescriptor_Size]=
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop) //63
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)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event
indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION
//0xc0,
}; /* Joystick_ReportDescriptor */
///////////////////////////键盘报告描述符完毕////////////////////////////
//USB鼠标报告描述符的定义
const u8 MouseReportDescriptor[Mouse_ReportDescriptor_Size]=
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, //Report ID (1)
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, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
以上是usb_desc.c中的修改,接着是需要修改usb_desc.h中的宏定义
3.由于我们用了 新的配置描述符,所以,我们需要重新定义配置描述符的大小
#define JOYSTICK_SIZ_CONFIG_DESC 66
4.由于我们用了 键盘鼠标 报告 描述符,所以,我们需要定义键盘报告描述符的大小
#define KP_ReportDescriptor_Size 63
#define Mouse_ReportDescriptor_Size 54
5.报告描述符的大小,但是我们还有增加 键盘鼠标报告描述符 的定义
extern const u8 KeyboardReportDescriptor[KP_ReportDescriptor_Size];
extern const u8 MouseReportDescriptor[Mouse_ReportDescriptor_Size];
(如果有不用的报告描述符,就给删掉吧)
6.好,接着让我们更改usb_prop.c中的代码, 我们可以在usb_prop.c中找到 类似一下的代码
ONE_DESCRIPTOR Joystick_Report_Descriptor = //用于获得报告描述符
{
(u8 *)Joystick_ReportDescriptor,
JOYSTICK_SIZ_REPORT_DESC
};
ONE_DESCRIPTOR Mouse_Hid_Descriptor = //用于获得配置描述符中的Hid描述
{
(u8*)Joystick_ConfigDescriptor + JOYSTICK_OFF_HID_DESC, //
JOYSTICK_SIZ_HID_DESC
};
//JOYSTICK_OFF_HID_DESC是HID描述在配置描述符中的偏移量
由于我们更改了报告描述符 和 配置描述符,所以该处应该修改,并且我们有两个报告描述
符(鼠标+键盘),也有两个HID描述(在配置描述符中),所以,这里一共要有4段代码
ONE_DESCRIPTOR KP_Report_Descriptor = //
{ //
(u8 *)KeyboardReportDescriptor, //
KP_ReportDescriptor_Size //
}; //
//
ONE_DESCRIPTOR KP_Hid_Descriptor = //
{ //
(u8*)Joystick_ConfigDescriptor + KP_OFF_HID_DESC, //
JOYSTICK_SIZ_HID_DESC //
}; //
//
ONE_DESCRIPTOR Mouse_Report_Descriptor = //
{ //
(u8 *)MouseReportDescriptor, //
Mouse_ReportDescriptor_Size //
}; //
//
ONE_DESCRIPTOR Mouse_Hid_Descriptor = //
{ //
(u8*)Joystick_ConfigDescriptor + Mouse_OFF_HID_DESC, //
JOYSTICK_SIZ_HID_DESC //
};
我们上面Mouse_OFF_HID_DESC 和 KP_OFF_HID_DESC分别是鼠标HID描述符,在配
置描述符中的偏移量(也就是在配置描述符中的位置),以及键盘HID描述符在配置描述符中
的偏移量,所以,我们回到usb_desc.h中,定义一下
#define KP_OFF_HID_DESC 18
#define Mouse_OFF_HID_DESC 50
呵呵,可以数来验证一下这个偏移是否正确
7.回到usb_prop.c中继续更改,找到void Joystick_Reset(void)函数
由于配置描述符中,使用到了两个端点,端点1的收发(用于键盘),端点2的输入(用于
鼠标,输入输出 是对于PC机的而言, 输入也就是单片机输入电脑)
将函数更改为一下代码
void Joystick_Reset(void)
{
/* Set Joystick_DEVICE as not configured */
pInformation->Current_Configuration = 0;
pInformation->Current_Interface = 0;/*the default Interface*/
/* Current Feature initialization */
pInformation->Current_Feature = Joystick_ConfigDescriptor[7];
SetBTABLE(BTABLE_ADDRESS);
/* Initialize Endpoint 0 */
SetEPType(ENDP0, EP_CONTROL);
SetEPTxStatus(ENDP0, EP_TX_STALL);
SetEPRxAddr(ENDP0, ENDP0_RXADDR);
SetEPTxAddr(ENDP0, ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0, Device_ketSize);
SetEPRxValid(ENDP0);
/* Initialize Endpoint In 1 */
SetEPType(ENDP1, EP_INTERRUPT); //初始化为中断端点类型
SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送数据的地址
SetEPTxCount(ENDP1, 8); //设置发送的长度
SetEPTxStatus(ENDP1, EP_TX_NAK); //设置端点处于忙状态
/* Initialize Endpoint Out 1 */
SetEPRxAddr(ENDP1, ENDP1_RXADDR); //设置接收数据的地址
SetEPRxCount(ENDP1, 2); //设置接收长度
SetEPRxStatus(ENDP1, EP_RX_VALID); //设置端点有效,可以接收数据
/* Initialize Endpoint In 2 */
SetEPType(ENDP2, EP_INTERRUPT); //初始化为中断端点类型
SetEPTxAddr(ENDP2, ENDP2_TXADDR); //设置发送数据的地址
SetEPTxCount(ENDP2, 5); //设置发送的长度
SetEPTxStatus(ENDP2, EP_TX_NAK); //设置端点处于忙状态
bDeviceState = ATTACHED;
/* Set this device to response on default address */
SetDeviceAddress(0);
}
由于使用了端点,我们到usb_conf.h中 定义一下各个端点
/* EP1 */
/* tx buffer base address */
#define ENDP1_TXADDR (0x100)
#define ENDP1_RXADDR (0x110)
/* EP2 */
/* tx buffer base address */
#define ENDP2_TXADDR (0x120)
8.回到usb_prop.c中继续作修改
找到Joystick_SetConfiguration 和 Joystick_SetDeviceAddress
这两个函数不需要,我们把里面的代码给删掉,变成以下
void Joystick_SetConfiguration(void)
{
}
void Joystick_SetDeviceAddress (void)
{
}
9.找到RESULT Joystick_Data_Setup(u8 RequestNo)函数,在这个函数中,便是对不同的接口
进行区分, USBwIndex0反应出了接口数,不同的接口,返回不同的报告描述符,
根据在配置描述符中的定义,第0个接口,返回键盘的相关描述符,第一个接口,返回鼠标
相关描述符。将函数配置为如下
RESULT Joystick_Data_Setup(u8 RequestNo)
{
u8 *(*CopyRoutine)(u16);
CopyRoutine = NULL;
if ((RequestNo == GET_DESCRIPTOR)
&& (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
&& (pInformation->USBwIndex0 < 2))
{
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)
{
if (pInformation->USBwIndex0 == 0)
CopyRoutine = KP_GetReportDescriptor;
else
CopyRoutine = Mouse_GetReportDescriptor;
}
else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)
{
if (pInformation->USBwIndex0 == 0)
CopyRoutine = KP_GetHIDDescriptor;
else
CopyRoutine = Mouse_GetHIDDescriptor;
}
} /* End of GET_DESCRIPTOR */
/*** GET_PROTOCOL ***/
else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
&& RequestNo == GET_PROTOCOL)
{
CopyRoutine = Joystick_GetProtocolValue;
}
if (CopyRoutine == NULL)
{
return USB_UNSUPPORT;
}
pInformation->Ctrl_ta = CopyRoutine;
pInformation->Ctrl__wOffset = 0;
(*CopyRoutine)(0);
return USB_SUCCESS;
}
在以上代码中
KP_GetReportDescriptor
Mouse_GetReportDescriptor
KP_GetHIDDescriptor;
Mouse_GetHIDDescriptor
都是函数名,这些函数需要定义
CopyRoutine是指针函数,以指示运行哪个函数
我们在usb_prop.h中,对函数进行定义
/***********************************************/
u8 *Mouse_GetReportDescriptor(u16 Length);
u8 *KP_GetReportDescriptor(u16 Length);
u8 *Mouse_GetHIDDescriptor(u16 Length);
u8 *KP_GetHIDDescriptor(u16 Length);
/***********************************************/
把一些没有用的函数删掉,比如
Joystick_GetReportDescriptor(u16 Length)
u8 *Joystick_GetHIDDescriptor(u16 Length)
10.我们回到usb_prop.c中继续修改
找到u8 *Joystick_GetReportDescriptor(u16 Length) 这种 获得报告描述符的 函数
这个函数没有什么用了,我们有我们自己的,可以删掉
u8 *KP_GetReportDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &KP_Report_Descriptor);
}
u8 *Mouse_GetReportDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &Mouse_Report_Descriptor);
}
由于有两个报告描述符需要获得,所以该处申明两个函数,根据接口号码,会进入不同的函
数,Joystick_Data_Setup函数中,我们可以看到接口不同,进入的函数不同,返回的报告描
述符也就不同了。
u8 *Joystick_GetHIDDescriptor(u16 Length) 也没有用,
我们用自己的,删掉它,更改为
u8 *KP_GetHIDDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &KP_Hid_Descriptor);
}
u8 *Mouse_GetHIDDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &Mouse_Hid_Descriptor);
}
现在编译~,我们就可以成功啦~!!
下载进入开发板
我们可以看到设备识别成功啦!
还不保险?那就看看Bus Hound的监控数据吧
点CapsLock有数据流动,那就OK啦。
端点输入数据,请参考
void Joystick_Send(u8 buf0, u8 buf1)
{
u8 Buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
Buffer[0] = buf0;
/* prepare buffer to send */
Buffer[3] = buf1;
if(Buffer[0]==0) //键盘
{
/*copy mouse position info in ENDP1 Tx Packet Memory Area*/
UserToPMABufferCopy(Buffer, GetEPTxAddr(ENDP1), 8);
/* enable endpoint for transmission */
SetEPTxValid(ENDP1);
}
else //鼠标
{
UserToPMABufferCopy(Buffer, GetEPTxAddr(ENDP2), 5);
SetEPTxValid(ENDP2);
}
}
鼠标键盘的具体数据,以及格式,请参考单接口模式下的,这个和那个类似的,用Buffer[0]
来区别键盘和鼠标(虽然发送的端点不一样,但是由于报告描述符中,鼠标键盘数据的
Buffer[0]的数据不一样,所以用这个方法来区别)
另外,还有什么疑问,可以在论坛里面讨论
也可以发送邮箱:lsj9383@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaa
2024年7月22日发(作者:宁含巧)
STM32 USB-HID复合设备(鼠标+键盘 双接口) 的实现方法
Author :卢晓铭
联系方法:lsj9383@
1.首先更改配置描述符,在usb_desc.c中.这是我使用的配置描述符,第一个接口是键盘功能,
第二个接口是鼠标功能
const u8 Joystick_ConfigDescriptor[JOYSTICK_SIZ_CONFIG_DESC] =
{
/***************配置描述符***********************/
0x09, //bLength字段
USB_CONFIGURATION_DESCRIPTOR_TYPE, //bDescriptorType字段
//wTotalLength字段
JOYSTICK_SIZ_CONFIG_DESC,
/* wTotalLength: Bytes returned */
0x00,
0x02, //bNumInterfaces字段
0x01, //bConfiguration字段
0x00, //iConfigurationz字段
0x80, //bmAttributes字段
0x32, //bMaxPower字段
/*******************第一个接口描述符*********************/
0x09, //bLength字段
0x04, //bDescriptorType字段
0x00, //bInterfaceNumber字段
0x00, //bAlternateSetting字段
0x02, //bNumEndpoints字段
0x03, //bInterfaceClass字段
0x01, //bInterfaceSubClass字段
0x01, //bInterfaceProtocol字段
0x00, //iConfiguration字段
/******************HID描述符************************/
0x09, //bLength字段
0x21, //bDescriptorType字段
0x10, //bcdHID字段
0x01,
0x21, //bCountyCode字段
0x01, //bNumDescriptors字段
0x22, //bDescriptorType字段
//bDescriptorLength字段。
//下级描述符的长度。下级描述符为键盘报告描述符。
sizeof(KeyboardReportDescriptor)&0xFF,
(sizeof(KeyboardReportDescriptor)>>8)&0xFF,
/**********************输入端点描述符***********************/
0x07, //bLength字段
0x05, //bDescriptorType字段
0x81, //bEndpointAddress字段
0x03, //bmAttributes字段
0x10, //wMaxPacketSize字段
0x00,
0x0A, //bInterval字段
/**********************输出端点描述符***********************/
0x07, //bLength字段
0x05, //bDescriptorType字段
0x01, //bEndpointAddress字段
0x03, //bmAttributes字段
0x10, //wMaxPacketSize字段
0x00,
0x0A, //bInterval字段
/*******************第二个接口描述符*********************/
0x09, //bLength字段
0x04, //bDescriptorType字段
0x01, //bInterfaceNumber字段
0x00, //bAlternateSetting字段
0x01, //bNumEndpoints字段
0x03, //bInterfaceClass字段
0x01, //bInterfaceSubClass字段
0x02, //bInterfaceProtocol字段
0x00, //iConfiguration字段
/******************HID描述符************************/
0x09, //bLength字段
0x21, //bDescriptorType字段
0x10, //bcdHID字段
0x01,
0x21, //bCountyCode字段
0x01, //bNumDescriptors字段
0x22, //bDescriptorType字段
sizeof(MouseReportDescriptor)&0xFF, //bDescriptorLength字段
(sizeof(MouseReportDescriptor)>>8)&0xFF,
/**********************输入端点描述符***********************/
0x07, //bLength字段
0x05, //bDescriptorType字段
0x82, //bEndpointAddress字段
0x03, //bmAttributes字段。D1~D0为端点传输类型选择
0x40, //wMaxPacketSize字段
0x00,
0x0A //bInterval字段
};
2.写键盘和鼠标报告描述符
//USB键盘报告描述符的定义
const u8 KeyboardReportDescriptor[KP_ReportDescriptor_Size]=
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop) //63
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)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event
indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION
//0xc0,
}; /* Joystick_ReportDescriptor */
///////////////////////////键盘报告描述符完毕////////////////////////////
//USB鼠标报告描述符的定义
const u8 MouseReportDescriptor[Mouse_ReportDescriptor_Size]=
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, //Report ID (1)
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, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
以上是usb_desc.c中的修改,接着是需要修改usb_desc.h中的宏定义
3.由于我们用了 新的配置描述符,所以,我们需要重新定义配置描述符的大小
#define JOYSTICK_SIZ_CONFIG_DESC 66
4.由于我们用了 键盘鼠标 报告 描述符,所以,我们需要定义键盘报告描述符的大小
#define KP_ReportDescriptor_Size 63
#define Mouse_ReportDescriptor_Size 54
5.报告描述符的大小,但是我们还有增加 键盘鼠标报告描述符 的定义
extern const u8 KeyboardReportDescriptor[KP_ReportDescriptor_Size];
extern const u8 MouseReportDescriptor[Mouse_ReportDescriptor_Size];
(如果有不用的报告描述符,就给删掉吧)
6.好,接着让我们更改usb_prop.c中的代码, 我们可以在usb_prop.c中找到 类似一下的代码
ONE_DESCRIPTOR Joystick_Report_Descriptor = //用于获得报告描述符
{
(u8 *)Joystick_ReportDescriptor,
JOYSTICK_SIZ_REPORT_DESC
};
ONE_DESCRIPTOR Mouse_Hid_Descriptor = //用于获得配置描述符中的Hid描述
{
(u8*)Joystick_ConfigDescriptor + JOYSTICK_OFF_HID_DESC, //
JOYSTICK_SIZ_HID_DESC
};
//JOYSTICK_OFF_HID_DESC是HID描述在配置描述符中的偏移量
由于我们更改了报告描述符 和 配置描述符,所以该处应该修改,并且我们有两个报告描述
符(鼠标+键盘),也有两个HID描述(在配置描述符中),所以,这里一共要有4段代码
ONE_DESCRIPTOR KP_Report_Descriptor = //
{ //
(u8 *)KeyboardReportDescriptor, //
KP_ReportDescriptor_Size //
}; //
//
ONE_DESCRIPTOR KP_Hid_Descriptor = //
{ //
(u8*)Joystick_ConfigDescriptor + KP_OFF_HID_DESC, //
JOYSTICK_SIZ_HID_DESC //
}; //
//
ONE_DESCRIPTOR Mouse_Report_Descriptor = //
{ //
(u8 *)MouseReportDescriptor, //
Mouse_ReportDescriptor_Size //
}; //
//
ONE_DESCRIPTOR Mouse_Hid_Descriptor = //
{ //
(u8*)Joystick_ConfigDescriptor + Mouse_OFF_HID_DESC, //
JOYSTICK_SIZ_HID_DESC //
};
我们上面Mouse_OFF_HID_DESC 和 KP_OFF_HID_DESC分别是鼠标HID描述符,在配
置描述符中的偏移量(也就是在配置描述符中的位置),以及键盘HID描述符在配置描述符中
的偏移量,所以,我们回到usb_desc.h中,定义一下
#define KP_OFF_HID_DESC 18
#define Mouse_OFF_HID_DESC 50
呵呵,可以数来验证一下这个偏移是否正确
7.回到usb_prop.c中继续更改,找到void Joystick_Reset(void)函数
由于配置描述符中,使用到了两个端点,端点1的收发(用于键盘),端点2的输入(用于
鼠标,输入输出 是对于PC机的而言, 输入也就是单片机输入电脑)
将函数更改为一下代码
void Joystick_Reset(void)
{
/* Set Joystick_DEVICE as not configured */
pInformation->Current_Configuration = 0;
pInformation->Current_Interface = 0;/*the default Interface*/
/* Current Feature initialization */
pInformation->Current_Feature = Joystick_ConfigDescriptor[7];
SetBTABLE(BTABLE_ADDRESS);
/* Initialize Endpoint 0 */
SetEPType(ENDP0, EP_CONTROL);
SetEPTxStatus(ENDP0, EP_TX_STALL);
SetEPRxAddr(ENDP0, ENDP0_RXADDR);
SetEPTxAddr(ENDP0, ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0, Device_ketSize);
SetEPRxValid(ENDP0);
/* Initialize Endpoint In 1 */
SetEPType(ENDP1, EP_INTERRUPT); //初始化为中断端点类型
SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送数据的地址
SetEPTxCount(ENDP1, 8); //设置发送的长度
SetEPTxStatus(ENDP1, EP_TX_NAK); //设置端点处于忙状态
/* Initialize Endpoint Out 1 */
SetEPRxAddr(ENDP1, ENDP1_RXADDR); //设置接收数据的地址
SetEPRxCount(ENDP1, 2); //设置接收长度
SetEPRxStatus(ENDP1, EP_RX_VALID); //设置端点有效,可以接收数据
/* Initialize Endpoint In 2 */
SetEPType(ENDP2, EP_INTERRUPT); //初始化为中断端点类型
SetEPTxAddr(ENDP2, ENDP2_TXADDR); //设置发送数据的地址
SetEPTxCount(ENDP2, 5); //设置发送的长度
SetEPTxStatus(ENDP2, EP_TX_NAK); //设置端点处于忙状态
bDeviceState = ATTACHED;
/* Set this device to response on default address */
SetDeviceAddress(0);
}
由于使用了端点,我们到usb_conf.h中 定义一下各个端点
/* EP1 */
/* tx buffer base address */
#define ENDP1_TXADDR (0x100)
#define ENDP1_RXADDR (0x110)
/* EP2 */
/* tx buffer base address */
#define ENDP2_TXADDR (0x120)
8.回到usb_prop.c中继续作修改
找到Joystick_SetConfiguration 和 Joystick_SetDeviceAddress
这两个函数不需要,我们把里面的代码给删掉,变成以下
void Joystick_SetConfiguration(void)
{
}
void Joystick_SetDeviceAddress (void)
{
}
9.找到RESULT Joystick_Data_Setup(u8 RequestNo)函数,在这个函数中,便是对不同的接口
进行区分, USBwIndex0反应出了接口数,不同的接口,返回不同的报告描述符,
根据在配置描述符中的定义,第0个接口,返回键盘的相关描述符,第一个接口,返回鼠标
相关描述符。将函数配置为如下
RESULT Joystick_Data_Setup(u8 RequestNo)
{
u8 *(*CopyRoutine)(u16);
CopyRoutine = NULL;
if ((RequestNo == GET_DESCRIPTOR)
&& (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
&& (pInformation->USBwIndex0 < 2))
{
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)
{
if (pInformation->USBwIndex0 == 0)
CopyRoutine = KP_GetReportDescriptor;
else
CopyRoutine = Mouse_GetReportDescriptor;
}
else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)
{
if (pInformation->USBwIndex0 == 0)
CopyRoutine = KP_GetHIDDescriptor;
else
CopyRoutine = Mouse_GetHIDDescriptor;
}
} /* End of GET_DESCRIPTOR */
/*** GET_PROTOCOL ***/
else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
&& RequestNo == GET_PROTOCOL)
{
CopyRoutine = Joystick_GetProtocolValue;
}
if (CopyRoutine == NULL)
{
return USB_UNSUPPORT;
}
pInformation->Ctrl_ta = CopyRoutine;
pInformation->Ctrl__wOffset = 0;
(*CopyRoutine)(0);
return USB_SUCCESS;
}
在以上代码中
KP_GetReportDescriptor
Mouse_GetReportDescriptor
KP_GetHIDDescriptor;
Mouse_GetHIDDescriptor
都是函数名,这些函数需要定义
CopyRoutine是指针函数,以指示运行哪个函数
我们在usb_prop.h中,对函数进行定义
/***********************************************/
u8 *Mouse_GetReportDescriptor(u16 Length);
u8 *KP_GetReportDescriptor(u16 Length);
u8 *Mouse_GetHIDDescriptor(u16 Length);
u8 *KP_GetHIDDescriptor(u16 Length);
/***********************************************/
把一些没有用的函数删掉,比如
Joystick_GetReportDescriptor(u16 Length)
u8 *Joystick_GetHIDDescriptor(u16 Length)
10.我们回到usb_prop.c中继续修改
找到u8 *Joystick_GetReportDescriptor(u16 Length) 这种 获得报告描述符的 函数
这个函数没有什么用了,我们有我们自己的,可以删掉
u8 *KP_GetReportDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &KP_Report_Descriptor);
}
u8 *Mouse_GetReportDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &Mouse_Report_Descriptor);
}
由于有两个报告描述符需要获得,所以该处申明两个函数,根据接口号码,会进入不同的函
数,Joystick_Data_Setup函数中,我们可以看到接口不同,进入的函数不同,返回的报告描
述符也就不同了。
u8 *Joystick_GetHIDDescriptor(u16 Length) 也没有用,
我们用自己的,删掉它,更改为
u8 *KP_GetHIDDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &KP_Hid_Descriptor);
}
u8 *Mouse_GetHIDDescriptor(u16 Length)
{
return Standard_GetDescriptorData(Length, &Mouse_Hid_Descriptor);
}
现在编译~,我们就可以成功啦~!!
下载进入开发板
我们可以看到设备识别成功啦!
还不保险?那就看看Bus Hound的监控数据吧
点CapsLock有数据流动,那就OK啦。
端点输入数据,请参考
void Joystick_Send(u8 buf0, u8 buf1)
{
u8 Buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
Buffer[0] = buf0;
/* prepare buffer to send */
Buffer[3] = buf1;
if(Buffer[0]==0) //键盘
{
/*copy mouse position info in ENDP1 Tx Packet Memory Area*/
UserToPMABufferCopy(Buffer, GetEPTxAddr(ENDP1), 8);
/* enable endpoint for transmission */
SetEPTxValid(ENDP1);
}
else //鼠标
{
UserToPMABufferCopy(Buffer, GetEPTxAddr(ENDP2), 5);
SetEPTxValid(ENDP2);
}
}
鼠标键盘的具体数据,以及格式,请参考单接口模式下的,这个和那个类似的,用Buffer[0]
来区别键盘和鼠标(虽然发送的端点不一样,但是由于报告描述符中,鼠标键盘数据的
Buffer[0]的数据不一样,所以用这个方法来区别)
另外,还有什么疑问,可以在论坛里面讨论
也可以发送邮箱:lsj9383@
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaa