【USB-HID】“自动化键盘“ - 模拟键盘输入

目录

  • [【USB-HID】"自动化键盘" - 模拟键盘输入](#【USB-HID】"自动化键盘" - 模拟键盘输入)
    • [1. 前言](#1. 前言)
    • [2. 模拟键盘](#2. 模拟键盘)
      • [2.1 STM32CubeMX 配置](#2.1 STM32CubeMX 配置)
      • [2.2 修改代码配置](#2.2 修改代码配置)
      • [2.3 发送按键信息](#2.3 发送按键信息)
    • [3. 接收主机Setup数据](#3. 接收主机Setup数据)
      • [3.1 获取PC下发的数据](#3.1 获取PC下发的数据)
    • [4. 总结](#4. 总结)

【USB-HID】"自动化键盘" - 模拟键盘输入

1. 前言

对于模拟键盘的实现,网上有很多的教程说明,我参考了网上的例程,很轻松的实现了模拟键盘的功能,实现了按键的输入,这篇文章我的主要目的是记录我的实现过程。

2. 模拟键盘

硬件:STM32F04 开发板

软件:STM32CubeMX + Keil5

2.1 STM32CubeMX 配置

只需要按照如图配置选择即可。





2.2 修改代码配置

  1. 修改协议类型:nInterfaceProtocol改为keyboard
  2. 修改报告描述长度:HID_MOUSE_REPORT_DESC_SIZE 改为 63
  3. 修改报告描述符:如下代码块所示

USB协议中这些配置信息是重点,后面需要深入去了解。

c 复制代码
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
    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)
    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
};

2.3 发送按键信息

首先实现一个发送按键数据的函数,按键数据一共8个字节,每个字节的描述如下所示,字节0每个Bit代表特殊按键,字节2-7每个字节代表一个普通按键,按键的键值码去网上搜一下就有,或者去找USB-HID协议文档看一下。然后再实现一个按键松开的函数,这里所有数据发送0表示所有按键都松开,初步先这样实现,后续再改。接着在main函数中调用,实现按键按下时发送组合键【Ctrl + A】。从Bus Hound监控的数据我们可以看到,数据【01 00 04 00 00 00 00 00】即为我们下位机发送的按键数据。

c 复制代码
void MX_USB_KEYBOARD_PRESS(uint8_t *sendBuffer)
{
	/*
	 * buffer[0] - bit0: Left CTRL
	 *           -bit1: Left SHIFT
	 *           -bit2: Left ALT
	 *           -bit3: Left GUI(win)
	 *           -bit4: Right CTRL
	 *           -bit5: Right SHIFT
	 *           -bit6: Right ALT
	 *           -bit7: Right GUI ()
	 * buffer[1] - Padding = Always 0x00
	 * buffer[2] - Key 1
	 * buffer[3] - Key 2
	 * buffer[4] - Key 3
	 * buffer[5] - Key 4
	 * buffer[6] - Key 5
	 * buffer[7] - Key 6
	 */
	USBD_HID_SendReport(&hUsbDeviceFS, sendBuffer, 8);
	HAL_Delay(20);
}

void MX_USB_KEYBOARD_RELEASE(void)
{
	uint8_t buffere_clear[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	USBD_HID_SendReport(&hUsbDeviceFS, buffere_clear, sizeof(buffere_clear));
	HAL_Delay(20);
}

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();

  while (1)
  {
	  if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_2))
	  {
		  void MX_USB_KEYBOARD_PRESS(uint8_t *sendBuffer);
		  void MX_USB_KEYBOARD_RELEASE(void);
		  uint8_t buffere[8] = {0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00};
		  MX_USB_KEYBOARD_PRESS(buffere);
		  MX_USB_KEYBOARD_RELEASE();
	  }
  }
}

3. 接收主机Setup数据

在学习别人的教程的过程中,我了解到按下【caps lock】 / 【num clk】这些按键的时候,PC会下发请求控制点灯。也就是说我们下位机发送【caps lock】按键指令就能控制笔记本上的【caps lock】按键灯,下位机发送【caps lock】指令给PC后,PC会给键盘下发一个Setup请求,并且跟随一个字节的数据,这个数据是发给断点0。,该字节数据中的每个位代表这些按键的信息,如下表所示。

我们将程序按键改为操作【caps lock】按键,程序运行后,按下按键,我们可以看到电脑上的指示灯会跟着变化。

c 复制代码
	  if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_2))
	  {
		  void MX_USB_KEYBOARD_PRESS(uint8_t *sendBuffer);
		  uint8_t buffere[8] = {0x00, 0x00, KEYBOARD_CAPS_LOCK, 0x00, 0x00, 0x00, 0x00, 0x00};
		  MX_USB_KEYBOARD_PRESS(buffere);
	  }
	  else
	  {
		  void MX_USB_KEYBOARD_RELEASE(void);
		  MX_USB_KEYBOARD_RELEASE();
	  }

我们按下笔记本上的【caps lock】按键,可以观察到下发的数据中对应的Bit位实现翻转。

通过仿真我们可以看到下位机接收的数据与上位机下发的是一致的。

3.1 获取PC下发的数据

这里如何获取PC下发的Setup数据,我找了很多帖子,基本都是介绍只发不收,最终我还是找到了如何获取数据的方法,这里记录一下。因为PC下发的是一个Class类型的Steup请求,所以我们要在USBD_HID_Setup回调函数中实现HID_REQ_SET_REPORT的处理,通过USBD_CtlPrepareRx获取数据,然后再回调给应用层,这里MX_USB_HID_Set_Reporter_CallBack函数再应用层中实现即可。(没做过USB协议,我不确定是不是这样实现的,或者这个库本身就支持?)

4. 总结

不懂USB协议的前提下,照着网上依样画葫芦还是不容易的,看似别人的例程简单,但是都五花八门,甚至很多都是错的,比如键盘的报告描述符配置成63个,但是实际数组中的个数都超过63个了,编译都不过。有些还说接收PC端发送【caps lock】按键的数据还要配置输出端口,结果我赔了USB通讯都报Error了,真的很无语。建议这些配置一开始就去参考USB-HID协议文档,文档中都有给出。实在懒得看去问问AI至少靠谱一点。实现之后还要去深入了解这些配置的含义。

相关推荐
电子云与长程纠缠15 分钟前
UE5编辑器下将RenderTarget输出为UTexture并保存
学习·ue5·编辑器·虚幻
武汉芯源半导体24 分钟前
方案介绍|CW32L010安全低功耗MCU:驱动高速风筒新力量
单片机·嵌入式硬件
violet_evergarden.30 分钟前
【51单片机】独立按键快速上手
笔记·单片机·嵌入式硬件·51单片机
云山工作室1 小时前
基于单片机的农田灌溉系统(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
马船长1 小时前
CTFHub 技能树 Web RCE eval执行(学习记录)
学习
python百炼成钢2 小时前
14-STM32 PWM脉冲宽度调制
stm32·单片机·嵌入式硬件
dal118网工任子仪2 小时前
32.攻防世界Web_php_include
笔记·学习·安全·web安全·网络安全
加德霍克3 小时前
Opencv之图像梯度处理和绘制图像轮廓
图像处理·人工智能·opencv·学习·计算机视觉
不当菜鸡的程序媛3 小时前
GAN 是如何学习语义的?
人工智能·学习·生成对抗网络
LateBloomer7773 小时前
顺序队列的实现及其应用
数据结构·笔记·学习·循环队列