☰
ps2-controller源码解读:PS2I2C类如何把6字节I2C数据变成16个按键和4根摇杆
2026/9/25 1:28:18 网站建设 项目流程

ps2-controller源码解读:PS2I2C类如何把6字节I2C数据变成16个按键和4根摇杆

【免费下载链接】ps2-controller源师兄扩展项目: PS2 | 由源师兄组织创建项目地址: https://gitcode.com/yuanshixiong/ps2-controller

ps2-controller是源师兄开源的 PS2 手柄扩展项目:通过 I2C 总线把 PS2 无线手柄接入「大师兄板」,提供一套完整的 I2C 驱动与积木块。这篇文章做一次源码解读,带你一步步看懂PS2I2C类是如何把 6 字节 I2C 数据"拆解"成 16 个按键和 4 个摇杆轴值的,适合刚入门 I2C 和位运算的读者。

一、30秒认识项目:文件都藏在哪

整个仓库非常精简,核心驱动其实全部内嵌在一个文件里:

文件作用
blocksdef.js定义积木块,并把完整的 Python 驱动(PS2I2C类)作为代码模板内嵌其中
category.json声明「PS2手柄」积木分类及其下的 3 个积木
config.json项目元信息:适配设备为大师兄板,名称「PS2手柄」

也就是说,blocksdef.js 里pythonGenerator.definitions_["ps2_i2c_driver"]这段模板,就是本文要拆解的"主角"——一个标准的 PS2 无线手柄 I2C 驱动。

二、先看数据帧:6 字节里装着什么

驱动假设手柄以7 位地址0x13挂在 I2C 总线上,其中数据寄存器REG_PS2_DATA(0xBB)一次返回6 个字节:

字节0 字节1 字节2 字节3 字节4 字节5 方向键/系统 肩键/动作键 RX轴 RY轴 LX轴 LY轴 (8个按键) (8个按键) 0~255 0~255 0~255 0~255

规律一目了然:

  • 字节0、字节1各是一个 8 位"位图",每个 bit 代表一个按键,两个字节正好 16 键;
  • 字节2~5是 4 根摇杆轴的模拟量,取值 0~255。

这就是标题的答案雏形:6 字节 = 16 个按键位 + 4 个摇杆通道。

三、源码逐段拆解:PS2I2C 类的 4 个核心方法

1. 初始化:一条 400kHz 的 I2C 总线

def __init__(self, address=PS2_I2C_ADDRESS): self._bus = I2C(1, freq=400000) self._addr = address self._buf = bytearray(6)

见 blocksdef.js。创建I2C(1)总线(400kHz 快速模式),并预分配一个 6 字节的缓冲区_buf用来缓存手柄数据。

2._ensure_data():10ms 缓存,避免刷屏式读取

now = self._ticks_ms() if self._last_update_ms and (now - self._last_update_ms) < 10: return self._bus.writeto(self._addr, bytearray([REG_PS2_DATA])) data = self._bus.readfrom(self._addr, 6)

见 blocksdef.js。逻辑是:先读寄存器0xBB选通数据,再读 6 字节写入缓存;10ms 内的重复查询直接复用缓存。这对游戏循环(通常每秒几十次轮询)非常关键——既保证响应灵敏,又不会把 I2C 总线打满。

3.is_button_pressed():位运算查按键,"按下 = 0"

value = self._buf[byte_index] return (value & (1 << bit_index)) == 0

见 blocksdef.js。关键有两点:

  • 按键→位置映射表:类属性_BUTTON_MAP把每个按键名映射为(byte_index, bit_index),例如LEFT是字节0 的第7位、SQUARE是字节1 的第7位,共 16 项,见 blocksdef.js;
  • 低电平有效:该硬件约定"按下的位为 0",所以用&屏蔽后判断是否等于 0。

另外它还内置了一张形状符号兼容表:传"△"、"○"、"×"、"□"(及其变体)也能正确识别,对小朋友写积木程序非常友好。

4.get_axis():4 个摇杆通道一次查表拿走

if a == "LX": return self._buf[4] if a == "LY": return self._buf[5] if a == "RX": return self._buf[2] if a == "RY": return self._buf[3]

见 blocksdef.js。摇杆没有位运算,直接按字节下标取 0~255 的模拟值即可。

四、从源码到积木:3 个积木块让新手零门槛使用

PS2I2C驱动对积木层是"透明"的,category.json 里声明了 3 个积木,与源码方法一一对应:

积木(类型)生成的 Python 代码
手柄(I2C) 初始化(ohcode_extend_ps2_i2c_init)自动引入驱动并执行ps2 = PS2I2C()
PS2 按键是否按下(ohcode_extend_ps2_i2c_button_pressed)ps2.is_button_pressed("CROSS")
PS2 摇杆数值(ohcode_extend_ps2_i2c_get_axis)ps2.get_axis("LX")

初始化积木下拉 16 个按键选项、摇杆积木下拉 LX/LY/RX/RY 四轴——拼积木的过程,本质上就是调用上文拆解过的两个方法,这就是"积木化封装"的价值:新手不用懂 I2C 和位运算也能驱动手柄。

五、速查表:16 个按键的位位置

字节bit7bit6bit5bit4bit3bit2bit1bit0
字节0LEFTDOWNRIGHTUPSTARTR3L3SELECT
字节1SQUARECROSSCIRCLETRIANGLER1L1R2L2

摇杆:字节2=RX,字节3=RY,字节4=LX,字节5=LY(均 0~255)。

六、新手避坑指南

  • 🎮按下是 0 不是 1:判断逻辑是(value & (1 << bit)) == 0,改代码时千万别写反;
  • 🧩未知按键安全返回 False:is_button_pressed对不认识的按键名直接返回False,不会抛异常;
  • ⏱️善用 10ms 缓存:在快速循环里连续调用查询接口是安全的,缓存机制已兜底;
  • 🔌地址别写错:7 位地址是0x13,寄存器0xAA读模式 ID(get_id,见 blocksdef.js),0xBB读 6 字节数据。

写在最后

ps2-controller用一个不足 100 行的 Python 类,完整演示了 I2C 从设备的典型套路:固定寄存器 + 位图按键 + 模拟量轴 + 短时缓存,再用 3 个积木块把它变成新手友好的可视化接口。读懂 blocksdef.js 这一个文件,你也就拿到了自己的 I2C 外设驱动模板。🚀

【免费下载链接】ps2-controller源师兄扩展项目: PS2 | 由源师兄组织创建项目地址: https://gitcode.com/yuanshixiong/ps2-controller

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询