【问题标题】:How to implement Left and Right Analog Triggers in a HID Gamepad for Android如何在 Android 的 HID 游戏手柄中实现左右模拟触发器
【发布时间】:2020-05-26 15:06:32
【问题描述】:

我正在开发一个带有 4 轴(14 位)、16 个按钮、2 个模拟触发器(1 个字节)和一个帽子开关的 HID 游戏手柄。我目前将 X 轴和 Y 轴用于左侧模拟摇杆,将 Rx 轴和 Ry 轴用于右侧模拟摇杆,将 Z 轴和 Rz 轴用于左右触发器。我能够注册所有按钮和模拟读数(已在 android 中使用 Gamepad tester 和在 linux 中使用 jstest-gtk 进行了测试),但问题是在(Android)游戏中,触发器应该是轴 7 和轴 8在我的例子中,Z 和 Rz 被分配了轴 14 和 15。我也尝试过使用 Acceleration(axis:11)、Brake(axis:12)、Rudder(axis:9) 和 Throttle(axis:10) 但轴7 和 8(默认触发轴)未分配。

这是我的 HID 报告描述符:-

0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
0x09, 0x05,                    // USAGE (Game Pad)
0xa1, 0x01,                    // COLLECTION (Application)

0x85, 0x01,                    //   REPORT_ID (1)
0x09, 0x01,                    //   USAGE (Pointer)
0xa1, 0x00,                    //   COLLECTION (Physical)
0x09, 0x30,                    //     USAGE (X) - Left Analog Left(-ve),Right(+ve)
0x09, 0x31,                    //     USAGE (Y) - Left Analog Up(-ve), Down(+ve)
0x09, 0x33,                    //     USAGE (Rx) - Right Analog Left(-ve), Right(+ve)
0x09, 0x34,                    //     USAGE (Ry) - Right Analog Up(-ve), Down(+ve)
0x16, 0x01, 0xE0,              //     LOGICAL_MINIMUM (-8191)
0x26, 0xFF, 0x1F,              //     LOGICAL_MAXIMUM (8191)        
0x75, 0x10,                    //     REPORT_SIZE (16)
0x95, 0x04,                    //     REPORT_COUNT (4)
0x81, 0x02,                    //     INPUT (Data,Var,Abs)
0xc0,                          //   END_COLLECTION
0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop) 
0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x00,              //     LOGICAL_MAXIMUM (255)
0x09, 0x32,                    //     USAGE (Z) - L2 Trigger(Shoulderpads Trigger)
0x09, 0x35,                    //     USAGE (Rz) - R2 Trigger(Shoulderpads Trigger)    
0x75, 0x08,                    //     REPORT_SIZE (8)    
0x95, 0x02,                    //     REPORT_COUNT (2)    
0x81, 0x02,                    //     INPUT (Data,Var,Abs)    

0x05, 0x09,                    //   USAGE_PAGE (Button)
0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)    
0x29, 0x10,                    //   USAGE_MAXIMUM (Button 16)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)     
0x95, 0x10,                    //   REPORT_COUNT (16)
0x75, 0x01,                    //   REPORT_SIZE (1)
0x81, 0x02,                    //   INPUT (Data,Var,Abs)

0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
0x09, 0x39,                    //   USAGE (Hat switch)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x07,                    //   LOGICAL_MAXIMUM (7)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x46, 0x3b, 0x01,              //   PHYSICAL_MAXIMUM (315)
0x65, 0x14,                    //   UNIT (Eng Rotation:Centimeter)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x81, 0x43,                    //   INPUT (Cnst,Var,Abs,Null)

0xc0                           // END_COLLECTION

任何帮助将不胜感激,谢谢。

编辑:- 这是我根据Nipo指定的Anrdoid CDD文档尝试的新报告描述符:-

