返回列表 发新帖我要提问本帖赏金: 40.00元(功能说明)

[APM32F0] APM32F072 USB键盘+鼠标

[复制链接]
 楼主| Peixu 发表于 2024-7-29 10:32 | 显示全部楼层 |阅读模式
<
本帖最后由 Peixu 于 2024-8-2 09:55 编辑

[url=home.php?mod=space&uid=760190]@21小跑堂 #申请原创#[/url]
这次给大家介绍一下如何用APM32F072的USB HID配置一个 USB键盘+鼠标 复合设备的过程。
首先我们需要1个USB键盘的例程(官方有例程)
第二步我们需要1个USB 鼠标的例程  (官方有例程)
第三步将他们结合起来变成USB 键盘+鼠标
6c3134e8113fa3cee16a342cd24e0b46
这里我们用USB鼠标的例程基础上添加键盘的功能。

第亿、打开USB_HID例程,修改USBD_ConfigDesc中的(USBD_CONFIG_DESCRIPTOR_SIZE)。
如下图所示的宏定义。配置集合的长度,由之前的34,改为41.
b533796f9ecc481e7c20be8e9197286c
9f543a4dbe6714d8498e7f72957a06a7
第二、USBD_ConfigDesc函数继续往下走找到
1、端点个数:由0x01变成0x02.
2、接口协议:由0x02(鼠标)变成0x01(键盘)。
这里主要还是以键盘功能为主,鼠标只是以一个附属功能加入到了键盘里。
945a29c3074c0df52e094d6ac24f94fa
3、在原来的鼠标例程上的最下面增加了一个输出端点的描述符:
69d7d43080d169dfe97005ddfd365bf7
第三,在usbd_hid.c修改USBD_HIDReportDesc函数报告描述符的长度:之前这里是鼠标的报告描述符,长度是74 改成117
dc4dd1cdba41ca1aa2d9ed882e34164e
40074b3b6b7c995465389f9c22a12b2f
接下来就是将键盘和鼠标的报告描述符合并为一个数组,分别在键盘和鼠标的报告描述符中放一个报告ID,
键盘的报告ID是1,鼠标的报告ID是2。这样USB主机就可以区分开收到的数据是键盘或是鼠标的数据。
c86a8bcfaf5b2922806966b45974dc75
47a6a32086f6b388f43f59914f6f3a18
这是加入后HID键盘+鼠标里的完整报告描述符
  1. uint8_t USBD_HIDReportDesc[USBD_HID_MOUSE_REPORT_DESC_SIZE] =
  2. {

  3.     0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
  4.     0x09, 0x06,                    // USAGE (Keyboard)
  5.     0xa1, 0x01,                    // COLLECTION (Application)
  6.     0x85, 0x01,                    //   REPORT_ID (1)
  7.     0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
  8.     0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
  9.     0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
  10.     0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
  11.     0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
  12.     0x75, 0x01,                    //   REPORT_SIZE (1)
  13.     0x95, 0x08,                    //   REPORT_COUNT (8)
  14.     0x81, 0x02,                    //   INPUT (Data,Var,Abs)
  15.     0x95, 0x01,                    //   REPORT_COUNT (1)
  16.     0x75, 0x08,                    //   REPORT_SIZE (8)
  17.     0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
  18.     0x95, 0x05,                    //   REPORT_COUNT (5)
  19.     0x75, 0x01,                    //   REPORT_SIZE (1)
  20.     0x05, 0x08,                    //   USAGE_PAGE (LEDs)
  21.     0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
  22.     0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
  23.     0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
  24.     0x95, 0x01,                    //   REPORT_COUNT (1)
  25.     0x75, 0x03,                    //   REPORT_SIZE (3)
  26.     0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
  27.     0x95, 0x06,                    //   REPORT_COUNT (6)
  28.     0x75, 0x08,                    //   REPORT_SIZE (8)
  29.     0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
  30.     0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
  31.     0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
  32.     0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
  33.     0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
  34.     0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
  35.     0xc0,                           // END_COLLECTION
  36.                
  37.     0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
  38.     0x09, 0x02,                    // USAGE (Mouse)
  39.     0xa1, 0x01,                    // COLLECTION (Application)
  40.     0x85, 0x02,                    //   REPORT_ID (2)
  41.     0x09, 0x01,                    //   USAGE (Pointer)
  42.     0xa1, 0x00,                    //   COLLECTION (Physical)
  43.     0x05, 0x09,                    //     USAGE_PAGE (Button)
  44.     0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
  45.     0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
  46.     0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
  47.     0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
  48.     0x95, 0x03,                    //     REPORT_COUNT (3)
  49.     0x75, 0x01,                    //     REPORT_SIZE (1)
  50.     0x81, 0x02,                    //     INPUT (Data,Var,Abs)
  51.     0x95, 0x01,                    //     REPORT_COUNT (1)
  52.     0x75, 0x05,                    //     REPORT_SIZE (5)
  53.     0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
  54.     0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
  55.     0x09, 0x30,                    //     USAGE (X)
  56.     0x09, 0x31,                    //     USAGE (Y)
  57.     0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
  58.     0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
  59.     0x75, 0x08,                    //     REPORT_SIZE (8)
  60.     0x95, 0x02,                    //     REPORT_COUNT (2)
  61.     0x81, 0x06,                    //     INPUT (Data,Var,Rel)
  62.     0xc0,                          //   END_COLLECTION
  63.     0xc0                           // END_COLLECTION
  64. };

