基于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字节报告ID(固定为4)
- 4字节按键数据(共32位,每一位对应一个按键的按下/释放状态)
- 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)
关键细节说明
- 报告长度修正:原代码中
in_report_lengths=(12,)是错误的,根据描述符结构修正为9字节,否则报告无法被主机正确解析。 - 按键检测逻辑:使用
digitalio模块读取GPIO状态,带上拉电阻后,按键按下时引脚为低电平(btn.value为False)。 - 按键位映射:每个按键对应报告中1个bit,比如第一个按键对应第1字节的第0位,第9个按键对应第2字节的第0位,以此类推。
- 轴控制扩展:如果需要控制轴,直接修改报告数组的第5-8字节即可。例如设置X轴最大值:
gamepad_report[5] = 0x7F;X轴最小值:gamepad_report[5] = 0x81(8位有符号数补码)。
内容的提问来源于stack exchange,提问作者Tijn Coenen
相关产品推荐
相关产品推荐

