You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Raspberry Pi Pico的USB HID游戏手柄按键触发方法咨询

Raspberry Pi Pico USB HID游戏手柄按键触发实现方法

我正在使用Raspberry Pi Pico开发USB HID游戏手柄,已通过编写游戏手柄描述符完成设备创建,当前代码运行正常,但不清楚如何实现手柄按键的触发操作,请问有可行的实现方法吗?

原设备初始化代码

import usb_hid

GAMEPAD_REPORT_DESCRIPTOR = bytes((
    0x05, 0x01,  # Usage Page (Generic Desktop Ctrls)
    0x09, 0x05,  # Usage (Game Pad)
    0xA1, 0x01,  # Collection (Application)
    0x85, 0x04,  #   Report ID (4)
    0x05, 0x09,  #   Usage Page (Button)
    0x19, 0x01,  #   Usage Minimum (Button 1)
    0x29, 0x20,  #   Usage Maximum (Button 32)
    0x15, 0x00,  #   Logical Minimum (0)
    0x25, 0x01,  #   Logical Maximum (1)
    0x75, 0x01,  #   Report Size (1)
    0x95, 0x20,  #   Report Count (32)
    0x81, 0x02,  #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x05, 0x01,  #   Usage Page (Generic Desktop Ctrls)
    0x15, 0x81,  #   Logical Minimum (-127)
    0x25, 0x7F,  #   Logical Maximum (127)
    0x09, 0x30,  #   Usage (X)
    0x09, 0x31,  #   Usage (Y)
    0x09, 0x32,  #   Usage (Z)
    0x09, 0x35,  #   Usage (Rz)
    0x75, 0x08,  #   Report Size (8)
    0x95, 0x04,  #   Report Count (4)
    0x81, 0x02,  #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,        # End Collection
))

gamepad = usb_hid.Device(
    report_descriptor=GAMEPAD_REPORT_DESCRIPTOR,
    usage_page=0x01,           # Generic Desktop Control
    usage=0x05,                # Gamepad
    report_ids=(4,),           # Descriptor uses report ID 4.
    in_report_lengths=(12,),    # This gamepad sends 6 bytes in its report.
    out_report_lengths=(0,),   # It does not receive any reports.
)

usb_hid.enable(
    (usb_hid.Device.KEYBOARD,
     usb_hid.Device.MOUSE,
     usb_hid.Device.CONSUMER_CONTROL,
     gamepad)
)

实现按键触发的完整方案

核心逻辑说明

根据定义的HID描述符,游戏手柄的报告结构为:

  1. 1字节报告ID(固定为4)
  2. 4字节按键数据(共32位,每一位对应一个按键的按下/释放状态)
  3. 4字节轴数据(X、Y、Z、Rz轴,每个轴取值范围-127到127)

通过读取GPIO引脚状态,更新报告数据后发送给主机,即可实现按键触发。

完整示例代码

import usb_hid
import board
import digitalio
import time

# 原描述符保持不变
GAMEPAD_REPORT_DESCRIPTOR = bytes((
    0x05, 0x01,  # Usage Page (Generic Desktop Ctrls)
    0x09, 0x05,  # Usage (Game Pad)
    0xA1, 0x01,  # Collection (Application)
    0x85, 0x04,  #   Report ID (4)
    0x05, 0x09,  #   Usage Page (Button)
    0x19, 0x01,  #   Usage Minimum (Button 1)
    0x29, 0x20,  #   Usage Maximum (Button 32)
    0x15, 0x00,  #   Logical Minimum (0)
    0x25, 0x01,  #   Logical Maximum (1)
    0x75, 0x01,  #   Report Size (1)
    0x95, 0x20,  #   Report Count (32)
    0x81, 0x02,  #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x05, 0x01,  #   Usage Page (Generic Desktop Ctrls)
    0x15, 0x81,  #   Logical Minimum (-127)
    0x25, 0x7F,  #   Logical Maximum (127)
    0x09, 0x30,  #   Usage (X)
    0x09, 0x31,  #   Usage (Y)
    0x09, 0x32,  #   Usage (Z)
    0x09, 0x35,  #   Usage (Rz)
    0x75, 0x08,  #   Report Size (8)
    0x95, 0x04,  #   Report Count (4)
    0x81, 0x02,  #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,        # End Collection
))

# 修正报告长度:1字节ID + 4字节按键 +4字节轴 =9字节
gamepad = usb_hid.Device(
    report_descriptor=GAMEPAD_REPORT_DESCRIPTOR,
    usage_page=0x01,
    usage=0x05,
    report_ids=(4,),
    in_report_lengths=(9,),
    out_report_lengths=(0,),
)

usb_hid.enable(
    (usb_hid.Device.KEYBOARD,
     usb_hid.Device.MOUSE,
     usb_hid.Device.CONSUMER_CONTROL,
     gamepad)
)

# 初始化按键GPIO(示例用GP0、GP1、GP2三个引脚连接按键)
button_pins = [board.GP0, board.GP1, board.GP2]
buttons = []
for pin in button_pins:
    btn = digitalio.DigitalInOut(pin)
    btn.direction = digitalio.Direction.INPUT
    btn.pull = digitalio.Pull.UP  # 带上拉电阻,按键按下时为低电平
    buttons.append(btn)

# 初始化HID报告数组
gamepad_report = bytearray(9)
gamepad_report[0] = 4  # 设置报告ID

while True:
    # 重置按键状态为未按下
    for i in range(1, 5):
        gamepad_report[i] = 0
    
    # 逐个检测按键状态
    for idx, btn in enumerate(buttons):
        if not btn.value:  # 按键被按下
            byte_pos = idx // 8 + 1  # 计算对应字节位置(第1-4字节存按键)
            bit_pos = idx % 8         # 计算对应bit位
            gamepad_report[byte_pos] |= (1 << bit_pos)  # 设置对应bit为1
    
    # 发送HID报告到主机
    usb_hid.send_report(gamepad, gamepad_report)
    
    # 短延时避免频繁发送报告
    time.sleep(0.01)

关键细节说明

  1. 报告长度修正:原代码中in_report_lengths=(12,)是错误的,根据描述符结构修正为9字节,否则报告无法被主机正确解析。
  2. 按键检测逻辑:使用digitalio模块读取GPIO状态,带上拉电阻后,按键按下时引脚为低电平(btn.value为False)。
  3. 按键位映射:每个按键对应报告中1个bit,比如第一个按键对应第1字节的第0位,第9个按键对应第2字节的第0位,以此类推。
  4. 轴控制扩展:如果需要控制轴,直接修改报告数组的第5-8字节即可。例如设置X轴最大值:gamepad_report[5] = 0x7F;X轴最小值:gamepad_report[5] = 0x81(8位有符号数补码)。

内容的提问来源于stack exchange,提问作者Tijn Coenen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 21:45:06