这样,我们USB键盘+鼠标二合一就改好了, 那么需要怎么使用呢?
第三、鼠标和键盘的使用属性简单介绍:
1、Mouse 鼠标:
我们用的鼠标主要就四个属性:键位(左键、右键、中键),左右移动、上下移动、滑轮
第 1 个字节(buff[0]):按钮状态    0x01左键   0x02右键   0x04中键(滚轮按钮)
第 2 个字节(buff[1]):X 轴移动量
第 3 个字节(buff[2]):Y 轴移动量
第 4 个字节(buff[3]):滚轮移动量  0x01表示滚轮向前滚动一格;0xFF表示滚轮向后滚动一格;0x80是个中间值,不滚动。
以下是一个简单的鼠标使用发送函数示例:
  1. void HidMouse_Write(uint8_t key)
  2. {
  3.     int8_t x = 0;
  4.     int8_t y = 0;
  5.        
  6.     int8_t Button = 0;
  7.     int8_t Wheel = 0;
  8.        
  9.     uint8_t buffer[4] = {0, 0, 0, 0};

  10.     switch (key)
  11.     {
  12.         case HID_MOUSE_KEY_LEFT:
  13.             x -= 10;                       
  14.         break;

  15.         case HID_MOUSE_KEY_RIGHT:
  16.             x += 10;
  17.         break;

  18.         case HID_MOUSE_KEY_UP:
  19.             y -= 10;               
  20.         break;

  21.         case HID_MOUSE_KEY_DOWN:
  22.             y += 10;
  23.         break;

  24.         case HID_MOUSE_BUTTON:
  25.             Button = 0x01;                        //0x01左键   0x02右键   0x04中键(滚轮按钮)
  26.         break;
  27.                                
  28.         case HID_MOUSE_WHEEL:
  29.             Wheel = 0xff;                //0x01表示滚轮向前滚动一格;0xFF表示滚轮向后滚动一格;0x80是个中间值,不滚动。
  30.         break;                               
  31.         default:
  32.             return;
  33.     }
  34.     buffer[0] = Button;
  35.     buffer[1] = x;
  36.     buffer[2] = y;
  37.     buffer[3] = Wheel;
  38.                
  39.     s_statusEP = 0;

  40.     USBD_TxData(USBD_EP_1, buffer, sizeof(buffer));
  41. }

2、Keyboard 键盘:
在键盘 HID 报告格式中,每个字节代表不同的含义:
第 1 个字节(buff[0]) 修饰键的状态(如 Ctrl、Alt、Shift 等)。
0x01 表示左 Ctrl 按下。
0x02 表示左 Shift 按下。
0x04 表示左 Alt 按下。
依此类推。
第 2 个字节(buff[1]):保留字节,通常为 0,不使用。
第 3 到第 8 个字节(buff[2] - buff[7]):按键代码,最多可以同时报告 6 个按键。
例如:
0x04 表示字母 'A' 键。
0x05 表示字母 'B' 键。
依此类推。
以下是一个简单的键盘使用发送函数示例:A-Z
  1. void USB_DevUserApplication(void)
  2. {
  3.     static uint8_t userAppState = USER_APP_INIT;
  4.     static uint8_t interval = 0;
  5.     static uint8_t report[8] = { 0 };
  6.     static uint8_t i = 4;

  7.     switch (userAppState)
  8.     {
  9.         case USER_APP_INIT:
  10.             interval = USBD_CUSTOM_HID_ReadInterval(&gUsbDeviceFS);

  11.             report[0] = 0;
  12.             report[1] = 0;
  13.             report[2] = 0;
  14.             report[3] = 0;
  15.             userAppState = USER_APP_RUN;
  16.             break;

  17.         case USER_APP_RUN:
  18.             if (!APM_MINI_PBGetState(BUTTON_KEY1))
  19.             {
  20.                 APM_DelayMs(10);
  21.                 if (!APM_MINI_PBGetState(BUTTON_KEY1))
  22.                 {
  23.                     if(i > 29)
  24.                     {
  25.                         i = 4;
  26.                         report[2] = KEYBOARD_ENTER;
  27.                         USBD_CUSTOM_HID_TxReport(&gUsbDeviceFS, (uint8_t*)report, 8);
  28.                         APM_DelayMs(20);
  29.                         report[2] = 0;
  30.                         USBD_CUSTOM_HID_TxReport(&gUsbDeviceFS, (uint8_t*)report, 8);
  31.                         APM_DelayMs(20);
  32.                     }
  33.                     
  34.                     report[2] = i;
  35.                     USBD_CUSTOM_HID_TxReport(&gUsbDeviceFS, (uint8_t*)report, 8);
  36.                     
  37.                     APM_DelayMs(20);
  38.                     
  39.                     report[2] = 0;
  40.                     USBD_CUSTOM_HID_TxReport(&gUsbDeviceFS, (uint8_t*)report, 8);
  41.                     
  42.                     i++;
  43.                     
  44.                     while(!APM_MINI_PBGetState(BUTTON_KEY1));
  45.                 }
  46.             }

  47.             APM_DelayMs(interval);
  48.             break;
  49.     }
  50. }

