ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Linux下Synaptics触摸屏驱动适配实战:I2C/SPI接口与调试指南

Linux下Synaptics触摸屏驱动适配实战:I2C/SPI接口与调试指南 简介面向嵌入式 Linux 驱动开发与系统移植人员这份压缩包提供 Synaptics 触摸屏设备的 I2C/SPI 驱动程序源码用于在 Linux 环境下驱动触摸屏、触摸板及触摸显示器将触摸信号转换为点击、滑动等操作指令可直接作为驱动移植或二次开发的参考基础。资源共 106 个文件压缩包约 2.09MB主要包含 C 源码、头文件、sample 示例、Makefile/Kconfig 构建配置以及 synaptics_reg_access、synaptics_fw_updater 等寄存器访问与固件升级工具覆盖驱动编译、设备配置、固件更新与调试等关键环节。目前已有 158 人学习下载。从中可以了解 Synaptics 驱动在 I2C/SPI 总线上的通信框架、触摸事件上报流程以及驱动作为硬件与操作系统中间层的实现方式对需要维护或适配 Synaptics 触摸设备的工程师来说具有直接的工程参考价值也能帮助缩短驱动调试和产品落地周期。 这两年我在好几个项目里跟Synaptics的触摸屏驱动打过交道从消费级的平板到工业控制的面板都遇到过踩过的坑堆起来能绕开发板一圈。Synaptics的触摸芯片在市面上存量极大其中大部分是通过I2C或SPI这两种接口接入主控的而Linux下的驱动适配工作说白了就是跟总线、中断、设备树、固件这几个东西死磕。这篇东西我按实际项目的处理顺序来讲先看总线和驱动框架怎么选、怎么搭再拆核心的数据通路然后把调试手段和参数整定讲透最后把最容易翻车的几个问题列出来。不管你是刚接手一个触摸不亮的板子还是准备在新的Linux平台上从零移植Synaptics驱动照着这个思路走能省下大量抓瞎的时间。重点会放在I2C接口上SPI部分的差异我会单独说明。1. 项目背景与方案选型I2C还是SPI这不是拍脑袋定的1.1 为什么Synaptics驱动会同时涉及I2C和SPI两种接口Synaptics的触控芯片比如市面上常见的RMI4系列内部逻辑是一套统一的寄存器映射协议业界习惯称为RMI4协议。这套协议本身不关心底层跑在什么物理总线上它既可以封装在I2C帧里也可以封装在SPI帧里。所以同一个驱动框架往往会在代码里拆成rmi_i2c.c和rmi_spi.c两个传输层文件分别挂在Linux的I2C子系统和SPI子系统下面。这就解释了为什么你搜“synaptics触摸驱动”时资料总是成对出现。物理接口决定了你在设备树里怎么写节点、在驱动里注册什么类型的总线设备、以及中断处理时用哪种读数据的方式。对做系统集成的工程师来说选哪个接口通常是硬件原理图阶段就定死的但理解两种接口在驱动层面的差异能帮你快速判断问题出在哪一层。1.2 两种总线在触控场景下的真实差异I2C和SPI的差别很多文章喜欢列一堆时序图讲协议细节但落到触控屏这个具体场景真正起决定作用的就三点速率、引脚、时序复杂度。我一直认为选型时只要盯住这三点就够了其他都是次要的。对比项I2CSPI触控场景下的影响理论速率100k~3.4Mbps可达几十Mbps大尺寸屏、高报点率用SPI更稳引脚占用2根SDASCL4根或更多MOSIMISOSCKCSI2C省引脚布线压力小时序要求有地址、ACK、时钟拉伸全双工、无应答I2C调试麻烦SPI相对粗暴抗干扰能力开漏上拉弱一些推挽输出强一些长走线、强干扰环境SPI更可靠常见场景手机、平板、小尺寸工控大屏一体机、车载、高端工控量大的消费类多数用I2C从我实际接触的项目来看7寸以下、报点率要求不高的屏幕I2C完全够用Android平板和很多Linux手持设备都是这么干的。但如果屏到了10寸以上或者客户要求多点触控上报率达到120Hz甚至更高I2C的带宽就会显得捉襟见肘这时候上SPI是更稳妥的选择。还有一种情况是主控的I2C控制器已经占满了只能从SPI借路说白了选型很多时候是“被逼的”。1.3 设备树里怎么区分两种接口设备树是Linux下描述硬件拓扑的“说明书”驱动通过它才知道自己该以什么身份加载。I2C和SPI接口的触控节点写法区别很大I2C靠地址寻址SPI靠片选号寻址。先看I2C的典型写法i2c2 { status okay; clock-frequency 400000; synaptics_touch: synaptics20 { compatible syna,rmi4-i2c; reg 0x20; interrupt-parent gpio1; interrupts 13 IRQ_TYPE_EDGE_FALLING; syna,reset-gpio gpio1 12 GPIO_ACTIVE_LOW; pinctrl-names default; pinctrl-0 pinctrl_i2c2_touch; }; };SPI的写法则是这样spi1 { status okay; pinctrl-names default; pinctrl-0 pinctrl_spi1_touch; synaptics_touch: synaptics0 { compatible syna,rmi4-spi; reg 0; spi-max-frequency 2000000; interrupt-parent gpio1; interrupts 13 IRQ_TYPE_EDGE_FALLING; spi-cpha; spi-cpol; }; };注意SPI节点里必须通过spi-max-frequency限制时钟频率因为触控芯片的SPI接口往往不支持太高的速率很多芯片手册会写明上限是2MHz或者5MHz超出之后通信时好时坏而且毫无规律。另外spi-cpha和spi-cpol这两个属性要跟芯片手册里的时序要求对齐这是个极高频率踩坑的点后面我单独说。2. 驱动框架与代码结构拆解2.1 从RMI4角度看Synaptics驱动的分层设计Synaptics的Linux驱动在mainline内核里位于drivers/input/rmi4/整个框架分成三层传输层Bus Layer、核心层Core Layer、功能层Function Layer。这个分层思路很值得学习它把“数据怎么传”和“数据是什么含义”彻底解耦了。传输层就是前面提到的rmi_i2c.c和rmi_spi.c职责只有一个按照物理总线的规则把寄存器地址和数据字节发出去、收回来。核心层rmi_bus.c和rmi_driver.c负责枚举芯片内部的功能模块并为每个功能模块创建对应的逻辑设备。功能层则是一个个功能模块的驱动最常见的是rmi_f01.c设备控制、rmi_f11.c和rmi_f12.c2D触摸上报、rmi_f34.c固件下载。拿人的身体来打比方I2C/SPI是血管核心层是心脏和大脑负责感知各个器官是否存在并调度它们工作功能层就是具体的器官——手指碰上去之后数据从物理层流到F11/F12最终通过input子系统上报给系统。2.2 probe流程驱动是怎么找到芯片并完成初始化的不管I2C还是SPI驱动的入口都是probe函数。以I2C为例当设备树里的节点与rmi_i2c驱动的compatible即syna,rmi4-i2c匹配时内核就会调用probe。这里面发生了几件关键的事情首先驱动会读取芯片的寄存器确认这是一个真实的Synaptics设备同时拿到芯片的ROM版本和功能模块的映射表。这个探测过程必须用一次总线事务完成读操作如果总线不稳定这一步就会失败。其次拿到映射表之后才轮到前面提到的核心层登场它逐个创建功能模块的设备。也就是说probe阶段的I2C通信只做最基本的握手真正的触控功能要等F01控制模块和F11/F12触摸模块都被成功绑定之后才可用。最后request_threaded_irq注册中断。这里有一个非常关键的细节触控芯片的中断处理函数里需要读I2C设备而I2C访问是可能睡眠的因为要等硬件控制器完成传输所以绝对不能直接用普通的中断上下文。必须用线程化中断threaded IRQ把实际的数据读取和上报工作放到内核线程里执行。static irqreturn_t rmi_i2c_irq_thread(int irq, void *p) { struct rmi_i2c_xport *xport p; int ret; ret rmi_read_block(xport-rmi_dev, RMI_ATTN_REPORT_OFFSET, xport-attn_data, xport-attn_size); if (ret) { dev_err(xport-client-dev, failed to read attention report: %d\n, ret); return IRQ_HANDLED; } rmi_process_interrupt_requests(xport-rmi_dev); return IRQ_HANDLED; }类似这样的代码里真正干活的是rmi_process_interrupt_requests它会根据attention report里各位的含义去调用对应的功能模块处理函数。F11/F12拿到原始的触摸坐标和压力数据后再转换成内核input子系统认识的事件上报上去。2.3 中断与上报通路从按下到用户空间事件这条链路值得完整走一遍。手指按下时Synaptics芯片检测到电容变化把触摸坐标写进自己内部的寄存器然后通过INT引脚拉低通知主控。主控的GPIO控制器触发中断内核发现该GPIO对应的irq有线程化处理函数唤醒该线程。线程里通过I2C/SPI读取attention report根据report判断是哪个功能模块有事件再读对应模块的数据寄存器拿到坐标。最后通过input_report_abs和input_sync上报。多点触控用的是内核的MT协议B核心是slot的概念。每个触摸点占据一个slot驱动通过tracking id来识别同一个手指的移动轨迹。实际代码里大致是这样的input_mt_slot(input, slot_num); input_mt_report_slot_state(input, ABS_MT_TRACKING_ID, true); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); input_report_abs(input, ABS_MT_PRESSURE, pressure); input_mt_report_pointer_emulation(input, true); input_sync(input);这段代码每次中断处理中都会执行一套完整的报点动作会连续上报多组事件。用户空间的libinput或tslib拿到这些事件后再叠加校准算法最终变成屏幕上看到的鼠标移动或手势动作。很多工程师排查问题只看应用层结果调了半天发现数据根本没从芯片出来这就是对链路理解不深导致的。3. 实操过程与关键调试方法3.1 用i2c-tools验证物理链路拿到一块触摸不工作的板子我第一件事永远是先确认芯片到底通不通这属于“先看病再开药”的思路。i2c-tools是Linux下最趁手的工具没有之一。先列出系统里有哪些I2C总线i2cdetect -l然后用扫描命令在对应的总线上寻找设备地址。Synaptics的芯片I2C地址一般是0x20或0x2C取决于硬件配置引脚的电平扫描结果如果能看到这个地址说明物理链路基本没问题i2cdetect -y -r 2如果扫描不到地址可以优先怀疑这几种情况电源没供上、复位引脚一直被拉低、I2C上拉电阻没贴或贴错、地址引脚配置不对。我遇到过最奇葩的一回是硬件把SDA和SCL焊反了结果扫描不到设备用示波器看波形又都是正常的最后拿万用表一量才暴露问题。如果设备能被扫到下一步是手动读取寄存器验证RMI4协议是否正常响应。直接读RMI4的F01控制寄存器i2cget -y 2 0x20 0x00正常时会返回一个非零值比如0x01表示设备ID相关寄存器可读。不过要注意i2cget默认用的是单字节读而很多RMI4寄存器需要块读block read才能拿到完整数据这是驱动里用i2c_transfer和普通i2c_smbus_read_byte_data的一个重要区别调试时要留意工具和驱动的读取方式是否一致。3.2 中断不触发的排查套路很多触摸失灵的问题实质是中断根本没进来。判断方法特别简单粗暴看/proc/interrupts里对应中断号的计数有没有在触摸时增加cat /proc/interrupts如果计数不涨问题大概率在硬件中断路径。按优先级排查先看GPIO配置确认触摸芯片的INT引脚没有被别的外设复用检查设备树里的pinctrl配置是否正确再看中断触发方式触摸芯片一般用下降沿触发IRQ_TYPE_EDGE_FALLING如果设备树配成了高电平触发只有在手指按住不放时才会触发一次表现为“点了没反应”或“反应极其迟钝”最后看中断有没有被其他驱动抢先注册同一GPIO被两个驱动使用的情况在复杂板卡上并不少见。如果计数在涨但触摸还是没反应那问题就从“中断没进来”变成了“中断进来了但没读对数据”。这时候要在驱动里加调试打印打印每次attention report的原始字节跟芯片手册对照看是否合理。最典型的问题是I2C读时序不对导致所有寄存器读出来都是0xFF或0x00这时候驱动会误以为没有触摸事件。3.3 坐标错乱与参数整定的实用技巧触摸能动了但坐标不对、方向反了这是适配期最常遇到的问题。方向对调通常不需要改代码。如果驱动用的是标准的input子系统上报用户空间可以用libinput的配置来翻转坐标。但更好的做法是在设备树或驱动里直接配好一劳永逸。F11/F12的寄存器里通常有X翻转、Y翻转、XY交换的配置位每个Synaptics芯片的手册都会写清楚。调试时可以用一个笨办法拿一支笔按住屏幕的左上角看系统上报的坐标是哪个象限然后根据偏差去设置对应的翻转位。比如左上角上报成了数值最大的坐标那X方向就需要翻转。如果左上角上报成了右下角的坐标那XY都需要翻转。还有一个容易忽略的是分辨率。触摸芯片报告的坐标范围不一定跟屏幕的分辨率一样比如屏幕是1080x1920但芯片上报的坐标范围只有1000x1750。这时候需要在驱动里给input设备设置正确的abs参数input_set_abs_params(input, ABS_MT_POSITION_X, 0, 1080, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 1920, 0, 0);如果嫌改代码麻烦也可以在设备树里通过touchscreen-size-x和touchscreen-size-y属性指定。但注意有些内核版本对这两个属性的解析依赖特定的驱动补丁老内核不一定支持最后还是得落到代码里改。3.4 SPI接口下容易忽略的时序参数SPI接入的Synaptics芯片调试时重点盯芯片手册里的CPOL和CPHA要求。我遇到过SPI模式识别全部正常、寄存器也能读但上报的坐标总是有规律地跳变折腾了半天最后发现是设备树里少了spi-cpol或spi-cpha导致时钟极性和相位跟芯片要求的不匹配。另外SPI速率不要一上来就调到最高。有些工程师习惯把spi-max-frequency直接写到8MHz或更高觉得SPI就该快。但触控芯片的SPI从机设计并不都是高速器件我实际测过一颗老芯片2MHz以下一切正常4MHz就开始偶发读错字节。稳妥的做法是从1MHz起步确认通信稳定后再逐步往上调直到找到性能和数据可靠性的平衡点。4. 常见问题与排查技巧实录4.1 问题速查表把我在实际项目中遇到的问题按出现频率排序做成一张速查表遇到相同的症状可以直接对号入座现象可能原因排查手段解决方向i2cdetect扫不到设备供电/复位/上拉/焊反万用表测电源、复位示波器看波形修硬件或改设备树地址扫描到设备但触摸无效中断没注册或没触发看/proc/interrupts计数检查GPIO配置和触发方式中断有计数但无报点attention report读错打印原始寄存器数据检查总线路由、块读时序坐标方向错乱X/Y翻转位未配置笔点角落对照坐标设置F11/F12翻转寄存器偶发失灵、要复位才好总线干扰或时序裕量不足降速、缩短走线、加重试降低时钟频率、增加驱动重试休眠唤醒后无触摸睡眠时供电或IO状态丢失查休眠流程、GPIO状态在resume里重新初始化芯片多点触控不生效内核没开MT协议B查内核config开启CONFIG_INPUT_MT_PROTOCOL_B4.2 典型问题复盘一个I2C时钟拉伸导致的“幽灵触摸”有一个项目让我印象特别深。触摸偶尔会自己乱跳像是有个看不见的手指在屏幕上乱点而且毫无规律。一开始怀疑是触摸芯片本身的噪声干扰加了各种滤波参数都没用。后来用示波器抓I2C波形才发现SCL线上偶尔会出现一个异常的低电平保持时间比正常的半个时钟周期长得多明显是I2C的时钟拉伸clock stretching现象。是主控的I2C控制器不支持时钟拉伸而触摸芯片在某种情况下确实会通过拉低SCL来请求主控等待。解决办法是在设备树里把I2C时钟频率从400kHz降到100kHz给双方留出更多的时序裕量。改完之后问题再没出现过。这个case给了一个重要教训掉到“参数调优”的坑里之前先把物理层的时序问题查干净。4.3 固件与初始化顺序的坑最后提醒一个很多人会忽视的点触摸芯片驱动跟其他外设驱动的初始化顺序问题。如果芯片的复位引脚接到了某个GPIO扩展器上而这个扩展器的驱动加载比触控驱动晚那触控驱动probe时复位脚还没被正确配置芯片可能处于不稳定的复位状态。这类问题多发生在带复杂PMIC和GPIO扩展器的平台上排查思路是看内核启动日志里驱动probe的顺序必要时在设备树里通过depends-on或调整驱动加载顺序来解决。但说实话此类问题的根治方案往往不是改设备树而是把复位逻辑从probe里挪到中断首次触发前或者干脆在用户空间通过一个初始化脚本来控制上电时序。5. 移植经验与几个实操心得5.1 从旧内核移植到新内核时最容易踩的坑很多还在维护的产品线用的是老内核3.x、4.x新项目想要平移到5.x或6.xSynaptics驱动这块有几个已知的变化点。老内核里有些厂商把Synaptics驱动放在drivers/input/touchscreen/下自己维护代码风格跟上游RMI4框架差异很大直接替换成新框架需要重新验证F11/F12的寄存器映射是否一致。另外新内核里I2C子系统对设备树的支持更加严格老的节点写法可能直接报错。移植过程中如果发现probe不被调用先查设备树中compatible是否在内核源码里查得到查不到就说明驱动编进去的方式不对。5.2 善用内核提供的调试开关RMI4驱动在debugfs下可以导出一些有用的信息。挂载debugfs后在/sys/kernel/debug/rmi4/下能找到功能模块的寄存器dump这对确认芯片内状态非常有帮助。此外内核的dynamic_debug机制可以动态打开驱动里的dev_dbg打印省去重新编译内核的麻烦echo file drivers/input/rmi4/* p /sys/kernel/debug/dynamic_debug/control这套操作在调试现场非常实用。很多板子不带编译环境能通过内核启动参数dyndbgfile drivers/input/rmi4/* p开启打印就少了一趟来回折腾的功夫。5.3 关于驱动稳定性的一点个人体会做驱动这东西很多时候不是调通了就完事而是要预判用户会在什么极端情况下使用。比如触摸屏在低温环境下I2C通信可能变慢芯片的响应时间会变化总线速率是否还有裕量再比如用户可能插着USB充电器触摸充电器的噪声会不会干扰I2C信号这些问题在实验室不见得能复现但一旦出货就会出现各种灵异事件。我的习惯是测试阶段专门做一轮总线和时序的裕量测试人为降低供电电压、拔掉屏蔽层、用长排线连接看驱动还能不能稳定工作。驱动代码里也要加上必要的通信重试机制一次I2C读失败不应该导致整个触摸功能挂死。最后再分享一个实用小技巧。量产阶段如果发现某些批次的触摸屏在特定固件版本下表现不一致可以直接在驱动里把F34的固件版本信息打印出来同时把触摸芯片的product ID和ROM版本号一起上报到内核日志。这样售后反馈问题时直接看日志就能判断是哪一批硬件、哪一版固件排查效率能提高一个量级。驱动开发看着是跟代码较劲实际上很多时候拼的是排查思路和对硬件的理解深度。本文还有配套的精品资源点击获取
返回列表