这次给大家介绍一下如何用APM32F072的USB HID配置一个 USB键盘+鼠标 复合设备的过程。
首先我们需要1个USB键盘的例程(官方有例程)
第二步我们需要1个USB 鼠标的例程 (官方有例程)
第三步将他们结合起来变成USB 键盘+鼠标
这里我们用USB鼠标的例程基础上添加键盘的功能。
第亿、打开USB_HID例程,修改USBD_ConfigDesc中的(USBD_CONFIG_DESCRIPTOR_SIZE)。
如下图所示的宏定义。配置集合的长度,由之前的34,改为41.
第二、USBD_ConfigDesc函数继续往下走找到
1、端点个数:由0×01变成0×02.
2、接口协议:由0×02(鼠标)变成0×01(键盘)。
这里主要还是以键盘功能为主,鼠标只是以一个附属功能加入到了键盘里。
3、在原来的鼠标例程上的最下面增加了一个输出端点的描述符:
第三,在usbd_hid.c修改USBD_HIDReportDesc函数报告描述符的长度:之前这里是鼠标的报告描述符,长度是74 改成117
接下来就是将键盘和鼠标的报告描述符合并为一个数组,分别在键盘和鼠标的报告描述符中放一个报告ID,
键盘的报告ID是1,鼠标的报告ID是2。这样USB主机就可以区分开收到的数据是键盘或是鼠标的数据。
这是加入后HID键盘+鼠标里的完整报告描述符
`uint8_t USBD_HIDReportDesc[USBD_HID_MOUSE_REPORT_DESC_SIZE] =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (1)
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
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, // REPORT_ID (2)
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)
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)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
`
这样,我们USB键盘+鼠标二合一就改好了, 那么需要怎么使用呢?
第三、鼠标和键盘的使用属性简单介绍:
1、Mouse 鼠标:
我们用的鼠标主要就四个属性:键位(左键、右键、中键),左右移动、上下移动、滑轮
1.第 1 个字节(buff[0]):按钮状态 0×01左键 0×02右键 0×04中键(滚轮按钮)
2.第 2 个字节(buff[1]):X 轴移动量
3.第 3 个字节(buff[2]):Y 轴移动量
4.第 4 个字节(buff[3]):滚轮移动量 0×01表示滚轮向前滚动一格;0xFF表示滚轮向后滚动一格;0×80是个中间值,不滚动。
以下是一个简单的鼠标使用发送函数示例:
`void HidMouse_Write(uint8_t key)
{
int8_t x = 0;
int8_t y = 0;
int8_t Button = 0;
int8_t Wheel = 0;
uint8_t buffer[4] = {0, 0, 0, 0};
switch (key)
{
case HID_MOUSE_KEY_LEFT:
x -= 10;
break;
case HID_MOUSE_KEY_RIGHT:
x += 10;
break;
case HID_MOUSE_KEY_UP:
y -= 10;
break;
case HID_MOUSE_KEY_DOWN:
y += 10;
break;
case HID_MOUSE_BUTTON:
Button = 0x01; //0x01左键 0x02右键 0x04中键(滚轮按钮)
break;
case HID_MOUSE_WHEEL:
Wheel = 0xff; //0x01表示滚轮向前滚动一格;0xFF表示滚轮向后滚动一格;0x80是个中间值,不滚动。
break;
default:
return;
}
buffer[0] = Button;
buffer[1] = x;
buffer[2] = y;
buffer[3] = Wheel;
s_statusEP = 0;
USBD_TxData(USBD_EP_1, buffer, sizeof(buffer));
}
`
2、Keyboard 键盘:
在键盘 HID 报告格式中,每个字节代表不同的含义:
1.第 1 个字节(buff[0]) 修饰键的状态(如 Ctrl、Alt、Shift 等)。
0×01 表示左 Ctrl 按下。
0×02 表示左 Shift 按下。
0×04 表示左 Alt 按下。
依此类推。
2.第 2 个字节(buff[1]):保留字节,通常为 0,不使用。
3.第 3 到第 8 个字节(buff[2] - buff[7]):按键代码,最多可以同时报告 6 个按键。
例如:
0×04 表示字母 ‘A’ 键。
0×05 表示字母 ‘B’ 键。
依此类推。
以下是一个简单的键盘使用发送函数示例:A-Z
`void USB_DevUserApplication(void)
{
static uint8_t userAppState = USER_APP_INIT;
static uint8_t interval = 0;
static uint8_t report[8] = { 0 };
static uint8_t i = 4;
switch (userAppState)
{
case USER_APP_INIT:
interval = USBD_CUSTOM_HID_ReadInterval(&gUsbDeviceFS);
report[0] = 0;
report[1] = 0;
report[2] = 0;
report[3] = 0;
userAppState = USER_APP_RUN;
break;
case USER_APP_RUN:
if (!APM_MINI_PBGetState(BUTTON_KEY1))
{
APM_DelayMs(10);
if (!APM_MINI_PBGetState(BUTTON_KEY1))
{
if(i > 29)
{
i = 4;
report[2] = KEYBOARD_ENTER;
USBD_CUSTOM_HID_TxReport(&gUsbDeviceFS, (uint8_t*)report, 8);
APM_DelayMs(20);
report[2] = 0;
USBD_CUSTOM_HID_TxReport(&gUsbDeviceFS, (uint8_t*)report, 8);
APM_DelayMs(20);
}
report[2] = i;
USBD_CUSTOM_HID_TxReport(&gUsbDeviceFS, (uint8_t*)report, 8);
APM_DelayMs(20);
report[2] = 0;
USBD_CUSTOM_HID_TxReport(&gUsbDeviceFS, (uint8_t*)report, 8);
i++;
while(!APM_MINI_PBGetState(BUTTON_KEY1));
}
}
APM_DelayMs(interval);
break;
}
}`
现在我们将它结合起来向USB主机发送数据的时候就需要做出改变了,现在的数组的第一个元素是报告ID,后面才是键盘数据或鼠标数据。
在上面的两个示例中,USB键盘我们定义了一个8元素的数组,USB鼠标我们定义了一个4元素的数组。
现在我们需要一个数组,在同一时间的时候只发送一种数据,再加上报告ID,所以变成9个.
以下是一个简单的键+鼠标使用发送函数示例
`void USB_DevUserApplication(void)
{
static uint8_t userAppState = USER_APP_INIT;
static uint8_t interval = 0;
uint8_t KeyBoard[9] = {1,0,0,0,0,0,0,0,0};
uint8_t Mouse[9] = {2,0,0,0,0,0,0,0,0};
static uint8_t i = 4;
switch (userAppState)
{
case USER_APP_INIT:
interval = USBD_HID_ReadInterval(&gUsbDeviceFS);
KeyBoard[0] = 0;
KeyBoard[1] = 0;
KeyBoard[2] = 0;
KeyBoard[3] = 0;
userAppState = USER_APP_RUN;
break;
case USER_APP_RUN:
if (!APM_MINI_PBGetState(BUTTON_KEY1))
{
APM_DelayMs(10);
if (!APM_MINI_PBGetState(BUTTON_KEY1))
{
if(i > 29)
{
i = 4;
KeyBoard[3] = KEYBOARD_ENTER;
USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)KeyBoard, sizeof(KeyBoard));
APM_DelayMs(20);
KeyBoard[3] = 0;
USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)KeyBoard, sizeof(KeyBoard));
APM_DelayMs(20);
}
KeyBoard[3] = i;
USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)KeyBoard, sizeof(KeyBoard));
APM_DelayMs(20);
KeyBoard[3] = 0;
USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)KeyBoard, sizeof(KeyBoard));
i++;
while(!APM_MINI_PBGetState(BUTTON_KEY1));
}
}
if (!APM_MINI_PBGetState(BUTTON_KEY2))
{
APM_DelayMs(10);
if (!APM_MINI_PBGetState(BUTTON_KEY2))
{
Mouse[3] -= 10;
USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)Mouse, sizeof(Mouse));
}
}
APM_DelayMs(interval);
break;
}
}`