|
|
本帖最后由 bblueice 于 2025-12-27 15:11 编辑
观前提醒:作者不是计算机专业的。所以以下有什么涉及到专业错误的请指出来,作者虚心接受一切批评。
之前开发的ESP32 HID设备都是通过HID通信控制硬盘盒控制中心什么的。但其实这些只是进行简单的通信,并没有完全掌握HID设备的相关开发。
正好最近无聊,于是打算做一些进阶的东西来打发时间。
三年前刚上大学的时候从笔记本换到台式机主力,突然从触摸板+鼠标转到纯鼠标的我在面对部分场景时显得有些吃力。
之前都是鼠标配合触摸板使用的,只有纯鼠标后在桌面上划拉半天才想起来我已经没有触摸板(悲。心想要是在台式机上能用上触摸板多好啊。
暑假开始整HID玩后,这个愿望就显得越发强烈了……
于是这个计划便也提上日程了(喜。
目前已经完成:
硬件:
软件:
- Microsoft精确式触摸板握手
- ELAN TouchPad驱动
- 从Mouse Mode (仅支持单指) 切换到Absolute Mode (支持多指), 感谢@ApprehensiveAnt9858
- 单指触摸支持
- 多指滑动手势支持
- 物理按键支持(左键 & 右键)
未完成的:
- 外壳设计 (SOLIDWORKS建模)
- 为后续的蓝牙支持添加电池(蓝牙无线模式下指纹模块不可用)
- 多指Tap手势支持(技术力有限暂时做不出来 ╥﹏╥)
网上已经有老哥尝试过了,他参考了外国大神的支持,使用的是ESP32+Synatics触摸板(P/S2协议)。
但是P/S2总感觉有一点小膈应……说不上来为什么,并且他的支持最高只有3指,最重要的是,他的多指手势并不是原生的,缺失了很多我想要的手势(要知道Windows原生支持的精确式触摸板功能是很完善的)。当然他用的也不是我们文章所使用到的USB HID技术栈。
叠个甲。这位老哥的思路是很好的,我没有批判这位老哥的意思,我只是觉得他的方案有太多局限性,如果再深入一点的话表现会更好(
所以我打算基于原生的HID触摸板,通过ESP32,造一个移动精确式触摸板设备,来满足自己的日常需要。
首先之一当然是要挑一块合适的触摸板啦。
根据Windows文档可以确定,主要的Windows精确式触摸板可以通过四种总线类型与主机进行连接:
- USB HID
- I2C HID
- SPI HID
- 第三方私有HID设备
无论是USB HID还是第三方私有HID设备,都好像不符合我的DIY之魂……
那么接下来就只剩下I2C HID和SPI HID类型的精确式触摸板了。
其实这两类在笔记本上很常见,但找到一块合适的触摸板有点难,需要找到合适的。
通过一篇老文章,我得知了来自ELAN Technology的触摸板能够支持标准的Microsoft HID over I2C Protocol:
选择范围瞬间缩小了不少。
查阅了大量包括笔记本图纸在内的资料后,我最终选择了板号Rev.A S8974A的触摸板:
这个触控板使用了ELAN 33370A作为触摸板主控,适用于R9000X 2021R/2022、小新Pro14、ThinkBook14 G4+,采用I2C HID协议。
与笔记本连接的定义如下:
插个题外话:感觉如果纯触摸板的话有点浪费了,于是我又购入了一个HP的指纹模块FM3483/3633,它可以通过USB总线提供Fingerprint for Windows Hello支持:
沟通触摸板和电脑USB HID的硬件依旧是我们的老熟人,ESP32-S2FN4R2。
ESP32-S2和指纹模块通过来自Microchip的USB2513B连接在同一块板子上:
硬件讲完了接下来就要到软件实现了,首先是多指识别。
在Windows体系里,多指手势并不是由应用或者触摸板硬件直接“声明”出来的,而是系统基于底层触点数据自动识别的。
也就是说,精确式触摸板只会上报原始触控数据:
- 当前有多少个手指
- 每个手指的唯一ID、X/Y坐标、是否接触、是否抬起
- 时间连续的多帧数据
假如我做了一个三指触摸,从逻辑层面来说是这样的:
第一帧:3个触点,位置A/B/C
第二帧:3个触点,整体向上移动
第三帧:3个触点,继续向上
第四帧:全部抬起
当原始触控数据上报到系统后,Windows的精确式触摸板驱动会:
- 解析HID报告
- 把每个触点转换成内部的指针对象
- 将这些指针数据送入到Input堆栈
系统就会知道:
最后系统会通过非公开的手势识别引擎判断多指触控类型。
接下来是触摸板的通信相关了。
HID通信在网上有很多文章,基本原理就是大差不差。所以这里主要分析的是I2C通信。
根据Windows精确式触摸板——设备总线连接定义,如果设备通过I2C连接到Host,至少需要5个引脚:
- 数据脚(SDA)
- 时钟脚(SCL)
- 中断脚(INT)
- 电源脚(VCC)
- 接地脚(GND)
同时手册中建议至少使用400KHz的I2C始终速度且应独享一个I2C控制器,不然可能会导致I2C总线端超量。最后在设备开发中我使用了800KHZ作为通信基准速度。
另外手册中也提到了Touchpad I2C Over USB这一情况:
如果决定使用某个 USB 桥将 I²C Windows 精确式触摸板连接到其 Windows 主机,则该桥应使用设备的独特属性(wVendorID、wProductID、wVersionID)将触摸板公开为独特的设备节点。
所以最后开发的时候,我们需要在USB描述符中声明HID TouchPad和Multi-Touch。
这里要注意的是不要尝试去伪装原有的ELAN触摸板,不然的话可能会加载错误的驱动,最后进入错误的设备路径导致设备无法被识别。
我们需要让Windows相信,这是一个USB HID Touchpad,不是笔记本内建的触摸板,以此来防止驱动栈误判。
感谢前辈们的辛勤付出,在开发中间层的时候,我们在开发的时候可以参考Linux源码部分hid-elan.c来开发中间层。
所以我们在开发的时候可以根据源码中的定义开发。
关于ELAN TouchPad,由于在这方面“几乎”没有人做过(我基本没查到),所以相关资料很少。
首先是地址确认,随便写了个地址程序,确定其ADDR为0x15(0x78那个不知道是什么,后续都是基于0x15开发的):
理论上来说这时候直接读接收内容直接转译就行,但是这里开始就有大坑了。
根据一篇很老的参考资料可以得知,ELAN触摸板有两种运行方式,一种是Mouse Mode(仅支持单指,多指没数据,Report ID为0x01),另外一种是Absolute Mode(支持多指,Report ID为0x04)。
但是如果没有经过BIOS/EC的私有协商后,ELAN触摸板触摸板固件对MCU暴露的是Mouse Mode而不是Absolute Mode。
我们可以从raw data看到,它传来的数据基本就和鼠标没区别(xy位移而不是绝对坐标值),Report ID也是0x01:
后面多方查找后,我在Reddit论坛的一篇帖子上找到了一名网友 @ApprehensiveAnt9858 的解决方法,他通过抓取HID报文获得了这个诱骗代码:
- int elan_i2c_write_payload(uint8_t addr, uint16_t reg, const uint8_t* data, size_t len) {
- Wire.beginTransmission(addr);
- Wire.write(reg & 0xFF); // LSB
- Wire.write((reg >> 8) & 0xFF); // MSB
- for (size_t i = 0; i < len; i++) {
- Wire.write(data[i]);
- }
- return Wire.endTransmission();
- }
- elan_i2c_write_payload(I2C_ADDR, 0x0005, abs_mode_cmd, sizeof(abs_mode_cmd));
- uint8_t abs_mode_cmd[] = {
- 0x33, 0x03, 0x06, 0x00,
- 0x05, 0x00, 0x03, 0x03,
- 0x00
- };
复制代码
这个代码通过HID描述符读取让触摸板固件认为有一个完整Host,它可以作用于Vendor Feature Report,固件内部状态机识别后开启PTP Collection。
最后的效果如下:
让触摸板进入PTP Mode后就简单的多了,剩下的两个大坑,一个是逆向ELAN I2C HID数据转译到USB,另一个是严格按照Microsoft Precision Touchpad HID格式去写转译层就行。
文中的代码和参考资料我会放在项目末尾。
最后的可以成功模拟出精确触摸板:
实物/测试大概是这样:
请忽略图中的胶布,因为fpc买短了所以只能先用胶布固定开发,等后面画个壳子装起来。
写在最后:
- 因为小弟水平实在有限,所以希望大佬们可以帮忙优化一下代码,顺便请求各位大佬们做一下最后的多指tap适配,在此谢过各位大佬们~(๑˃̵ᴗ˂̵)
- 项目仅适用于部分ELAN触摸板,Synaptic等其他厂家的触摸板需自行适配。
资料参考:
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
打赏
-
查看全部打赏
|