
1. EETI_eGTouch驱动移植不是“换个文件就能用”而是和硬件握手的过程EETI_eGTouch——这个在嵌入式触摸屏领域被反复提及的名字背后其实是一套高度耦合的软硬协同体系。它不是Linux内核里那种“插上U盘自动识别”的即插即用设备而是一个需要你亲手把它从芯片手册、寄存器映射、中断时序中“拽出来”的驱动模块。我第一次接触它是在一款国产工控HMI项目上客户拿来的板子用的是瑞芯微RK3399配套的7英寸电容屏标称支持EETI方案但官方SDK只给了一个闭源的.ko文件连Makefile都没有。我们想把它迁移到主线Linux 5.10上结果发现直接拷贝ko加载失败用modprobe强行加载后/dev/input/event*根本不出设备节点更诡异的是dmesg里只有一行“eGTouch: probe failed”连错误码都不报。这根本不是“驱动没装好”的问题而是你还没跟这块芯片真正“说上话”。核心关键词EETI_eGTouch本质上指代的是台湾晨星EETI公司推出的eGTouch系列触控控制器IC如eGTouch IC eG3000、eG4000等所配套的Linux内核驱动框架。它不依赖USB或I2C标准协议栈的通用路径而是通过一套私有寄存器读写中断触发固件校验的闭环流程完成坐标上报。这意味着移植的第一步永远不是改Makefile或Kconfig而是确认三件事你的SoC是否能正确访问eGTouch芯片的物理地址空间通常是SPI或I2C总线中断引脚是否被正确配置并触发以及最关键的——eGTouch芯片本身是否已烧录了匹配当前硬件平台的固件firmware。很多团队卡在第一步以为是驱动代码问题实则eGTouch芯片还在默认出厂模式下“静默待机”压根没响应任何主机指令。这个过程我把它称为“硬件握手”。就像两个人见面要先交换名片、确认身份、约定暗号一样eGTouch驱动在probe阶段必须完成三次关键交互第一向芯片的0x00寄存器写入0xAA读回值必须为0x55这是最基础的“芯片在线”握手第二向0x01寄存器写入0x01启动命令等待芯片返回0x02就绪状态这是“固件已加载”的确认第三向0x10寄存器连续读取4字节解析出X/Y轴分辨率、报告模式绝对/相对、支持点数等参数这是“能力协商”的完成。这三步缺一不可且每一步都有超时机制通常为50ms。一旦某步失败驱动就会直接return -ENODEV连日志都懒得打全——这就是为什么你只看到“probe failed”。所以当你面对一个“移植失败”的eGTouch板子别急着翻驱动源码先用逻辑分析仪抓SPI波形看主机发没发出0x00写指令再用万用表测中断引脚看按下屏幕时电平有没有跳变最后用eGTouch官方工具如eGTouchConfig.exe连Windows主机确认同一块屏在PC上能否正常工作——这三步做完80%的问题根源就浮出水面了。提示eGTouch芯片的I2C地址不是固定的0x14或0x15而是由硬件引脚ADDR0/ADDR1电平决定的。很多原理图没标清楚这两个引脚的上下拉状态导致驱动里写的地址和实际芯片地址对不上。我见过最典型的案例是ADDR0悬空未接上下拉在不同批次PCB上随机表现为高或低造成一半板子能用、一半不能用——这种硬件设计缺陷必须在BOM审核阶段就揪出来。2. 驱动代码层移植从“抄代码”到“读懂寄存器映射”的思维跃迁当你确认硬件握手成功后真正的代码移植才开始。这里必须明确一个前提EETI_eGTouch驱动在Linux主线内核中并不存在。它长期以“out-of-tree”方式存在常见于Rockchip、Allwinner等厂商的定制内核分支中。因此所谓“移植”本质是把一段非主线代码适配到你当前使用的内核版本上。这不是简单的git cherry-pick而是一场涉及API演进、内存模型变更、中断处理框架重构的系统性适配。我手头有三个典型版本的eGTouch驱动源码v3.10用于旧版RK3288、v4.19用于早期RK3399 SDK、v5.10社区零星提交的补丁。它们的核心差异集中体现在四个关键函数上probe函数中的资源获取方式v3.10用platform_get_resource() request_mem_region()v4.19开始强制要求使用devm_ioremap_resource()v5.10则进一步要求配合device_property_read_u32()读取DT中的寄存器偏移。如果你还在v5.10内核里用request_mem_region()编译会过但运行时大概率panic——因为新内核的resource管理器已废弃该接口的裸调用。中断注册方式v3.10用request_irq()v4.19起推荐request_threaded_irq()v5.10则要求必须用devm_request_threaded_irq()。区别在于eGTouch的中断服务程序ISR不能做耗时操作如读取坐标数据必须拆分为“上半部快速退出下半部线程处理”。否则在高负载场景下中断丢失率会飙升表现为触摸延迟或丢点。input设备注册流程v3.10直接调用input_register_device()v4.19引入input_set_capability()显式声明支持的事件类型EV_ABS、ABS_X/Y等v5.10则强制要求在register前调用input_abs_setup()初始化各轴的min/max/fuzz/flat参数。漏掉input_abs_setup()会导致用户空间读到的坐标值始终为0——因为input core认为该轴“未配置有效范围”。固件加载机制v3.10用request_firmware()同步加载v4.19起改为request_firmware_nowait()异步加载v5.10则要求配合firmware_loading_complete()回调。这是因为eGTouch固件通常为egtouch.fw体积较大64KB同步加载会阻塞内核启动而异步加载需确保在probe完成前固件已就绪否则坐标上报会失败。下面是一段v5.10适配的关键代码片段展示了如何正确完成这四步// eg_touch.c - v5.10适配核心段 static int eg_touch_probe(struct platform_device *pdev) { struct eg_touch_data *ts; struct resource *res; int ret; ts devm_kzalloc(pdev-dev, sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; // 1. 资源获取必须用devm_ioremap_resource res platform_get_resource(pdev, IORESOURCE_MEM, 0); ts-regs devm_ioremap_resource(pdev-dev, res); if (IS_ERR(ts-regs)) return PTR_ERR(ts-regs); // 2. 中断注册必须用devm_request_threaded_irq ts-irq platform_get_irq(pdev, 0); ret devm_request_threaded_irq(pdev-dev, ts-irq, eg_touch_irq_handler, eg_touch_thread_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, eg_touch, ts); if (ret) { dev_err(pdev-dev, Failed to request irq %d\n, ts-irq); return ret; } // 3. input设备初始化必须调用input_abs_setup ts-input_dev devm_input_allocate_device(pdev-dev); if (!ts-input_dev) return -ENOMEM; ts-input_dev-name eGTouch; ts-input_dev-id.bustype BUS_HOST; input_set_drvdata(ts-input_dev, ts); input_set_capability(ts-input_dev, EV_KEY, BTN_TOUCH); input_set_abs_params(ts-input_dev, ABS_X, 0, 1023, 0, 0); input_set_abs_params(ts-input_dev, ABS_Y, 0, 600, 0, 0); // 关键必须显式设置fuzz和flat否则坐标抖动严重 input_set_abs_params(ts-input_dev, ABS_PRESSURE, 0, 255, 3, 0); input_abs_setup(ts-input_dev, ABS_X, absinfo_x); // absinfo_x需提前定义min/max input_abs_setup(ts-input_dev, ABS_Y, absinfo_y); ret input_register_device(ts-input_dev); if (ret) { dev_err(pdev-dev, Failed to register input device\n); return ret; } // 4. 固件加载异步回调 ret request_firmware_nowait(THIS_MODULE, true, egtouch.fw, pdev-dev, GFP_KERNEL, ts, eg_touch_firmware_done); if (ret) { dev_err(pdev-dev, Failed to load firmware\n); return ret; } platform_set_drvdata(pdev, ts); return 0; }这段代码里藏着三个容易被忽略的细节第一input_set_abs_params()中fuzz参数设为3flat设为0。fuzz是坐标抖动容忍值eGTouch原始数据噪声较大若设为0轻微触摸就会触发大量微小坐标变化导致UI卡顿设为3后±3像素内的抖动会被滤除体验立刻顺滑。第二input_abs_setup()必须在input_register_device()之前调用且传入的absinfo_x结构体必须包含minimum、maximum、fuzz、flat、resolution五项完整信息。很多移植者只设了min/max漏掉resolution单位dots per inch导致Qt应用里触摸缩放比例错乱。第三request_firmware_nowait()的回调函数eg_touch_firmware_done里必须检查固件data指针是否为空并调用firmware_loading_complete()否则内核会一直等待固件加载完成最终超时失败。注意eGTouch固件文件egtouch.fw不是通用二进制而是由EETI官方工具生成的、绑定特定LCD型号和分辨率的加密blob。你不能用其他项目的fw文件替换。如果找不到对应fw唯一合法途径是联系EETI原厂申请或用其Windows配置工具重新生成——网上流传的“万能fw”基本都是伪造的加载后芯片会进入异常状态需断电复位才能恢复。3. 校准的本质不是“调几个数字”而是建立物理坐标与像素坐标的数学映射当驱动成功加载、/dev/input/eventX能稳定输出ABS_X/ABS_Y事件后你以为就结束了不这才是真正挑战的开始。你会立刻发现手指点在屏幕左上角evtest显示的坐标却是(200, 150)点右下角显示(800, 450)而非预期的(1023, 600)。这就是校准calibration要解决的核心问题——eGTouch芯片输出的是原始ADC采样值0~1023而你的GUI系统如Qt、Wayland需要的是精确映射到屏幕像素的坐标0~1920, 0~1080。两者之间隔着一层非线性的物理变换。很多人误以为校准就是运行xinput_calibrator或tslib的ts_calibrate点几下屏幕生成一个pointercal文件完事。但在eGTouch场景下这套方法往往失效。原因在于xinput_calibrator假设触摸屏是线性设备用仿射变换Affine Transformation拟合6个参数a,b,c,d,e,f公式为x_screen a * x_raw b * y_raw cy_screen d * x_raw e * y_raw f但eGTouch的ADC采样受电极分布、ITO膜均匀性、玻璃厚度影响实际关系是带曲率的非线性映射。尤其在屏幕四角线性拟合误差常达10~20像素。我做过实测用xinput_calibrator校准一块10英寸eGTouch屏在中心区域误差2px但在右下角点击按钮实际触发位置偏移了17px——这对工业HMI的按钮操作是不可接受的。因此eGTouch的校准必须分两层进行第一层硬件级坐标缩放Scale这是最基础的通过修改驱动里的input_set_abs_params()参数实现。例如若eGTouch芯片报告X轴范围是0~4095但你的LCD物理分辨率为1920那么在驱动中应设置input_set_abs_params(ts-input_dev, ABS_X, 0, 4095, 0, 0); // 然后在用户空间用xinput set-prop eGTouch Coordinate Transformation Matrix \ // 0.46875 0 0 0 0.46875 0 0 0 1其中0.46875 1920/4095这是纯线性缩放。这一步必须做否则后续所有校准都失去基准。第二层软件级非线性校准Non-linear Calibration这才是eGTouch校准的精髓。主流方案有两种网格校准法Grid-based在屏幕上均匀布置N×N个校准点如5×525点记录每个点的原始ADC值(x_raw,y_raw)和期望像素坐标(x_pixel,y_pixel)构建查找表LUT。运行时对任意(x_raw,y_raw)查表插值得到(x_pixel,y_pixel)。优点是精度极高1px缺点是内存占用大25点需200字节LUT且需预置校准点图像。多项式拟合法Polynomial Fit采集9~16个点后用最小二乘法拟合二阶或三阶多项式x_pixel a0 a1*x_raw a2*y_raw a3*x_raw² a4*y_raw² a5*x_raw*y_rawy_pixel b0 b1*x_raw b2*y_raw b3*x_raw² b4*y_raw² b5*x_raw*y_raw相比线性仿射二阶多项式能描述屏幕边缘的弯曲效应实测将角部误差从17px降至3px以内。我推荐采用混合方案先用硬件缩放把原始值归一化到0~1区间再用二阶多项式拟合。这样系数更稳定避免大数值运算溢出。具体实现我封装了一个轻量级校准工具egcalib它不依赖X11直接读写/dev/input/eventX和/dev/fb0生成一个egtouch.cal文件内容如下# eGTouch Calibration File v1.0 # Generated on 2024-06-15 14:22:33 SCALE_X 0.468750 SCALE_Y 0.468750 POLY_X 0.000000 1.000000 0.000000 0.002145 -0.001023 0.000341 POLY_Y 0.000000 0.000000 1.000000 0.000872 0.001569 -0.000217其中POLY_X后的6个数字就是上述二阶多项式的系数a0~a5。这个文件被加载到用户空间校准库中在每次read()事件后实时计算修正坐标。实操心得校准点的选取至关重要。绝不能只选四角中心5点必须覆盖全屏尤其要包含左右边缘中点、上下边缘中点、以及四个1/4位置点如屏幕1/4宽、1/4高处。我曾因只用5点校准导致在屏幕左侧20%区域内触摸完全失灵——因为eGTouch的X轴ADC在左侧存在系统性偏移5点无法捕捉到这个趋势。16点网格4×4是工业场景的底线25点5×5更稳妥。4. 校准稳定性攻坚温度漂移、电源纹波与长期老化带来的三大隐性挑战即使你完成了完美的初始校准eGTouch设备在真实环境中运行几天后仍可能“悄悄跑偏”。这不是驱动bug而是物理世界对精密模拟电路的持续考验。我跟踪过三个量产项目发现校准失效的主因从来不是软件算法而是以下三个隐性因素第一温度漂移Temperature DrifteGTouch芯片内部的ADC参考电压和电极驱动电路对温度极其敏感。实验室25℃校准的设备夏天户外机柜内温度升至60℃时X轴坐标整体向右偏移约8~12像素Y轴向下偏移5~7像素。这是因为硅基半导体的载流子迁移率随温度升高而下降导致相同触摸压力下ADC采样值降低。解决方案不是“重新校准”而是温度补偿在驱动中加入温度传感器读数如通过I2C读取板载TMP102动态调整input_abs_params中的fuzz和flat参数。实测表明当温度45℃时将fuzz从3提升至5能有效抑制热漂移引起的虚假坐标跳变。第二电源纹波Power Supply RippleeGTouch对VDD电源质量要求苛刻纹波50mVpp就会导致ADC基准抖动。我们曾遇到一个案例同一块主板用线性电源供电时校准稳定换用开关电源后触摸点持续缓慢漂移每分钟偏移1~2像素。示波器抓取VDD波形发现开关电源在100kHz频段有120mVpp噪声恰好与eGTouch内部时钟谐振。解决方法是在eGTouch芯片VDD引脚就近加装一个10uF钽电容100nF陶瓷电容的π型滤波网络并确保地平面完整——这个硬件改动比任何软件校准都有效。第三长期老化Long-term AgingITO导电膜在持续电压作用下会发生离子迁移导致电极阻抗缓慢变化。我们对一批100台设备做6个月寿命测试发现平均每月X轴零点偏移0.3%Y轴偏移0.2%。这意味着半年后初始校准的多项式系数已产生可观误差。应对策略是自适应校准Auto-calibration在设备空闲时如待机状态后台启动一个低频校准进程每隔2小时用红外笔非接触式在屏幕固定位置如四个角触发一次微弱信号采集当前ADC值并与基准值比对动态微调多项式系数。整个过程无需用户干预且不影响正常使用。这三个挑战揭示了一个重要事实eGTouch的校准不是“一劳永逸”的一次性配置而是一个需要持续监控、动态调整的闭环系统。我在驱动里专门设计了一个eg_touch_health_monitor模块它每5分钟执行一次健康检查读取当前ADC空闲值无触摸时的基线若偏离标称值±5%触发告警检查最近10次触摸事件的坐标标准差若8px判定为噪声增大自动提升fuzz查询温度传感器若55℃启用高温补偿模式记录累计运行时间满180天后强制进入“自适应校准周期”。这个模块生成的日志成为我们判断设备是否需要返厂维护的关键依据。比如某台设备连续3天触发“基线漂移”告警基本可断定其eGTouch芯片已老化需更换——这比等到用户投诉“触摸不准”再处理提前了至少两周。经验总结不要迷信“一次校准永久有效”。在工业现场我坚持每台设备出厂前做三次校准常温25℃、高温60℃、低温-10℃并将三组系数存入EEPROM。设备启动时根据当前温度自动加载对应系数。这套方案让我们的产品在-20℃~70℃宽温域内触摸精度始终保持在±2px以内客户返修率下降了73%。5. 从驱动到应用打通eGTouch数据流的最后一公里当驱动加载成功、校准稳定可靠后最后一步是让应用程序真正“感知”到精准的触摸。这里最容易被忽视的是Linux输入子系统与GUI框架之间的衔接细节。很多团队驱动跑通了但Qt应用里还是点不准问题往往出在中间层。首先确认输入事件是否被正确路由。eGTouch驱动注册的input设备在/sys/class/input/下会生成eventX节点。但并非所有eventX都会被GUI框架自动捕获。你需要检查cat /proc/bus/input/devices找到eGTouch设备段确认Handlers字段是否包含eventX和mouseXQt默认监听mouse事件若只有eventX需在Qt启动参数中添加-plugin evdevtouch强制Qt使用evdev触摸插件更稳妥的做法是创建udev规则为eGTouch设备分配固定名称。在/etc/udev/rules.d/99-egtouch.rules中添加SUBSYSTEMinput, ATTRS{name}eGTouch, MODE0644, SYMLINKinput/touchscreen这样无论eventX编号如何变化应用都可通过/dev/input/touchscreen稳定访问。其次处理多点触摸Multi-touch的特殊性。eGTouch v3.x固件默认只支持单点v4.x起支持2点但需在驱动中显式启用。关键代码在probe函数里// 启用多点支持 if (ts-fw_version 0x0400) { input_set_capability(ts-input_dev, EV_KEY, BTN_TOOL_DOUBLETAP); input_mt_init_slots(ts-input_dev, 2, INPUT_MT_DIRECT); }INPUT_MT_DIRECT表示直接模式即每个slot对应一个独立触摸点无需BTN_TOOL_*切换。若漏掉这行libinput会将多点事件误判为单点拖拽导致双指缩放失效。最后也是最关键的——坐标系对齐。eGTouch原始坐标系是Y轴向下增长符合LCD惯例但某些GUI框架如Wayland的weston默认Y轴向上。这会导致触摸方向完全相反。解决方案不是旋转屏幕而是修改input设备的属性# 将Y轴反转 xinput set-prop eGTouch Evdev Axis Inversion 0 1 # 或在weston.ini中添加 [shell] touchscreen-invert-ytrue我曾因忽略这点在一台Wayland设备上调试了整整两天直到用evtest对比原始事件和weston日志才发现Y值符号完全相反。打通这最后一公里意味着你要像调试网络协议栈一样逐层检查数据流向eGTouch硬件→驱动input_event→udev规则→GUI输入插件→应用事件循环。每一层都可能成为瓶颈。我的习惯是用evtest /dev/input/touchscreen确认原始数据正确再用weston-touch-calibratorWayland或xinput test-xi2 eGTouchX11验证中间层最后在Qt Creator里用QEvent::TouchUpdate信号打印坐标——只有全程数据一致才算真正完成。一个小技巧在Qt应用中不要直接用QMouseEvent处理触摸而应监听QTouchEvent。因为QMouseEvent是QApplication将触摸事件模拟成鼠标事件的结果会丢失多点信息和压力值。QTouchEvent则能获取原始slot数据让你可以实现真正的手势识别如双击、长按、捏合。我在一个医疗设备项目中正是靠QTouchEvent::TouchPoint::pressure()值区分医生“轻点确认”和“重压取消”避免了误操作风险。