现在我们将它结合起来向USB主机发送数据的时候就需要做出改变了,现在的数组的第一个元素是报告ID,后面才是键盘数据或鼠标数据。
在上面的两个示例中,USB键盘我们定义了一个8元素的数组,USB鼠标我们定义了一个4元素的数组。
现在我们需要一个数组,在同一时间的时候只发送一种数据,再加上报告ID,所以变成9个.
以下是一个简单的键+鼠标使用发送函数示例
  1. void USB_DevUserApplication(void)
  2. {
  3.     static uint8_t userAppState = USER_APP_INIT;
  4.     static uint8_t interval = 0;

  5.     uint8_t KeyBoard[9] = {1,0,0,0,0,0,0,0,0};
  6.     uint8_t Mouse[9] = {2,0,0,0,0,0,0,0,0};
  7.     static uint8_t i = 4;               

  8.     switch (userAppState)
  9.     {
  10.         case USER_APP_INIT:
  11.             interval = USBD_HID_ReadInterval(&gUsbDeviceFS);

  12.             KeyBoard[0] = 0;
  13.             KeyBoard[1] = 0;
  14.             KeyBoard[2] = 0;
  15.             KeyBoard[3] = 0;
  16.             userAppState = USER_APP_RUN;
  17.             break;

  18.         case USER_APP_RUN:
  19.             if (!APM_MINI_PBGetState(BUTTON_KEY1))
  20.             {
  21.                 APM_DelayMs(10);
  22.                 if (!APM_MINI_PBGetState(BUTTON_KEY1))
  23.                 {
  24.                     if(i > 29)
  25.                     {
  26.                         i = 4;
  27.                         KeyBoard[3] = KEYBOARD_ENTER;
  28.                         USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)KeyBoard, sizeof(KeyBoard));
  29.                         APM_DelayMs(20);
  30.                         KeyBoard[3] = 0;
  31.                         USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)KeyBoard, sizeof(KeyBoard));
  32.                         APM_DelayMs(20);
  33.                     }
  34.                                                                                 KeyBoard[3] = i;
  35.                     USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)KeyBoard, sizeof(KeyBoard));
  36.                     
  37.                     APM_DelayMs(20);
  38.                     
  39.                     KeyBoard[3] = 0;
  40.                     USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)KeyBoard, sizeof(KeyBoard));
  41.                     
  42.                     i++;
  43.                     
  44.                     while(!APM_MINI_PBGetState(BUTTON_KEY1));
  45.                 }
  46.             }

  47.             if (!APM_MINI_PBGetState(BUTTON_KEY2))
  48.             {
  49.                 APM_DelayMs(10);
  50.                 if (!APM_MINI_PBGetState(BUTTON_KEY2))
  51.                 {
  52.                                                                                 Mouse[3] -= 10;
  53.                     USBD_HID_TxReport(&gUsbDeviceFS, (uint8_t*)Mouse, sizeof(Mouse));
  54.                 }
  55.             }

  56.             APM_DelayMs(interval);
  57.             break;
  58.     }
  59. }


打赏榜单

21小跑堂 打赏了 40.00 元 2024-07-31
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

基于M32F072单片机移植USB键盘加鼠标复合驱动,过程讲解详细,代码展出完整。  发表于 2024-7-31 17:38
gouguoccc 发表于 2024-8-1 08:13 来自手机 | 显示全部楼层
我晕,一大饼代码还有乱码,看的心累啊。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

32

主题

58

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部