尧图精选

FPGA+Linux驱动7寸GT911触摸屏:从设备树到应用层全流程解析

🕒 发布时间:2026/9/16 17:39:29 📁 来源:尧图网络
最近在折腾黑金的FPGA开发板想把一块7寸触摸屏在Linux侧完整跑起来查了一圈发现网上资料散得厉害要么只讲RGB屏幕点亮的驱动要么只给个i2c探测命令真正从设备树到内核模块再到应用层触摸事件串起来的笔记少之又少。这篇就把我这次“FPGALinux7寸触摸屏驱动”整个坎途梳理一遍从方案选型到踩坑排错都写清楚给后面做类似项目的朋友一个能直接抄作业的参考。先交代一下平台背景我用的是一块黑金的ZYNQ系列FPGA开发板PL端是自己做的FPGA逻辑PS端跑了Linux系统触摸屏是一块7寸1024x600的RGB接口屏触摸部分是电容式GT911方案。整个任务说白了就是三件事让Linux内核识别触摸控制器、把触摸数据上报到输入子系统、让上层应用能读到干净的触摸事件。这套思路放在黑金、米联客、正点原子的FPGA SoC板上基本通用如果你是做其他触摸IC或者用纯ARM Linux逻辑也一样。1. 项目整体设计与方案选型1.1 为什么要在FPGA上跑Linux做触摸屏驱动很多刚接触FPGA的朋友会有个误区觉得FPGA就是拿来写Verilog、做时序逻辑的触摸屏驱动这种“软件活”应该交给ARM或者单片机。但实际上在ZYNQ这类SoC平台上PS处理系统和PL可编程逻辑是共存的ARM核完全可以跑完整的Linux系统PL端用FPGA做并行接口转换、图像预处理或者自定义外设。触摸屏驱动放在Linux侧来做本身就是这套架构里最自然、性价比最高的分工。我这次的具体硬件连接是触摸屏通过FPC排线接到一块转接板上转接板再引出I2C信号到ZYNQ的PS端I2C控制器中断脚和复位脚分别接在PS端的GPIO上。屏幕的RGB显示信号走的是PL端由FPGA产生像素时钟和数据触摸这部分独立走PS端的总线。这样设计的好处是驱动任务全部落在Linux侧可以用现成的内核框架和调试工具不用在PL端自己造轮子去解析触摸协议。1.2 触摸屏方案怎么选电容、电阻还是红外7寸触摸屏市面上能碰到的主流方案大概有三种电容式典型IC是GT911、FT5x06系列、电阻式典型IC是XPT2046、ADS7846、红外式典型方案是TOUCHKIT支持的红外触摸框。我这次用的是电容式GT911理由很直接7寸RGB屏配电容触摸是工业平板和HMI最常见的组合GT911支持十点触控价格便宜Linux内核里也有对应的驱动省去不少移植功夫。电阻屏的好处是便宜、抗干扰强但只支持单点而且需要校准用户体验差一些。红外触摸框则适合大尺寸屏幕7寸这个规格用红外方案的性价比不高而且红外触摸对边框结构要求比较高小尺寸很少见。如果你拿到手的屏幕是TOUCHKIT红外方案那就要在驱动配置上走的是另一条路这里先不展开后面有时间单独写一篇。核心经验是定方案之前先确认触摸IC的型号然后把Linux内核源码里drivers/input/touchscreen/目录翻一翻看看有没有现成的驱动文件。有就直接配置没有就要掂量一下自己改驱动的成本。我这次就是先在内核里查到了gt9xx.c心里才踏实下来的。2. 触摸屏驱动核心细节解析2.1 触摸IC上电时序和I2C地址确认GT911这个IC看起来简单但上有两个坑特别容易踩上电时序和I2C设备地址。GT911的地址可以通过引脚电平来配置上拉电阻接高是0x5D接低是0x14也有芯片固定是0x28、0x5D之类的。我试过直接在系统里跑i2cdetect结果发现明明连线正确却检测不到设备后来查手册才发现复位时序不过关芯片压根没起来。GT911的复位时序大概是这样的INT引脚先拉高复位引脚拉低保持至少10ms然后复位拉高再等至少50ms最后INT引脚切换成输入模式或者根据实际电路配置电平。这套时序用GPIO模拟就行但顺序不能错。如果只是把I2C两根线接上就急着探测大概率是空手而归。我当时就是把复位时间从10ms加到100ms又把INT的上下拉关系重新理了一遍i2cdetect才终于扫出了地址。关于I2C地址实际工作中最常见的坑是驱动里写死了一个地址但你的硬件上IC的地址选择脚接法不一样导致驱动匹配失败。调试方式很简单i2cdetect扫出来的地址和驱动源码里I2C_CLIENT_END之前的地址对比一下不一致就改设备树或者驱动别死磕。2.2 设备树怎么描述触摸屏节点设备树是Linux下硬件描述的核心触摸屏挂在哪个I2C控制器上、中断脚是哪个GPIO、复位脚是哪个GPIO、坐标轴是否翻转这些都要写清楚。我板子上的触摸IC挂在I2C0上设备树节点大致长这样i2c0 { status okay; clock-frequency 100000; gt911: gt9115d { compatible goodix,gt911; reg 0x5d; interrupt-parent gpio0; interrupts 54 2; irq-gpios gpio0 54 GPIO_ACTIVE_HIGH; reset-gpios gpio0 53 GPIO_ACTIVE_HIGH; touchscreen-inverted-x; touchscreen-inverted-y; touchscreen-swapped-x-y; }; };这里几个属性值得多说两句。reg是I2C地址必须和芯片实际配置对上interrupt-parent和interrupts决定中断脚有的板子上中断脚并不直接映射到SoC的中断控制器而是接到PL端做转接那这里的写法就完全不一样得先确认硬件的中断通路irq-gpios和reset-gpios是驱动里做复位时序用的GPIO编号要根据原理图对着SoC的引脚表换算。还有一点很隐蔽GT911支持读取坐标但坐标轴方向和屏幕的物理安装方向经常不一致比如屏装上去是竖着的但逻辑上希望是横着的坐标。设备树里的touchscreen-inverted-x、touchscreen-inverted-y、touchscreen-swapped-x-y就是为了解决这个问题的但不同内核版本的驱动对这几个属性的解析可能不同我就在某个版本上遇到过属性写进去没用最后只能改驱动代码。2.3 Input子系统上报链路Linux触摸屏驱动的最终目标是向/dev/input/eventX上报触摸事件这一层走的是内核的Input子系统。驱动里做的事情概而言之就是在驱动加载时分配一个input设备设置好事件类型和坐标范围然后在中断处理函数或者轮询任务里读取触摸坐标通过input_report_abs上报ABS_X、ABS_Y、ABS_MT_POSITION_X这类绝对坐标事件再配合input_sync通知上层这一帧数据同步完成。GT911支持多点触控所以驱动里一般用的是Type B协议需要上报ABS_MT_SLOT来区分触点ID。上层应用如果用Qt或者GTK这些细节已经被封装好了直接用QTouchEvent或者通过libinput读设备节点就行但如果是自己写C程序读event节点就得解析struct input_event里的type、code、value三要素。调试阶段最常用的工具是evtest跑起来能看到每一个事件的实时打印比任何文档都直观。我自己的体会是驱动只要能过evtest这一关触摸屏驱动就算基本通了剩下的坐标翻转、多点稳定性的微调都是时间问题。遇到evtest里事件完全不出来优先查中断有没有触发事件有但坐标乱跳优先查电源地线和屏排线的屏蔽情况。3. 实操过程与核心环节实现3.1 内核配置与GT911驱动编译Linux内核编译是整个流程里最见功底的环节。我用的是黑金光盘里配套的内核源码版本是4.xGT911的驱动在CONFIG_TOUCHSCREEN_GT9XX这个配置项下面。进入内核目录后执行make ARCHarm menuconfig然后在Device Drivers - Input device support - Touchscreens里找到Goodix GT9xx based touchscreen直接编成模块M或者编译进内核Y都可以。我自己习惯编成模块方便调试阶段单独替换不折腾整个内核镜像。如果menuconfig里找不到这个选项大概率是内核源码里根本就没有这个驱动文件要么换内核版本要么把驱动源码手动放到drivers/input/touchscreen/目录下然后补Kconfig和Makefile。这一步对新手很不友好但也不是没法解决直接把驱动文件复制进去在Kconfig里仿照其他触摸屏驱动加两行配置项在Makefile里加一行obj-$(CONFIG_TOUCHSCREEN_GT9XX) gt9xx.o然后再回去make menuconfig就能看到了。配置完成之后开始交叉编译我的工作环境是Ubuntu虚拟机交叉编译工具链用的arm-linux-gnueabihf-gcc。编模块的时候要注意内核源码的版本和工具链版本匹配不然编出来加载的时候会报版本不一致、unknown symbol之类的错。export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf- make modules编完模块后在源码目录下能找到drivers/input/touchscreen/gt9xx.ko这个文件就是我们要的东西把它拷贝到开发板的/lib/modules/对应目录或者直接用insmod加载就行。3.2 设备树编译和启动验证修改完设备树源码之后要编译成dtb文件烧到启动分区里。黑金ZYNQ的启动方式是BOOT.bin加image.ub加dtb的组合设备树编译命令是dtc -I dts -O dtb -o devicetree.dtb devicetree.dts有的黑金资料里会给一个设备树工程目录直接在里面改然后编译也方便。如果不想重新打包整个启动镜像有些板子支持从SD卡单独加载dtb调试阶段特别实用改一次设备树不用反复擦写整个启动系统。上电启动之后第一件事就是看内核日志确认I2C控制器起来了、触摸屏驱动有没有probe成功dmesg | grep -i goodix dmesg | grep -i i2c正常情况会看到类似goodix gt911:成功初始化这样的日志。如果dmesg什么都看不到先用i2cdetect确认设备在不在总线上i2cdetect -y -r 0命令最后的0是I2C总线编号要确认你的触摸屏挂的是哪一路。看到设备地址出现在表格里说明硬件通路通了问题在驱动配置什么都没扫到问题基本在硬件或者复位时序。有一次我扫不到设备排查半天发现是I2C总线上拉电阻没焊两根线直接悬空这种问题看一眼原理图就能定位别一上来就怀疑驱动。3.3 evtest验证触摸事件设备树和驱动都就位之后检查一下输入设备节点有没有生成cat /proc/bus/input/devices找到Name里带Goodix或者Touchscreen的那一项记下/dev/input/eventX的编号。然后安装evtestapt install evtest evtest /dev/input/event1这时候用手指在屏幕上滑动终端里应该会刷出一堆type 3 (EV_ABS)的事件坐标值会跟着手指位置变化。如果什么事件都没有但/proc/bus/input/devices里已经有设备节点大概率是中断没触发。我当时就卡在这后来发现是设备树里GPIO的active level写反了中断一直处于错误的电平状态驱动压根收不到通知。如果evtest里能出坐标但位置对不上就是坐标翻转的问题。GT911的驱动源码里通常有gt911_x_to_capacity这类坐标换算函数可以临时加打印或者直接改翻转逻辑不过更推荐的方式是先去设备树里把touchscreen-inverted-x、touchscreen-inverted-y、touchscreen-swapped-x-y这三个属性组合调一遍大多数情况能解决实在不行再动代码。3.4 应用层读取触摸数据触摸屏驱动的最终使用者是应用层的GUI程序。如果你跑的是Qt在Linux下Qt默认走libinput或者evdev插件驱动注册的设备节点会被自动识别不用额外配置。但如果你是在裸环境下自己写读取程序可以直接读event节点#include linux/input.h #include fcntl.h #include stdio.h #include unistd.h int main(void) { int fd open(/dev/input/event1, O_RDONLY); struct input_event ev; if (fd 0) { perror(open); return -1; } while (1) { read(fd, ev, sizeof(ev)); if (ev.type EV_ABS) { if (ev.code ABS_X || ev.code ABS_MT_POSITION_X) printf(X%d\n, ev.value); else if (ev.code ABS_Y || ev.code ABS_MT_POSITION_Y) printf(Y%d\n, ev.value); } } return 0; }这个程序是最基础的读取示例实际项目里还要处理多点触控、按下和释放事件、触点的生命周期管理复杂性会上去不少但底层的驱动打通了应用层的事情就只是时间问题。我在实际项目里一般不会自己写这层解析直接用现成的GUI框架或者tslib这类触摸库稳定性比自己轮子好太多。4. 常见问题与排查技巧实录4.1 I2C总线上扫不到触摸设备这是出现频率最高的问题。常规检查路径按顺序大概是先测I2C总线上的上拉电阻有没有贴、SCL和SDA电平是否正常、触摸屏的供电有没有到位、复位引脚电平对不对、INT引脚有没有被拉死。GT911最麻烦的地方是如果复位时序不对芯片会一直待在异常状态I2C上怎么扫都是空的。我自己的排查经验是先不看驱动用示波器或者万用表确认一下INT引脚在上电后有没有一个明显的跳变有跳变说明芯片在试着请求主机读取数据链路大概率是通的没有跳变就老老实实去看复位时序。软件模拟复位的时候驱动里有个goodix_i2c_reset之类的函数可以加打印看它每个步骤执行到哪了比盲猜效率高得多。另外有个容易被忽略的点是I2C控制器的时钟频率。GT911最高支持400kHz但有的板子默认配到了1MHz芯片可能受不了导致通信不稳定。设备树里把clock-frequency降到100kHz试试往往能解决一些莫名其妙的偶发问题。4.2 设备节点存在但evtest没事件/proc/bus/input/devices里明明有设备但evtest一点反应都没有这种问题的根源通常在中断。要么中断根本没有触发要么中断请求失败。先用cat /proc/interrupts看看对应的中断号有没有计数用手指快速点屏幕的同时观察计数有没有增加。计数在涨说明中断通了问题在上报逻辑计数不动问题在中断配置或者硬件通路。还有一种情况是驱动注册了input设备但始终没有使能触摸屏的中断原因是驱动里的probe逻辑没走完某个GPIO请求失败导致整个初始化提前返回。这种问题dmesg里一般会有err级别的日志仔细看goodix相关的输出基本上能定位到具体是哪个资源没申请到。实在不行可以在驱动源码里临时加printk在probe、open、中断处理函数的入口各打一条跑一遍看执行流程到底断在哪一步。调试内核模块不比调试普通程序printk是最简单也最有效的武器。4.3 坐标反向和偏移的处理坐标反向的解决办法前面提过优先在设备树里调翻转属性。但要注意驱动对属性的命名在不同内核版本有差异比如4.4内核用的touchscreen-inverted-x有些版本则是touchscreen-inverted-y的解析方式相同另外还有swap-x-y和touchscreen-swapped-x-y的差别。最简单的办法是把驱动源码里of_property_read_bool搜一遍看它到底认哪些属性。坐标偏移往往和触摸屏的原始分辨率、驱动初始化的坐标范围有关。GT911的坐标范围出厂默认可能是根据屏幕尺寸自动配置的但如果你用的是非标准分辨率驱动里读出来的坐标范围就和屏幕实际像素对不上表现为触点总在某个角落或者坐标整体偏掉。你可以检查一下/sys/bus/i2c/devices/0-005d/下面的配置文件看看驱动有没有导出坐标范围的调试接口。另外还有一个非常现实的问题电源纹波大、地线没接好触摸坐标会漂得有规律比如不触摸的时候还在上报坐标或者滑动轨迹呈锯齿状。这种问题的排查方向是电源和布线而不是软件我之前在一台电源适配器很差的工控机上遇到过换了个正规电源直接就好了。4.4 驱动加载时提示版本或符号问题内核模块加载报version magic不匹配或者Unknown symbol这种问题在新手阶段特别常见。版本魔数不匹配通常是因为模块是用不同版本的内核源码编出来的或者交叉编译工具链版本不一样导致的内核配置差异。解决方法是把内核源码和实际运行的内核二进制严格对应上最好在开发板上直接确认一下uname -r的结果然后回头在源码目录里核对。Unknown symbol则常见于模块依赖其他内核符号但那个符号没有被导出或者依赖的模块还没加载。先看一下具体缺哪个符号用grep在内核源码里搜这个符号的EXPORT_SYMBOL声明如果是依赖别的模块就把依赖模块一起加载。GT911这个驱动依赖的符号不多一般不会出这类问题但我在做其他外设驱动的时候遇到过几次经验是先保证所有内置模块都加载完整再单独装外围的ko。这些小毛病的排查思路其实都大同小异看日志、追符号、查配置。内核调试没有捷径但每一步都有迹可循静下心来一层层排查总能找到问题。5. 一些提升开发效率的小建议5.1 用好内核自带的调试工具Linux内核为触摸屏调试提供了不少现成工具除了evtest之外lsinput、input-utils、hexdump /dev/input/eventX这些都可以用。当evtest不方便安装的时候直接用hexdump读event节点是最朴素的验证方式hexdump -C /dev/input/event1手指按下去就能看到一串二进制输出type、code、value都在里面数据对不对一目了然。内核日志级别也要学会灵活调整echo 8 /proc/sys/kernel/printk可以打开更详细的日志驱动里用dev_dbg打的调试信息就能看到了。我在调试阶段还会用strace去跟踪应用程序打开input设备节点时的系统调用确认应用层有没有因为权限、路径等原因打不开设备。有时候应用连不上触摸屏不是驱动的问题而是/dev/input/eventX的文件权限问题加个udev规则把权限放一下就好了。5.2 模块化开发驱动和应用同步迭代触摸屏驱动开发不是一个纯软件的活它和硬件、内核、应用三个层面都有关系。我的习惯是把整个任务拆成三个里程碑第一个里程碑是i2cdetect能扫到设备代表硬件通路通了第二个里程碑是evtest能看到坐标事件代表内核驱动通了第三个里程碑是GUI里能滑动触摸代表应用层集成了。每个里程碑之间相互独立卡住的时候定位范围会小很多。模块化加载的方式也很重要。驱动编成.ko第一次加载用insmod出了问题直接rmmod再insmod改代码重新编译后不用重启系统就能验证新驱动。等彻底稳定了再把驱动编进内核或者放到开机自动加载的脚本里。这个小技巧在开发阶段能省下大量反复烧写系统的时间强烈推荐。5.3 关于黑金开发板的资料活用黑金开发板自带的资料里其实已经有了很多细碎的知识点比如Linux内核编译环境的搭建、设备树的语法介绍、触摸屏原理图的解析这些东西分散在多个文档里做项目的时候要认真翻一遍。遇到问题时代码就是最大的文档drivers/input/touchscreen/gt9xx.c里面的注释和宏定义能告诉你很多源码层面的设计意图比网上的二手教程可靠得多。最后再说一个我自己的体会触摸屏驱动在嵌入式Linux里算是入门级的驱动程序但它串起来的I2C、GPIO中断、设备树、Input子系统、内核模块编译这些知识点几乎覆盖了嵌入式Linux开发的全部核心内容。把这块彻底吃透后面再做其他外设驱动流程基本都是一样的套路。这次在这个项目上从零到点亮触摸屏前后花了两天时间大部分时间消耗在复位时序和中断配置上。如果你动手做的时候也卡住了别气馁先确认硬件时序对不对再确认中断通路有没有断最后才去怀疑驱动代码这个排查顺序能帮你少走很多弯路。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →