1. RK3568 Android11双屏异触实现原理剖析
在嵌入式设备开发中,双屏异触(Dual Touch Panel Independent Touch)是一个具有挑战性的技术点。RK3568作为瑞芯微推出的中高端处理器,其Android11 BSP已经为双屏异触提供了完善的底层支持。要实现这个功能,我们需要深入理解Android输入子系统的架构设计。
Android输入子系统采用分层架构,从下到上主要分为:
- 内核驱动层:负责原始输入事件的采集和初步处理
- EventHub层:管理输入设备的连接和事件读取
- InputReader层:解析原始事件并转换为Android标准事件
- InputDispatcher层:将事件分发给对应的窗口
当系统连接多个触摸屏时,关键问题在于如何让系统正确识别每个触摸事件对应的物理屏幕。这涉及到两个核心机制:
- 输入设备标识(InputDeviceIdentifier)
- 显示设备关联(Display ID映射)
2. 输入设备标识解析与配置
2.1 InputDeviceIdentifier结构体详解
InputDeviceIdentifier是Android输入子系统的核心数据结构,它包含了识别输入设备所需的所有关键信息。在frameworks/native/include/input/InputDevice.h中定义的结构体如下:
cpp复制struct InputDeviceIdentifier {
std::string name; // 设备名称(如"Weida Hi-Tech CoolTouch System")
std::string location; // 设备物理位置(如"usb-fd880000.usb-1.1/input0")
std::string uniqueId; // 设备唯一ID
uint16_t bus; // 总线类型(如USB总线为0x0003)
uint16_t vendor; // 厂商ID(如0x2575)
uint16_t product; // 产品ID(如0xc300)
uint16_t version; // 固件版本
std::string descriptor; // 设备描述符(哈希值)
uint16_t nonce; // 随机数(用于临时区分设备)
};
在实际项目中,我们主要通过以下字段来区分主副屏的触摸设备:
- location:物理连接路径,包含USB端口号等硬件拓扑信息
- name:设备名称字符串,通常包含厂商和型号信息
- vendor/product:USB设备的厂商和产品ID
2.2 ADB调试与设备信息获取
开发过程中,我们可以通过ADB命令查看系统识别到的所有输入设备:
bash复制adb shell cat /proc/bus/input/devices
典型输出示例:
code复制I: Bus=0003 Vendor=2575 Product=c300 Version=0110
N: Name="Weida Hi-Tech CoolTouch System"
P: Phys=usb-fd880000.usb-1.1/input0
S: Sysfs=/devices/platform/fd880000.usb/usb2/2-1/2-1.1:1.0/input/input1
U: Uniq=
H: Handlers=event1
B: EV=1b
B: KEY=400 0 0 0 0 0
B: ABS=260800000000003
输出字段解析:
I::设备基本信息(总线类型、厂商ID等)N::设备名称(关键识别字段)P::物理连接路径(关键识别字段)S::sysfs路径H::对应的事件设备节点(如event1)B: EV=:支持的事件类型B: ABS=:支持的绝对坐标事件参数
提示:在实际硬件中,主副屏触摸设备通常会有不同的Phys路径(如usb-1.1和usb-1.4),这是区分它们的重要依据。
3. 双屏异触实现方案
3.1 显示系统与输入设备的关联
Android系统通过displayId将触摸事件与显示设备关联起来。在MotionEvent中可以看到这个关键字段:
code复制MotionEvent(..., displayId=0, ...)
系统会根据displayId的值将事件分发给对应的主屏(0)或副屏(1)。要实现双屏异触,关键在于正确设置输入设备的displayId映射。
3.2 EventHub配置实现
系统在EventHub.cpp中通过openDeviceLocked函数初始化输入设备时,会根据设备属性设置INPUT_DEVICE_CLASS_EXTERNAL标志:
cpp复制status_t EventHub::openDeviceLocked(const char* devicePath) {
...
if (isExternalDeviceLocked(device)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
}
...
}
这个标志决定了设备事件将被路由到哪个显示屏。我们需要在isExternalDeviceLocked函数中实现主副屏触摸设备的识别逻辑。
3.3 设备识别策略实现
Rockchip提供了两种识别主副屏触摸设备的方式,通过CONFIG_TP_DISTINGUISH宏定义选择:
cpp复制#define CONFIG_TP_DISTINGUISH_OFF 0x00 // 关闭双TP异触
#define CONFIG_TP_DEVICE_NAME_DISTINGUISH 0x01 // 根据设备名称区分
#define CONFIG_TP_USB_NUM_DISTINGUISH 0x02 // 根据USB端口号区分
3.3.1 基于USB端口号的识别方案
这是最可靠的识别方式,适合主副屏触摸设备使用相同驱动的情况:
cpp复制#if (CONFIG_TP_DISTINGUISH == CONFIG_TP_USB_NUM_DISTINGUISH)
#define TP_MAIN_USB_NUM '4' // 主屏USB端口号
#define TP_SUB_USB_NUM '3' // 副屏USB端口号
bool EventHub::isExternalDeviceLocked(Device* device) {
char DEVICE_IDENNTIFIER[30] = {0};
sprintf(DEVICE_IDENNTIFIER,"%s",device->identifier.location.c_str());
if(DEVICE_IDENNTIFIER[19] == TP_MAIN_USB_NUM){
return false; // 主屏设备
}else if(DEVICE_IDENNTIFIER[19] == TP_SUB_USB_NUM){
return true; // 副屏设备
}
...
}
3.3.2 基于设备名称的识别方案
当触摸设备使用不同驱动时,可以采用名称识别:
cpp复制#if (CONFIG_TP_DISTINGUISH == CONFIG_TP_DEVICE_NAME_DISTINGUISH)
#define TP_MAIN_DEVICE_NAME "eGalax Inc. eGalaxTouch EXC3111-5621-08.00.00.00"
#define TP_SUB_DEVICE_NAME "ILITEK ILITEK-TP"
bool EventHub::isExternalDeviceLocked(Device* device) {
if(strcmp(device->identifier.name.c_str(),TP_MAIN_DEVICE_NAME)==0){
return false; // 主屏设备
}
if(strcmp(device->identifier.name.c_str(),TP_SUB_DEVICE_NAME)==0){
return true; // 副屏设备
}
...
}
4. 实战开发与调试技巧
4.1 硬件连接方案设计
在实际硬件设计中,推荐采用以下连接方式:
- 主屏触摸设备连接到USB Hub的端口4
- 副屏触摸设备连接到USB Hub的端口3
- 确保两个触摸设备的供电稳定
这种设计可以简化软件识别逻辑,只需要检查USB端口号即可区分主副设备。
4.2 内核驱动配置要点
在内核设备树中,需要正确配置USB控制器和Hub:
dts复制&usb_host0_ehci {
status = "okay";
};
&usb_host0_ohci {
status = "okay";
};
&usb2phy0 {
status = "okay";
};
&usb2phy0_host {
status = "okay";
};
确保USB Hub驱动正常加载,可以通过以下命令检查:
bash复制adb shell ls /sys/bus/usb/devices
4.3 输入子系统调试命令
开发过程中常用的调试命令:
- 查看输入设备列表:
bash复制adb shell dumpsys input
- 查看特定输入设备能力:
bash复制adb shell getevent -li /dev/input/eventX
- 实时监控输入事件:
bash复制adb shell getevent /dev/input/eventX
- 查看触摸屏坐标范围:
bash复制adb shell cat /sys/class/input/eventX/device/absinfo/abs_*
4.4 常见问题排查
问题1:副屏触摸无响应
排查步骤:
- 确认副屏触摸设备已正确识别(cat /proc/bus/input/devices)
- 检查EventHub日志过滤条件是否正确
- 确认副屏Display ID配置正确(dumpsys display)
- 检查WindowManager是否注册了副屏的窗口
问题2:主副屏触摸事件混淆
解决方案:
- 确认USB端口号识别逻辑正确
- 检查location字符串解析是否正确
- 考虑改用设备名称识别方案
- 在InputReader中增加调试日志
问题3:触摸坐标偏移
处理方法:
- 检查触摸屏的坐标范围(absinfo)
- 确认显示屏分辨率配置正确
- 检查InputReader的校准参数
- 必要时调整触摸屏的坐标变换矩阵
5. 性能优化建议
5.1 输入延迟优化
双屏场景下输入延迟问题更为突出,可采取以下措施:
- 提高InputReader线程优先级
- 优化EventHub的事件读取逻辑
- 减少InputDispatcher的分发延迟
- 使用JankTracker监控输入延迟
5.2 电源管理配置
为降低功耗,建议:
- 为触摸设备配置合理的唤醒间隔
- 实现触摸屏的自动休眠机制
- 优化USB总线的电源管理策略
- 根据使用场景动态调整采样率
5.3 多指触控优化
在双屏场景下,需要特别注意:
- 确保多点触控协议一致
- 正确配置触摸设备的slot数量
- 处理跨屏手势的特殊情况
- 优化触摸点跟踪算法
在RK3568平台上实现双屏异触功能,需要深入理解Android输入子系统的工作原理,并结合具体硬件设计进行适配。通过合理配置InputDeviceIdentifier和设备识别策略,可以构建稳定可靠的双屏输入解决方案。