基于STM32的自定义HID游戏手柄Linux下按键反向轴偏移问题咨询
问题背景
我基于STM32设计了一款自定义游戏手柄,配备4个模拟轴与22个按键,已完成全套固件开发,其中USB描述符经过多次调试才完成。
该手柄在Windows系统下运行完全符合预期,但在Linux(具体为Raspbian发行版)系统中存在两处异常:
- 最后5个按键逻辑反向,未按下时默认处于“激活”状态,按下后反而变为“未激活”
- 摇杆轴的位置识别存在偏差,Windows下识别结果正确但Linux下错误
我的产品要求必须同时兼容多平台(后续还需适配Mac系统,目前暂无可用测试设备),想请教该问题的产生原因,以及对应的解决方法。
设备lsusb -v输出信息
Bus 001 Device 003: ID 0483:5750 STMicroelectronics Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x0483 STMicroelectronics idProduct 0x5750 bcdDevice 2.00 iManufacturer 1 STMicroelectronics iProduct 2 STM32 Custom Human interface iSerial 3 74E79C303734 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0029 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xc0 Self Powered MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.11 bCountryCode 0 Not supported bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 53 Report Descriptor: (length is 53) Item(Global): Usage Page, data= [ 0x01 ] 1 Generic Desktop Controls Item(Local ): Usage, data= [ 0x05 ] 5 Gamepad Item(Main ): Collection, data= [ 0x01 ] 1 Application Item(Local ): Usage, data= [ 0x01 ] 1 Pointer Item(Main ): Collection, data= [ 0x00 ] 0 Physical Item(Local ): Usage, data= [ 0x30 ] 48 Direction-X Item(Local ): Usage, data= [ 0x31 ] 49 Direction-Y Item(Local ): Usage, data= [ 0x32 ] 50 Direction-Z Item(Local ): Usage, data= [ 0x33 ] 51 Rotate-X Item(Global): Logical Minimum, data= [ 0x00 ] 0 Item(Global): Logical Maximum, data= [ 0xff 0x0f ] 4095 Item(Global): Report Size, data= [ 0x10 ] 16 Item(Global): Report Count, data= [ 0x04 ] 4 Item(Main ): Input, data= [ 0x02 ] 2 Data Variable Absolute No_Wrap Linear Preferred_State No_Null_Position Non_Volatile Bitfield Item(Main ): End Collection, data=none Item(Global): Usage Page, data= [ 0x09 ] 9 Buttons Item(Local ): Usage Minimum, data= [ 0x01 ] 1 Button 1 (Primary) Item(Local ): Usage Maximum, data= [ 0x16 ] 22 (null) Item(Global): Logical Minimum, data= [ 0x00 ] 0 Item(Global): Logical Maximum, data= [ 0x01 ] 1 Item(Global): Report Size, data= [ 0x01 ] 1 Item(Global): Report Count, data= [ 0x16 ] 22 Item(Main ): Input, data= [ 0x02 ] 2 Data Variable Absolute No_Wrap Linear Preferred_State No_Null_Position Non_Volatile Bitfield Item(Global): Report Size, data= [ 0x01 ] 1 Item(Global): Report Count, data= [ 0x02 ] 2 Item(Main ): Input, data= [ 0x03 ] 3 Constant Variable Absolute No_Wrap Linear Preferred_State No_Null_Position Non_Volatile Bitfield Item(Main ): End Collection, data=none Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0002 1x 2 bytes bInterval 10 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0002 1x 2 bytes bInterval 10 Device Status: 0x0001 Self Powered
测试参考截图
- 无按键按下时Linux系统识别结果:

- 无按键按下时Windows系统识别结果:

我还查阅到相关资料提到,Linux内核驱动可能会对HID设备上报的数值做处理,jstest-gtk工具支持调整轴的缩放、死区、反向等参数,或许可作为问题排查的参考线索。
问题原因分析
1. 按键反向问题
Windows的HID解析器严格遵循上报的字节顺序,而Linux的hid-input驱动处理多字节按键位域时,会按照小端序逐字节解析位状态,你当前的22个按键占3字节(22个有效位+2个填充位),最后5个按键刚好落在第三个字节的高位区域,默认填充值为1导致未按下时被识别为激活状态。
另外你当前的Report Descriptor中,按键的Input项没有显式指定位顺序,Linux内核默认假设位域是从最低位到最高位对应Usage从低到高,如果你固件上报时是高位对应高编号按键,就会出现后几位逻辑反转的情况。
2. 摇杆轴偏差问题
你当前定义的轴逻辑范围是0~4095,Windows HID解析会直接使用这个范围的原始值,而Linux的joydev驱动会默认将16位的HID轴值映射到-32767~32767的标准摇杆范围,映射时如果你的固件上报的ADC原始值没有校准到中点为2048,就会出现位置偏差。同时Linux部分内核版本会对游戏手柄的X/Y轴默认做死区补偿,没有匹配你的设备参数就会出现偏移。
解决方法
固件端修改(推荐,跨平台兼容性最好)
- 修正按键上报顺序:调整固件中按键位域的填充逻辑,保证第1个按键对应上报字节的最低位(bit0),第22个按键对应第三字节的bit5,填充的2个bit(bit6、bit7)显式置0,避免未定义状态。
- 显式声明HID报告的位顺序:在Report Descriptor的Input项中添加位顺序标记,和你固件的上报逻辑对齐,避免不同系统解析逻辑差异。
- 轴值预校准:固件中提前对ADC采样值做校准,保证摇杆中位时上报值固定为2048,上下行程刚好覆盖0~4095的范围,消除系统映射时的偏差。
Linux端临时适配(仅用于测试,不适合产品级)
- 按键反向:可以编写自定义HID quirks规则,针对你的设备ID(0483:5750)指定按键位解析顺序,或者使用
hid-recorder抓取上报数据后通过udev规则修正按键映射。 - 轴偏差:使用
jstest-gtk工具直接调整轴的缩放、偏移、反向参数,调整完成后保存为系统级的joystick配置文件,永久生效。
内容的提问来源于stack exchange,提问作者notloc

