主题
原理说明
pico-hid-mapper 是一个运行在微雪 RP2350-USB-C 开发板上的固件,核心功能是将 USB HID 键鼠输入映射为触屏操作,通过另一个 USB 口输出给手机等设备。
硬件概况
RP2350-USB-C 是微雪推出的双核 RP2350 开发板,板载两个 USB-C 口:
| 接口 | 功能 |
|---|---|
| 原生 USB-C | RP2350 芯片内置 USB 控制器,连接手机 / 电脑,输出触屏 HID + ECM 虚拟网卡 |
| PIO-USB-C | 通过 PIO(可编程 I/O)模拟的 USB 口,可连接外置键鼠或接收上位机命令 |
双 USB 口无需任何外接线,开发板即插即用。
双核架构
RP2350 芯片有两个 ARM Cortex-M33 核心,固件将两个核心分工:
| 核心 | 职责 | 运行频率 |
|---|---|---|
| Core 0 | 管理原生 USB 口 — TinyUSB 设备栈,提供 HID 触屏 + ECM 虚拟网卡 | ~500k Hz |
| Core 1 | 管理 PIO-USB 口 — Host/Device 协议栈 + 映射引擎 + Lua 虚拟机 | ~1000k Hz |
两个核心通过映射引擎协作:Core 1 负责「输入侧」(键鼠 / 上位机报文),Core 0 负责「输出侧」(触屏上报、网络通信),核心之间通过回调接口传递数据。
Lua
tick回调与映射引擎core_process以 100 µs / 10kHz 的频率调用,固件内部从事件接收到触屏输出,链路延迟约 0.015 ms。同一台机器上,从 PIO-Device 发送 HID 命令帧到收到触屏 HID 报告的实际端到端延迟约 1.8 ms。这 1.8 ms 的主要构成:
- USB HID 端点轮询间隔(主机侧通常 1ms 一帧,上下行各占用一次)
- PIO-USB 命令帧解析 + 映射引擎处理(~0.015 ms,占比极小)
- 触屏事件队列 + Core 0 触屏上报
- 操作系统 HID 驱动层缓冲与分发
可见瓶颈主要在 USB 传输层(主机轮询周期),固件内部处理几乎不占延迟。
PIO 口的两种角色
PIO-USB-C 口启动时按 flash 中存储的角色二选一运行(可通过 WebUI 切换,重启生效):
Device 模式(默认)
PIO 口作为 USB Device,通过 HID 命令帧协议接收上位机下发的控制命令。
典型场景:上位机(电脑 / 另一台设备)通过 USB 连接 Pico 的 PIO 口,下发键鼠报文或直接控制触屏输出。此时 PIO 口相当于一个「命令接口」,上位机可通过 HIDAPI 完全控制设备行为。
上位机 → HID 命令帧 → PIO-USB (Device) → 映射引擎 / 触屏直出 → 原生 USB → 手机Host 模式
PIO 口作为 USB Host,外接键鼠。插入的键盘/鼠标被枚举后,HID 报告自动喂入映射引擎,引擎根据配置文件将按键/鼠标动作转为触屏事件。支持普通键盘、鼠标以及 NKRO 全键无冲键盘(键位段式的报告描述符会被自动识别解析)。
外接键鼠 → PIO-USB (Host) → 映射引擎 → 原生 USB → 手机/电脑⚠️ 必须使用 USB 扩展坞(仅支持 CH334 USB 2.0 Hub):PIO-USB 是通过 RP2350 的 PIO 状态机软件模拟的 USB1.1 控制器,而非硬件 USB PHY。直接连接键盘或鼠标时,部分设备在热插拔时可能出现枚举失败,需要重新上电才能识别。扩展坞自带硬件 USB PHY,电气信号更稳定,PIO-USB 只需与扩展坞通信即可。已测试 CH334 USB 2.0 Hub 能稳定工作,其他型号拓展坞不保证兼容。
➕ GPIO8 扩展主机端口:固件同时初始化了第二个 PIO-USB 主机端口(GPIO8 = D+,GPIO9 = D−)。可将 GPIO8/GPIO9 引出作为 D+/D−,配合 5V 与 GND,自行焊接外接第二个 USB 键鼠设备。
切换方式
通过 WebUI 的「PICO 配置」弹窗切换角色,设备自动保存到 flash 并重启生效。
映射引擎
映射引擎是固件的核心模块,负责将键鼠输入转换为触屏 HID 报告。它是一个平台无关的纯逻辑模块,不依赖具体硬件。
输入源
映射引擎可接收以下输入:
| 输入类型 | 来源 |
|---|---|
| 键盘按键 | 外接键盘 (Host 模式) / HIDAPI 命令 (Device 模式) |
| 鼠标按键/移动/滚轮 | 外接鼠标 (Host 模式) / HIDAPI 命令 (Device 模式) |
| 手柄摇杆/按键 | 外接手柄 — Host 模式 (DS5 / XInput) / HIDAPI CMD 0xF9 — Device 模式 |
| 自定义事件 | WebSocket / WebHID / HIDAPI 命令 |
输出类型
映射引擎通过回调输出:
| 输出 | 说明 |
|---|---|
| 触屏事件 | DOWN(按下)、MOVE(移动)、UP(抬起),32 位坐标精度 |
| 虚拟鼠标状态 | 光标位置 + 按键状态,可通过网络转发 |
| 调试信息 | 实时日志,通过 WebSocket 推送给所有连接的客户端。Web UI 日志页支持一键导出为 .log 文件 |
手柄映射
手柄摇杆和按键通过映射引擎转换为触屏操作:
- 左摇杆 → 轮盘移动(与 WASD 轮盘共用同一触点,二者不共存)。圆形死区判定,推杆幅度控制轮盘半径(推到底=疾跑距离),方向角精确跟随。未推过死区时轮盘触点释放。
- 右摇杆 → 视角控制或鼠标指针移动。采用极坐标曲线映射(先过曲线再分解回 x/y,避免对角线方向偏移),积分累加 + 回报率限流。映射开启时控制视角,关闭时移动虚拟鼠标指针。死区、曲线和灵敏度均可通过 HTTP API 独立配置。
- 手柄按键 → 与键盘/鼠标按键处理流程完全一致:边沿检测 → bitmap 监听过滤 → Lua 脚本拦截 → 映射引擎查询配置 → 触屏输出。手柄按键码使用独立编码空间(0x01–0x1B),在 JSON 配置文件中可与键盘/鼠标按键统一管理。
- 扳机(LT/RT) → 当前版本暂存模拟值,不做主动映射。RT 在 vmouse 模式(映射关闭)下有特殊行为:自动转为鼠标左键点击。
配置文件
映射规则由 JSON 配置文件定义,内容包括:
- 按键 → 触屏点击区域的对应关系(含手柄按键)
- 鼠标移动的灵敏度、视角拖拽行为
- 手柄摇杆的死区、曲线、灵敏度
- WASD 轮盘的八方向映射
配置文件通过 WebUI 上传,写入 flash 持久存储,掉电不丢。最多支持 9 个配置槽位,可使用web或者快捷键left_alt + F1~9切换。
恢复出厂设置
配置(KV 存储)与 Lua 脚本持久化在 flash 顶部专区内。若配置损坏导致设备异常,可恢复出厂默认(清空全部 KV 配置与 Lua 脚本,回到编译默认):
- Web UI:PICO 配置 → 危险操作 → 恢复出厂设置。设备运行中直接整区擦除 KV + Lua 配置区,随后自动重启。
- 开机 GPIO strap:上电时短接 GPIO29 与 3V3。开机早期固件检测到高电平,即在加载任何配置之前整区擦除 KV(128KB)+ Lua(68KB)专区,擦后自动重建空存储回到默认。该路径不依赖网络 / Web UI,用于设备无法启动时的兜底恢复。
⚠️ 恢复出厂会同时清除授权激活码(
lc),设备回到未激活状态,需要重新激活。此操作不可撤销。
Lua 脚本引擎
固件内置 Lua 5.4.6 解释器,在映射引擎的边沿检测之后、触屏输出之前运行。你可以用 Lua 脚本:
- 拦截、修改或放行按键/鼠标/手柄事件
- 实现压枪宏、连招宏、一键丢弃等自动化操作
- 通过
tick回调实现定时逻辑 - 用协程(coroutine)实现异步动作序列
Lua 脚本通过 WebUI 上传管理,支持暂停/恢复/停止。脚本中使用 print() 输出的日志会通过 WebSocket 实时推送。
详细 API 见 Lua 脚本 API。
通信概览
设备通过原生 USB 口与手机建立 ECM 虚拟网卡连接,Pico 固定 IP 为 192.168.73.1。
| 通信方式 | 地址 | 用途 |
|---|---|---|
| HTTP | http://192.168.73.1/ | WebUI 配置管理(上传配置/脚本、切换模式) |
| WebSocket | ws://192.168.73.1/ws | 双向实时通信(自定义事件 → 设备,日志 ← 设备) |
| HID 触屏 | 原生 USB HID 报告 | 向手机输出触屏事件 |
| UART(MAKCU) | GPIO2/3,921600 | 上位机键鼠控制协议,详见 MAKCU 使用说明 |
Android 手机访问
Android 系统限制应用直接访问 ECM 网段,需通过端口转发(如 ADB + socat)将 Pico 的 80 端口映射到手机本地 127.0.0.1:8000,然后在手机浏览器打开 http://127.0.0.1:8000/。
详见 快速开始 中的配置步骤。