资讯动态

Zynq FPGA开发板7寸触摸屏Linux驱动移植与设备树实战

发布时间:2026/9/13 15:14:49 来源:尧图企业网站定制
手头这块黑金 Zynq 开发板是我把 FPGA 和 Linux 真正串起来做的第一个完整项目PL 侧逻辑负责给 7 寸屏输出 RGB 显示时序ARM 侧跑嵌入式 Linux通过 I2C 接口与触摸屏控制芯片通信最后再由设备树、内核驱动、触摸校准工具配合起来手指才能在屏幕上划过时系统坐标精准跟随。触摸屏驱动听起来只是“外设驱动”但放在 FPGA 开发板上它牵扯到的其实是整套软硬件协同设计的链路。为了把这个项目写清楚我不会只盯着设备树里那几行配置而是把从硬件连接、内核源码到实测调试的完整过程都过一遍。这样不管是刚接触 Linux 驱动开发的 FPGA 玩家还是正在为屏幕点击没反应发愁的嵌入式工程师都能直接对照自己的板子找到问题的突破口。1. 为什么这个项目值得写FPGA、Linux、触摸屏不是三个考点而是整条链路1.1 很多 FPGA 学习者对“Linux 驱动”的理解偏差我在学习 FPGA 的第二年对 Linux 驱动的认知基本停留在“在 PC 上装个模块、insmod 一下”的阶段。拿到带 7 寸触摸屏的开发板之后我以为要做的事情无非是找到一篇“触摸屏驱动移植笔记”然后把几个文件放到内核里。真到动手才发现事情远没有那么简单。你需要先搞清楚这块 7 寸屏到底通过什么接口和 FPGA 连接是 RGB 并口还是 MIPI 接口要确认触摸控制芯片是 GT911 系列还是 FT5x06 系列它们的 I2C 地址分别怎么选还要在 Linux 设备树里把 GPIO 的中断引脚、复位引脚写到与板卡原理图完全一致的位置最后内核驱动加载后还得处理坐标方向、分辨率、多点触摸上报格式这些问题。一个“触摸屏驱动”项目实际上覆盖了显示链路、输入子系统、设备树、内核裁剪、总线通信五个层面。这也是为什么很多人在网上搜“7寸触摸屏 Linux 驱动”发现资料很分散每一篇都只说了一小部分。我希望这篇文章能把这些碎片拼成一张完整的图。1.2 这类项目的系统框架和工作职责划分在 Zynq 这类 SoC FPGA 上芯片内部天然分成 PS 和 PL 两个部分。PS 侧是双核 Cortex-A9 处理器跑 Linux 系统负责人机交互、协议处理和文件系统PL 侧则是可编程逻辑用来做高速并行、定制时序和显示输出这类任务。7 寸触摸屏项目划分下来大概是这个结构PL 侧生成 LCD 控制器时序通过 VDMA 把帧缓存数据传输到 RGB 接口屏幕上才能看到 Linux 桌面或 Qt 界面PS 侧通过 I2C 控制器和触摸芯片通信读取触摸坐标中断引脚来了之后触发驱动处理设备树层把“哪个 I2C 总线、哪个 GPIO 引脚、屏幕分辨率多少”这些硬件信息告诉内核内核驱动层利用 input 子系统把触摸数据上报给上层用户程序读到的是标准化的 ABS_MT 事件。很多新手容易犯的错误是把“驱动”理解成单片机里的那一个while(1) { read_touch(); }函数。在 Linux 系统里硬件驱动只是内核与外界打交道的最小单元还需要设备模型、中断子系统、输入框架一起配合才能真正无所谓地跑起来。2. 硬件链路整理显示引脚、I2C、中断和复位引脚的相互约束2.1 显示链路FPGA 逻辑生成的 RGB 时序与设备树 timing动手写代码之前我建议先把板子的硬件连接关系彻底理一遍。7 寸屏和 FPGA 开发板的连接通常是一个 40Pin 或 50Pin 的 FPC 排线引脚大致分为三类RGB 数据线、时钟和同步信号、触摸相关的 I2C 与 GPIO。显示侧的 RGB 接口一般包括 R/G/B 各 5 bit 或 6 bit加上 PCLK、DE、HSYNC、VSYNC总共差不多 20 多根线。FPGA 的 PL 侧逻辑里会有一个“LCD 时序控制器”IP它源源不断地把帧缓存数据按行场同步的节拍输出到这些引脚上面。在 Linux 的设备树里对应显示节点会有一段像这样的参数display-timings { native-mode timing0; timing0: timing0 { clock-frequency 51200000; hactive 1024; vactive 600; hfront-porch 160; hback-porch 140; hsync-len 20; vfront-porch 12; vback-porch 20; vsync-len 3; de-active 1; pixelclk-active 1; }; };这些值不是随便填的而要看 7 寸液晶面板的数据手册。FPGA 里生成的时序和 Linux 设备树里描述的时序必须完全一致否则屏幕会出现偏色、闪烁甚至直接白屏。时钟频率这里写的 51.2MHz是 1024x600 分辨率 60 帧刷新率下比较典型的像素时钟。2.2 触摸链路GT911 的 I2C 接口和 GPIO 的“二重身份”触摸部分我手头这块 7 寸电容屏用的是 GT911 触摸控制芯片。它对外提供一组 I2C 接口还有两根特殊的 GPIO一根是复位脚另一根是中断脚。这里有一个关键点GT911 的 I2C 地址不是固定的它由中断引脚在上电时的电平决定。如果 INT 引脚被拉低芯片地址通常是 0x287 位地址视角很多资料写成 0x5d其实是因为用了 8 位地址写法如果 INT 引脚拉高则地址变成 0x29或 0x5c/0x5a 这种 8 位写法。在原理图上中断引脚既要承担“告诉 Linux 有触摸事件”的作用又要参与“选择芯片 I2C 地址”的过程。这看似矛盾实际上靠的是上电时序芯片复位结束瞬间INT 引脚的电位决定地址之后 INT 引脚就切换为中断输出功能。所以复位引脚和中断引脚的时序配合非常重要驱动里必须有合理的 reset 流程。2.3 从原理图反推设备树信息哪些空格是必须填的在 Linux 设备树里描述触摸屏实际上就是把原理图上那几个关键连接翻译成内核认识的属性。具体来说你需要确认四件事触摸芯片接在 PS 端的哪一路 I2C 上是 I2C0 还是 I2C1中断信号接到哪一个 GPIO 银行口的第几号引脚复位信号接到哪个 GPIO触摸屏的分辨率是多少是否需要在驱动层做坐标反转。这些信息在板卡的原理图里都能查得到。比如我这边触摸屏的 INT 引脚接到了 PS 端 GPIO 的第 13 脚那就是interrupts 0 13 IRQ_TYPE_EDGE_FALLING复位脚接的是第 12 脚写为reset-gpios gpio0 12 GPIO_ACTIVE_LOW。不同板子接法不同一定要对照着自己的原理图来改直接抄网上别人的配置基本都会翻车。3. 设备树下 Linux 对触摸屏的“出厂配置”3.1 触摸屏节点的完整写法设备树是硬件板级配置与内核驱动的分界线。针对 GT911我在设备树里加入的节点大致长这样i2c0 { status okay; clock-frequency 100000; gt9115d { compatible goodix,gt911; reg 0x5d; interrupt-parent gpio0; interrupts 13 IRQ_TYPE_EDGE_FALLING; reset-gpios gpio0 12 GPIO_ACTIVE_LOW; irq-gpios gpio0 13 GPIO_ACTIVE_HIGH; touchscreen-size-x 1024; touchscreen-size-y 600; touchscreen-inverted-x; touchscreen-inverted-y; }; };这里面的compatible属性决定了内核到底匹配哪一个驱动。goodix,gt911对应的是 Linux 内核源码里drivers/input/touchscreen/goodix.c这个文件。如果你用的触摸芯片是 FocalTech 的 FT5x06compatible就要改成edt,edt-ft5x06一类的值这是新手最常踩的坑。reg 0x5d表示 I2C 从机地址。前面说过GT911 的地址和 INT 引脚的电平有关系所以实际使用时先用 i2cdetect 扫描确认一下最靠谱代码如下i2cdetect -y 0如果主机控制器是 I2C0用-y 0如果是 I2C1用-y 1。正常扫描结果里应该能看到一个设备编号那个才是你设备树里要填的地址。3.2 设备树属性背后的驱动加载逻辑设备树里每一行属性驱动加载时都会去读取。拿上面的节点举例interrupt-parent表示中断控制器是谁在 Zynq 上通常是gpio0或intcinterrupts里的第一个数字代表中断在芯片内部的编号13 这个编号对应引脚编号具体映射关系和引脚复用表有关reset-gpios指定复位引脚驱动在初始化阶段先把复位脚拉高再拉低让 GT911 完成一次上电复位irq-gpios指定中断输入引脚驱动运行时要靠它判断“有没有手指按下”。有件事我要特别提醒Zynq 的 GPIO 有 MIO 和 EMIO 之分。MIO 引脚是 PS 端固定的引脚接管后不能再被 PL 侧逻辑使用EMIO 则是 PS 和 PL 之间的桥接通道可以扩展到 FPGA 的引脚上。如果你把触摸屏的 GPIO 接到了 EMIO设备树里的interrupt-parent可能就不再是gpio0了而是gpio1。这类细节在原理图里都能看出来连接前一定要确认。3.3 坐标反转属性什么时候加 inverted-x/yGT911 本身是个支持 I2C 配置的触摸芯片它内部寄存器里存有 X/Y 的最大分辨率。驱动初始化时会读这些值然后按比例关系把触摸坐标换算成屏幕坐标。如果你的屏幕摸着方向反了可以在设备树里加touchscreen-inverted-x和touchscreen-inverted-y这两个布尔属性或者反过来去掉它们。我只踩过一次这个坑屏幕明明没有翻转设备树里却被我多写了两个反转属性结果手指往左滑光标往右跑一度以为是驱动算错。去掉属性后问题立刻消失。设备树改完后还要把它编译成 DTB 并打包进启动镜像常见的做法是在 U-Boot 环境下把新的devicetree.dtb写入 FAT 分区setenv fdt_file devicetree.dtb saveenv改完设备树必须重启系统/proc/device-tree下对应的信息才会更新。不要指望像模块一样热插拔一下驱动就能生效。4. 内核驱动从源码入口到事件上报的完整路径4.1 内核配置项和触摸屏驱动源码位置设备树只是完成了“硬件长什么样”的描述真正干活的是内核驱动。GT911 的驱动在 Linux 内核里属于输入子系统下的触摸屏类别对应的配置项是CONFIG_TOUCHSCREEN_GOODIX。编译内核前先确认这个选项有没有被编译成模块或编译进内核Device Drivers --- Input device support --- [*] Touchscreens --- * Goodix I2C touchscreen如果你的内核版本或 BSP 里已经有这个目录也可以直接使用。没有的话可能需要从上游内核把goodix.c拉过来并且补充对应的 Kconfig 和 Makefile 配置。4.2 驱动初始化流程上电、复位、读配置、注册中断大概梳理一下goodix.c的初始化流程这对理解整个系统非常有帮助。驱动首先解析设备树节点里的reset-gpios和irq-gpios然后执行复位操作把复位脚拉低等待一段时间再拉高。这一步是为了让 GT911 重新进入可配置状态同时根据 INT 引脚的当前电平确定 I2C 地址。接下来驱动会尝试读取芯片内部信息。GT911 的寄存器地址是 16 位的在 I2C 通信时先发送寄存器高字节再发送低字节然后才能读写数据。驱动通过读取触摸屏的 X/Y 最大分辨率实际上是在和芯片“确认身份”。如果 I2C 通信异常这里就会直接返回错误dmesg里通常能看到类似goodix i2c-goodix: i2c test failed的日志。最后一步是注册中断并创建输入设备。中断触发方式一般配置为下降沿触发手指按下的瞬间GT911 会通过 INT 引脚给 CPU 一个信号驱动进入中断回调从芯片寄存器里读取当前触摸点信息再通过 input 子系统把事件上报给用户空间。4.3 用 evtest 验证第一笔触摸数据驱动编译进内核并启动后第一步不是写 Qt 程序而是先确认内核能不能收到触摸事件。Linux 输入子系统会为触摸设备生成一个/dev/input/eventX节点可以用evtest来查看evtest /dev/input/event2运行后按下屏幕终端里会不断输出ABS_MT_POSITION_X、ABS_MT_POSITION_Y、BTN_TOUCH这样的事件。看到这些字段就说明数据链路已经打通了。有一个常用的排查手段是按代码去寻找 eventX 节点对应的设备名称cat /proc/bus/input/devices里面会列出所有输入设备其中有NameGoodix Capacitive TouchScreen这一行就是触摸设备节点。4.4 多点触控协议 Type B为什么上报格式那么复杂现代电容触摸屏基本都是多点触控上报数据用的是内核里十分重要的 MT Protocol Type B。Type B 协议要求驱动为每个手指分配一个独立的slot手指按下时上报ABS_MT_SLOT、ABS_MT_TRACKING_ID、ABS_MT_POSITION_X、ABS_MT_POSITION_Y等事件。整个数据结构有点像一个矩阵一个 slot 代表一根手指tracking ID 告诉系统“这是同一根手指在连续移动”。如果 tracking ID 变了系统就知道原来的手指抬起了新手指按下了。正因为协议比较严格你在设备树里写的分辨率、坐标反转属性如果不正确就会导致多指识别错乱比如两根手指同时滑动系统却只上报其中一根或者出现“鬼点”。5. 坐标对齐与屏幕方向驱动上线只是开始5.1 为什么坐标会乱分辨率、方向、像素时钟的三重因素驱动能上报触摸事件离“用起来顺畅”还有一段距离。我遇到最多的情况是鼠标能动但手指和光标错位甚至往一个方向滑动光标却反向移动。坐标错乱通常由三个因素叠加而成分辨率不匹配设备树里写的是 1024x600但触摸芯片内部固件配置的分辨率是 800x480内核换算时就可能不对方向不对屏幕物理方向是横屏但驱动或应用层默认是竖屏坐标轴就反了像素时钟与采样时刻导致的数据错位显示和触摸虽然不是同一条数据通道但坐标对齐最终依赖“屏幕物理坐标”如果 LCD 扫描方向与触摸屏的坐标方向不一致就会出现 X 和 Y 调换的问题。5.2 在驱动和应用层之间做好坐标换算大多数情况下坐标对齐是在内核驱动层完成的。设备树里的touchscreen-size-x/y决定坐标的最大边界驱动把芯片原始值等比例映射到这个范围内。如果芯片上报的最大值是 1200屏幕实际分辨率是 1024驱动会把 1200 按比例缩放成 1024避免坐标溢出。如果系统里跑的是 Qt 或 GTK 这类 GUI 框架应用层还会再做一次坐标系变换。Qt 的屏幕方向配置、窗口管理器的旋转设置都可能影响最终触控点的显示位置。所以我调试时有个习惯先用evtest确认内核上报的事件是“干净”的再去看应用层。如果evtest里面坐标已经不对那问题一定在内核或设备树而不是 Qt 程序。5.3 屏幕旋转场景坐标变换怎么做很多 7 寸屏是横屏安装但产品 UI 是竖屏显示这时就要做坐标变换。常见做法有两种。一种是在设备树里加反转属性适合 180 度翻转的场景另一种是在应用层面用环境变量或 Qt 的QScreen::setOrientationUpdateMask做 90 度旋转。前一种改完对所有程序生效但灵活性差后一种灵活但要确保应用和输入事件都使用同一套旋转逻辑。我个人的建议是如果你只是做固定方向的产品原型优先用设备树把 X/Y 方向和 180 度反转都配好如果需要用户横竖屏切换则在应用层统一处理坐标旋转内核驱动只负责把原始坐标报上来。6. 实战修复记录驱动开发中我修复过的几类典型故障6.1 I2C 扫描不到设备地址位的陷阱第一次接上触摸屏我在设备树里写了reg 0x5d但i2cdetect却扫到0x28。这其实不是设备树填错了而是 7 位地址和 8 位地址混用的问题。很多芯片资料把地址写成 8 位形式比如 0x5d但内核 I2C 子系统用的是 7 位地址实际应该填 0x28。遇到扫描不到设备的情况先用i2cdetect -y 0看总线通不通再用示波器或者逻辑分析仪看 SCL/SDA 上有没有波形。如果完全没波形说明 I2C 控制器没工作大概率是设备树里 I2C 节点没使能。6.2 复位时序不对导致芯片状态异常GT911 正常工作的前提是复位顺序正确。如果驱动刚复位完马上就去读寄存器芯片可能还没准备好I2C 通信就会失败。我调试时曾修改过复位延时的数值发现只要高低电平持续时间不够长设备就会间歇性失效。标准做法是复位脚先拉低至少 10ms再拉高并等待 50ms 以上再发起 I2C 通信。这个时序在不同批次芯片上可能略有差异在驱动里可以微调。6.3 中断引脚配置错误CPU 占用率飙高有一次触摸屏看似正常但整个系统 CPU 占用率非常高用top一看一个内核线程占用了几乎 90% 的 CPU。查下来发现中断引脚被配置成了电平触发而 GT911 在无触摸时默认把中断脚拉到了一个不稳定的电平导致中断反复触发。解决方法是把设备树里的中断触发方式改为IRQ_TYPE_EDGE_FALLING并在驱动里保证每次中断处理完能正确清除中断状态。不要小看这个细节它直接影响系统的整体稳定性。6.4 电压域问题导致触摸随机跳点触摸点偶尔会突然跳到屏幕边缘或者报出一些物理上不可能的点这类问题往往不是软件配置能解决的。我遇到过一例最后查到是触摸芯片的供电电压偏低导致内部模拟前端采样不稳定。如果你的板子在 USB 供电时触摸正常外接大负载后开始跳点先测一下触摸芯片电源引脚的纹波。触摸芯片对 AVDD 的稳定性要求比较高常见工作电压是 3.3V 或 2.8V电源波动过大时会出现典型的“飞点”现象。6.5 输入设备节点编号不固定系统重启后/dev/input/eventX的编号可能变化如果应用层直接写死了设备节点触摸就时不时失效。解决办法是用 udev 规则根据设备名称创建稳定的软链接或者在应用层扫描/dev/input目录查找Goodix设备名。更稳妥的方案是同时监听多个事件节点通过Input device name来区分哪个是触摸屏。这一点在做开机自启动的图形界面程序时尤其重要很多产品出厂后触摸失效原因就是节点号漂移。6.6 设备树更新了但启动镜像没跟着更新设备树文件改完后需要重新编译 DTB 并打包成 BOOT.BIN 或者 fit image。常见问题是交叉编译环境里改了设备树源码但 U-Boot 的fdt_file环境变量还指向旧的 DTB于是系统加载的还是老配置。排查方法是启动后查看cat /proc/device-tree/gt9115d/compatible如果输出的是旧值说明启动镜像没有更新如果输出正常就说明设备树生效了。这个命令对任何设备树节点都通用建议收藏。6.7 显示颜色不对触摸方向也乱最后一种情况是显示链路和触摸链路都正常但界面颜色看起来不对同时触摸方向乱。这种往往是 RGB 数据位序不对比如把 RGB888 配置成了 BGR888。屏幕上颜色通道发生交换物理上每一列的排列顺序改变触摸坐标自然也和视觉位置对不上。解决方法是回到 FPGA 侧修改 AXIS 或 VDMA 的数据位宽和像素格式让 Linux 帧缓冲输出顺序和屏幕面板的 RGB 接线一致。简单的验证方法是先让系统显示一张纯红色图片如果屏幕显示成蓝色就说明 R/B 通道反了。最后说一点我个人的习惯每次做完这类 FPGALinux 的外设项目我都习惯把硬件原理图的关键页面截图存在工程目录里因为驱动开发的每一步排查最终都会回到原理图的验证上。设备树不是凭空写的内核驱动也不是碰运气的它们都是在描述同一个物理事实。触摸屏驱动看起来只是让“手指点到哪里光标跟到哪里”但真正做完这个过程你对 Zynq 双核系统里 PS 与 PL 的配合、设备树与驱动的绑定关系、输入子系统的上报机制都会有一个质的认识。后续如果再在这个项目上加 LVDS 转接、多点手势识别甚至用 PL 侧加速触摸数据处理整个思路都会顺畅很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价