0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
0x09, 0x05,                    // USAGE (Game Pad)
0xa1, 0x01,                    // COLLECTION (Application)
0x85, 0x01,                    //   REPORT_ID (1)
0x09, 0x01,                    //   USAGE (Pointer)
0xa1, 0x00,                    //   COLLECTION (Physical)
0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
0x09, 0x30,                    //     USAGE (X) - Left Analog Left(-ve),Right(+ve)
0x09, 0x31,                    //     USAGE (Y) - Left Analog Up(-ve), Down(+ve)
0x09, 0x32,                    //     USAGE (Z) - Right Analog X
0x09, 0x35,                    //     USAGE (Rz) - Right Analog Y
0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
0x25, 0x7F,                    //     LOGICAL_MAXIMUM (127)       
0x75, 0x08,                    //     REPORT_SIZE (8)
0x95, 0x04,                    //     REPORT_COUNT (4)
0x81, 0x02,                    //     INPUT (Data,Var,Abs)
0x05, 0x02,                    // USAGE_PAGE (Simulation Control)            
0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x00,              //     LOGICAL_MAXIMUM (255)        
0x09, 0xC4,                    //     USAGE(Acceleration)
0x09, 0xC5,                    //     USAGE(Brake)        
0x75, 0x08,                    //     REPORT_SIZE (8)
0x95, 0x02,                    //     REPORT_COUNT (2)    
0x81, 0x02,                    //     INPUT (Data,Var,Abs)
0x05, 0x09,                    //   USAGE_PAGE (Button)
0x09, 0x01,                    //   USAGE(Button 1)
0x09, 0x02,                    //   USAGE(Button 2)
0x09, 0x04,                    //   USAGE(Button 4)
0x09, 0x05,                    //   USAGE(Button 5)
0x09, 0x07,                    //   USAGE(Button 7)
0x09, 0x08,                    //   USAGE(Button 8)
0x09, 0x0E,                    //   USAGE(Button 14)
0x09, 0x0F,                    //   USAGE(Button 15)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)     
0x95, 0x08,                    //   REPORT_COUNT (8)
0x75, 0x01,                    //   REPORT_SIZE (1)
0x81, 0x02,                    //   INPUT (Data,Var,Abs)           
0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
0x09, 0x39,                    //   USAGE (Hat switch)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x07,                    //   LOGICAL_MAXIMUM (7)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x46, 0x3b, 0x01,              //   PHYSICAL_MAXIMUM (315)
0x65, 0x14,                    //   UNIT (Eng Rotation:Centimeter)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
0x65, 0x00,                    // Unit (None)
/*!@ The below section is taken from the page 31 of the document given
  in the link below.
  https://www.silabs.com/documents/public/application-notes/AN993.pdf
 */
0x05, 0x0C,                    //   USAGE_PAGE (Consumer)
0x0A, 0x23, 0x02,              //   USAGE (AC Home)
0x0A, 0x24, 0x02,              //   USAGE (AC Back)
0x75, 0x01,                    //   REPORT_SIZE(1)
0x95, 0x02,                    //   REPORT_COUNT(2)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x45, 0x01,                    //   PHYSICAL_MAXIMUM (1)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
0x95, 0x01,                    //   REPORT_COUNT (2)
0x75, 0x02,                    //   REPORT_SIZE (1)
0x81, 0x43,                    //   INPUT (Cnst,Var,Abs,Null)
0xc0,                          //   END_COLLECTION
0xc0,                          //   END_COLLECTION

【问题讨论】:

  • 你看过Android CDD的第7.2.6.1段吗?
  • @Nipo 感谢回复,我会测试文档中给出的配置并贴出结果,再次感谢
  • @Nipo 嗨,尝试了文档中的描述符配置,但轴(0x00C4-AXIS_RTRIGGER/ACCELERATION 和 0x00C5-AXIS_LTRIGGER/BRAKE)被检测为轴 12 和轴 13。文档明确指定使用这两个轴,所以我认为描述符有问题。

标签: android c hid gamepad


【解决方案1】:

这是对我有用的报告描述符:-

0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
0x09, 0x05,                    // USAGE (Game Pad)
0xa1, 0x01,                    // COLLECTION (Application)    

0x85, 0x01,                    //   REPORT_ID (1)
0x09, 0x01,                    //   USAGE (Pointer)
0xa1, 0x00,                    //   COLLECTION (Physical)    
0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
0x09, 0x30,                    //     USAGE (X) - Left Analog Left(-ve),Right(+ve)
0x09, 0x31,                    //     USAGE (Y) - Left Analog Up(-ve), Down(+ve)    
0x09, 0x32,                    //     USAGE (Z) - Right Analog Left(-ve)Right(+ve)
0x09, 0x35,                    //     USAGE (Rz)- Right Analog Up(-ve), Down(+ve)
0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
0x75, 0x08,                    //     REPORT_SIZE (8)
0x95, 0x04,                    //     REPORT_COUNT (4)
0x81, 0x02,                    //     INPUT (Data,Var,Abs)
0xc0,                          //   END_COLLECTION

