例程:STM32USBdevice: 基于STM32的USB设备例子程序 - Gitee.com
键鼠一体的应用场景如集合器,比如我们在网上购买的键鼠套装,是将2.4G集合到一个USB接收器中,这个USB接收器对于PC来说就是一个键鼠一体设备。或者我们可以自制一个带有鼠标摇杆的键盘等场景。
键鼠一体实现方式是在报告描述符中描述两个设备,即键盘设备和鼠标设备,报告描述就是将键盘描述和鼠标描述何为一个,那么PC如何区分这两个报告呢?通过REPORT_ID来区分,下面展示一下使用DT工具生成的报告描述符。
其中键盘REPORT_ID为1,鼠标REPORT_ID为2.在软件发送消息时,使用 REPORT_ID 作为报告的第一个字节,可以让主机准确区分不同类型的报告。也就是我们需要在额外的报告消息中添加第一个字节为REPORT_ID即可。
我们在keyboard代码的基础上进行修改。
前置操作步骤可以看我上一篇关于keyboard的教程,这篇我们之列出差异。
首先我们需要将上面的报告描述符添加到代码Middlewares\ST\STM32_USB_Device_Library\Class\HID\Src\usbd_hid.c中,
__ALIGN_BEGIN static uint8_t HID_KEYMOUSE_ReportDesc[HID_KEYMOUSE_REPORT_DESC_SIZE] __ALIGN_END = {
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)
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
};
将代码中HID_KEYBOARD_REPORT_DESC_SIZE替换为HID_KEYMOUSE_REPORT_DESC_SIZE,将HID_KEYBOARD_REPORT_DESC_SIZE替换为HID_KEYMOUSE_REPORT_DESC_SIZE。
在Middlewares\ST\STM32_USB_Device_Library\Class\HID\Inc\usbd_hid.h中定义HID_KEYMOUSE_REPORT_DESC_SIZE
#define HID_KEYMOUSE_REPORT_DESC_SIZE 119U
这里我们就把USB描述符相关的修改完成了,剩下我们就是编写测试逻辑。
在Core\Src\freertos.c文件中添加鼠标结构体
/* USER CODE BEGIN PTD */
/* 鼠标报文结构体 */
struct mouseHID_t {
uint8_t reportId;
uint8_t buttons;
int8_t x;
int8_t y;
int8_t wheel;
};
/* USER CODE END PTD */
第一个字节为report ID,固定为2,因为报告描述符是这样定义的。
在测试任务中代码如下
void StartDefaultTask(void *argument)
{
/* init code for USB_DEVICE */
MX_USB_DEVICE_Init();
/* USER CODE BEGIN StartDefaultTask */
uint8_t keyBoard[9] = {1, 0, 0, 0, 0, 0, 0, 0, 0};
uint8_t key1Status = 0;
uint8_t key2Status = 0;
TickType_t xLastFlashTime = osKernelGetTickCount();
/* Infinite loop */
for (;;) {
// 获取当前时间戳
TickType_t xCurrentTime = osKernelGetTickCount();
// 检查是否已经过了 1 秒(pdMS_TO_TICKS 函数将毫秒转换为系统时钟节拍)
if ((xCurrentTime - xLastFlashTime) >= pdMS_TO_TICKS(1000)) {
// 切换 LED1 的状态
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
// HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
// 更新上一次的时间戳
xLastFlashTime = xCurrentTime;
}
if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET) {
if (key1Status == 0) {
keyBoard[3] = 0x39; // 设置caps lock按键状态
USBD_HID_SendReport(&hUsbDeviceFS, keyBoard, sizeof(keyBoard));
key1Status = 1;
}
} else {
if (key1Status == 1) {
key1Status = 0;
keyBoard[3] = 0; // 清除按键状态
USBD_HID_SendReport(&hUsbDeviceFS, keyBoard, sizeof(keyBoard));
}
}
if (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_SET) {
/* 发送鼠标报文(一直发送该报文,鼠标会水平向右移动) */
struct mouseHID_t mouseHID;
mouseHID.reportId = 2;
mouseHID.buttons = 0b00001000;
mouseHID.x = -10;
mouseHID.y = 0;
mouseHID.wheel = 0;
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t *)&mouseHID, sizeof(struct mouseHID_t));
}
osDelay(1);
}
键盘数组改为了uint8_t keyBoard[9] = {1, 0, 0, 0, 0, 0, 0, 0, 0};,第一字节为1,必须与报告描述符中定义相同。
按下按键1会切换大小写,这时我们的键盘灯会跟着变化。按键2按下时鼠标会向左移动。