0x05, 0x02,                    //     USAGE_PAGE (Simulation Control)
0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x00,              //     LOGICAL_MAXIMUM (255)
0x09, 0xC4,                    //     USAGE(Acceleration)
0x09, 0xC5,                    //     USAGE(Brake)           
0x75, 0x08,                    //     REPORT_SIZE (8)
0x95, 0x02,                    //     REPORT_COUNT (2)
0x81, 0x02,                    //     INPUT (Data,Var,Abs)

0x05, 0x09,                    //   USAGE_PAGE (Button)
0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)
0x29, 0x10,                    //   USAGE_MAXIMUM (Button 16)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
0x95, 0x10,                    //   REPORT_COUNT (16)
0x75, 0x01,                    //   REPORT_SIZE (1)
0x81, 0x02,                    //   INPUT (Data,Var,Abs)

0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
0x09, 0x39,                    //   USAGE (Hat switch)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x07,                    //   LOGICAL_MAXIMUM (7)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x46, 0x3b, 0x01,              //   PHYSICAL_MAXIMUM (315)
0x65, 0x14,                    //   UNIT (Eng Rotation:Centimeter)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x81, 0x43,                    //   INPUT (Cnst,Var,Abs,Null)
0x65, 0x00,                    //   Unit (None)

0x05, 0x0C,                    //   USAGE_PAGE (Consumer)
0x0A, 0x23, 0x02,              //   USAGE (AC Home)
0x0A, 0x24, 0x02,              //   USAGE (AC Back)
0x75, 0x01,                    //   REPORT_SIZE(1)
0x95, 0x02,                    //   REPORT_COUNT(2)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x45, 0x01,                    //   PHYSICAL_MAXIMUM (1)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)

0x95, 0x01,                    //   REPORT_COUNT (2)
0x75, 0x02,                    //   REPORT_SIZE (1)
0x81, 0x43,                    //   INPUT (Cnst,Var,Abs,Null)

0xc0                           // END_COLLECTION

以下是我在测试时看到的一些发现:-

  1. 但上述描述符并非在所有游戏中都有效,我发现Android中的某些游戏可以选择配置/自定义按钮映射,如DEAD TRIGGER 2 - Zombie Survival Shooter FPS,但在此类游戏中,默认轴为正确的操纵杆是 Rx 和 Ry,因此在此类游戏中,必须明确映射正确的操纵杆和触发器(用于触发器的轴仍然是个谜)。
  2. 但在无法更改按钮映射的游戏中,此 HID 描述符即使是触发器也能正常工作(这让我相信这是定义描述符的正确方法)。

【讨论】:

    【解决方案2】:
    private static float getCenteredAxis(MotionEvent event,
                                         InputDevice device, int axis, int historyPos) {
        final InputDevice.MotionRange range =
                device.getMotionRange(axis, event.getSource());
        if (range != null) {
            final float flat = range.getFlat();
            final float value =
                    historyPos < 0 ? event.getAxisValue(axis):
                            event.getHistoricalAxisValue(axis, historyPos);
            if (Math.abs(value) > flat) {
                return value;
            }
        }
        return 0;
    }
    
    
    
        // trigger_l
        float l = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_BRAKE, historyPos);
        textjoy7.setText(String.valueOf(l));
    
        if (l == 0) {
            l = getCenteredAxis(event, mInputDevice,
                    MotionEvent.AXIS_LTRIGGER, historyPos);
        }
        //trigger_x
        float r = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_GAS, historyPos);
        textjoy8.setText(String.valueOf(r));
    
        if (r == 0) {
            r = getCenteredAxis(event, mInputDevice,
                    MotionEvent.AXIS_RTRIGGER, historyPos);
        }
    

    【讨论】:

    • 感谢您的回答,看您的回答似乎提供的代码是从android端读取游戏手柄输入。就我而言,我正在开发 HID 游戏手柄,但无法在游戏中检测到模拟触发器。一段时间后发现问题是由于特定游戏开发者使用的库(我测试的 3 款游戏来自同一开发者)和一些使用原生 android 游戏手柄库的游戏在没有任何配置的情况下工作。对于描述符,请参阅我的答案。
    • 是的,但也许我的回答会派上用场)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-26
    • 2012-07-27
    • 1970-01-01
    • 1970-01-01
    • 2015-06-18
    • 1970-01-01
    相关资源
    最近更新 更多