资讯详情

FPGA开发板Linux触摸屏驱动实战:GT911设备树配置到应用层全链路调通

📅 2026/9/21 2:30:50 | 华诺云谱 👁 阅读
FPGA开发板Linux触摸屏驱动实战:GT911设备树配置到应用层全链路调通
刚把一块7寸电容触摸屏在FPGA开发板的Linux系统上完整点亮并且把触摸驱动从设备树配置到坐标上报全链路调通整个过程踩了不少坑。这个项目来自「黑金云课堂」的FPGA Linux开发实战系列核心是解决很多同学拿到开发板之后屏幕能显示但触摸没反应、坐标乱跳、应用层收不到事件这类问题。这类工作放在纯ARM Linux板卡上不算难但在FPGA平台上做Linux触摸屏驱动涉及的因素要多出不少硬件上要确认触摸芯片和FPGA逻辑之间的I2C时序、复位和中断引脚分配软件上要同步处理设备树、内核驱动、input子系统以及应用层的校准和对接。这篇文章把我从零到一跑通7寸屏触摸驱动的完整过程记录下来包括方案选型、设备树写法、内核配置、驱动移植、校准测试和常见故障排查给准备在FPGA平台上做Linux人机交互的工程师一个可以直接参考的实操范本。1. 项目整体设计思路与平台选型1.1 为什么要在FPGA上跑Linux做触摸屏驱动很多人对FPGA的印象还停留在逻辑验证和高速接口处理上觉得做界面交互是ARM或者应用处理器的事。但从实际项目角度看FPGA平台上跑Linux做HMI人机界面的需求非常普遍。现在的FPGA芯片普遍集成了硬核处理器比如Xilinx Zynq系列的双核Cortex-A9、Intel Cyclone V SoC的ARM Cortex-A9或者可以例化软核处理器MicroBlaze、NIOS II在FPGA内部既做实时逻辑处理又跑操作系统管理界面和通信协议一套硬件解决控制和交互两个需求。就我用的黑金系列开发板来说它属于典型的FPGAARM异构架构PL端可编程逻辑负责图像采集、接口扩展、自定义外设PS端处理系统跑Linux系统。触摸屏驱动看似只是一个输入设备的驱动但它牵扯到整套Linux输入子系统链路设备树描述硬件连接、内核驱动完成设备注册和事件上报、应用层通过input设备节点读取坐标。跑通这一条链路等于把FPGA平台上的Linux驱动开发基础打了一遍。1.2 硬件平台与7寸屏接口选型这次使用的7寸屏是RGB888接口的TFT液晶屏分辨率为1024x600触摸部分为电容式多点触控。屏幕的显示信号来自FPGA逻辑侧生成的时序这个在我们前期的教程里已经跑通原理是PL端通过Verilog生成PCLK、HSYNC、VSYNC、DE和RGB数据信号送给屏幕驱动板。触摸芯片和主控制器之间走I2C接口。这款7寸屏的触摸芯片有两种常见方案一种是汇顶科技的GT911另一种是敦泰的FT5x06系列。黑金的这块屏用的是GT911这款芯片在国产触摸方案里占有率非常高7寸到10寸的屏幕经常见到它的身影。GT911支持最多5点触控I2C接口内置固件自动校准对外提供INT中断引脚和RST复位引脚。驱动开发的关键就是通过I2C读取触摸坐标寄存器并且在INT引脚触发时通知CPU有触摸事件发生。这里有个选型经验触摸芯片在量产项目里一定要留出引脚复用的余地哪怕当前只用单点触摸PCB上也尽量把INT和RST引脚都引出来到主控GPIO方便软件层做中断唤醒和复位时序控制。我见过不少板子为了省GPIO把RST直接接死在高电平后面调试触摸无反应或者休眠唤醒失败时完全没有操作空间。1.3 触摸驱动方案选型内核自研还是用现成驱动Linux内核里对触摸屏驱动有一套完整的input子系统框架驱动只需要做三件事初始化I2C通信、注册input设备、在中断或轮询时上报坐标。GT911的驱动在Linux内核里其实已经有了不同版本位置和实现略有差异较老的内核里是drivers/input/touchscreen/gt9xx.c这个是早期的GT9xx系列驱动。较新的内核4.x以上通过drivers/input/touchscreen/goodix.c支持这个文件同时兼容GT911等型号。部分BSP厂商会提供独立patch把驱动挂在drivers/input/touchscreen/下命名为gt911.c或者gt9xx.c。在方案选型上我采用的是内核自带的goodix.c驱动。理由很简单代码随内核发布长期维护稳定与input子系统的集成度最高不需要自己维护一串上报逻辑。唯一需要做的小改动是确认芯片的复位和中断引脚配置是否匹配驱动代码的GPIO操作方式。也有人问过用用户态驱动行不行做法是绕开内核input子系统直接应用层读取I2C设备节点解析触摸芯片寄存器然后通过/dev/uinput注入事件。这个方案的优点是不需要重新编译内核拿到一个加载了i2c-dev模块的系统就能跑缺点也很明显事件延迟大、占用CPU高、无法深度睡眠。如果是原型验证或者临时测试用户态驱动可以接受但做正式产品不推荐。在FPGA平台上PS端的ARM核往往还要承担通信和图形界面的工作能省一点CPU是一点。2. 硬件原理与设备树配置2.1 确认触摸芯片型号与I2C地址查原理图和屏幕模组规格书确认过程这里要仔细做。GT911这颗芯片比较特殊它的I2C从机地址不是固定的根据INT引脚在上电时的电平状态决定INT引脚上电时为低电平I2C地址是 0xBA/0x288位地址/7位地址INT引脚上电时为高电平I2C地址是 0x5D/0x2E8位地址/7位地址这里说的0x5D就是7位地址设备树里reg属性填的是7位地址所以填0x5d。有的驱动里写0xBA那是8位地址表示法用的时候要区分清楚。我在第一次调试时没注意这个细节设备树里填了reg 0xBA导致I2C子系统在探测设备时地址对不上驱动probe函数根本不会执行。确认方法很简单上电后用i2cdetect -y bus号扫描I2C总线看到地址为0x5d或0x28的应答设备就说明I2C通信正常并且知道了芯片实际工作的地址。如果扫描不到先用示波器或者逻辑分析仪抓I2C的SDA和SCL确认上拉电阻和时钟频率是否正常再看地址是不是被芯片配置成了另一个档位。GT911的INT和RST引脚在硬件上会连接到PS端的GPIO这两个引脚在设备树里的描述方式直接决定驱动能不能正常工作。2.2 设备树触摸屏节点怎么写设备树是把硬件信息传递给Linux内核的最关键一步。我用的内核版本是4.14设备树里I2C节点的写法如下面所示这是在黑金开发板设备树源文件基础上修改的结果i2c1 { status okay; clock-frequency 100000; pinctrl-names default; pinctrl-0 pinctrl_i2c1_default; gt911: gt9115d { compatible goodix,gt911; reg 0x5d; interrupt-parent gpio; interrupts 7 IRQ_TYPE_EDGE_FALLING; irq-gpios gpio 7 GPIO_ACTIVE_HIGH; reset-gpios gpio 6 GPIO_ACTIVE_LOW; touchscreen-size-x 1024; touchscreen-size-y 600; touchscreen-max-id 5; }; };这里有几个关键点compatible字符串必须和驱动里of_device_id匹配表里的字符串一致。在goodix.c驱动的匹配表里包含了goodix,gt911这一项所以直接用。如果你用的驱动是厂商改过的版本记得确认匹配字符串。interrupts属性配置的是中断号和触发方式。GT911的INT引脚是开漏输出平时需要外部上拉有触摸时引脚拉低所以我们通常配IRQ_TYPE_EDGE_FALLING或者IRQ_TYPE_LEVEL_LOW。有的板级设计上拉了反相那就需要看实际波形再调整这个没有标准答案以实测为准。reset-gpios和irq-gpios是goodix.c驱动在probe过程中实际操作的两个引脚。GTP911要求先拉低RST复位再拉高释放复位并且在这个过程中INT引脚的电平状态决定了芯片的I2C地址。设备树里对这两个GPIO的GPIO_ACTIVE_LOW和GPIO_ACTIVE_HIGH标志不能写反否则驱动操作出来的电平完全相反。2.3 最常见的天坑复位与中断时序GT911的上电复位时序是这项目里踩过最大的坑值得单独拿出来说。驱动在goodix.c里对复位时序有严格要求拉低reset引脚保持至少10ms实际驱动里通常延时20ms以上。在reset释放之前INT引脚需要被配置为输入并且根据你想要的I2C地址设置相应电平。释放reset等待至少50ms让芯片内部固件启动完成。芯片稳定后通过I2C读取0x8040寄存器确认通信正常然后配置工作寄存器。如果复位时序不对最典型的现象是i2cdetect能扫到地址但是驱动在读取0x8040产品ID时返回错误或者驱动直接报I2C read failed。还有一个隐蔽问题如果设备树里reset-gpios没有配置驱动会认为芯片不需要复位直接用默认配置去访问而这个配置往往和实际硬件不匹配结果就是驱动认为注册成功了但上报的坐标全是0或者乱跳。我的排查方法是先用简单的GPIO操作脚本手动模拟复位时序然后在应用层直接读写I2C寄存器确认芯片能正常通信之后再回到驱动里使能它的复位流程。这样把问题拆开哪一步出问题一目了然。3. 内核配置与驱动编译移植3.1 内核配置项怎么开在编译内核之前需要确认触摸屏驱动对应的配置项是否开启。用make menuconfig打开内核配置界面路径在Device Drivers --- Input device support --- * Touchscreens --- * Goodix GT9xx based touchscreen配置项名称是CONFIG_TOUCHSCREEN_GOODIX。对应goodix.c驱动。如果这里选成M模块那编译后会生成独立的.ko文件需要手动加载如果选成*内建会编进内核镜像里内核启动时自动匹配设备树节点并加载驱动。我推荐在开发调试阶段选成M这样驱动可以单独修改单独编译不需要每次改一点代码就烧整个内核镜像。调稳定之后再改成*内建减少启动时的模块加载依赖。另外还要确保I2C驱动本身开启。FPGA平台上PS端的I2C控制器驱动通常在Device Drivers --- * I2C support --- * I2C device interface * Synopsys DesignWare I2C adapterI2C device interface是/dev/i2c-x设备节点支持调试阶段必须开方便用i2cdetect和i2cget直接测试硬件。3.2 交叉编译驱动模块的完整流程在FPGA平台上做Linux开发交叉编译是基本功。黑金的BSP已经提供了完整的交叉编译工具链通常在/opt/或者BSP目录下根据架构选择对应前缀。Zynq平台用的是arm-linux-gnueabihf-前缀export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf- export PATH$PATH:/opt/arm-linux-gnueabihf/bin内核源码解压后进入顶层目录先加载默认配置或者自己保存的配置文件make xilinx_zynq_defconfig make menuconfig修改完配置后开始编译make zImage -j8 make modules -j8 make dtbs编译过程中如果报头文件缺失多半是交叉编译工具链没设对或者内核源码目录本身不完整。这里有个小技巧编译完模块后到drivers/input/touchscreen/目录下检查一下有没有生成goodix.ko如果生成了说明驱动编译成功。如果你改的是goodix.c源码单独编译单个模块可以用make Mdrivers/input/touchscreen编译出来goodix.ko之后用scp传到开发板insmod加载。不过要注意如果你在menuconfig里把这个驱动设成了M设备树节点匹配后驱动不会自动加载需要先把.ko文件放到开发板的/lib/modules/$(uname -r)/目录下然后执行depmod -a再用modprobe goodix加载或者重启让它自动加载。3.3 设备节点与事件验证驱动加载成功之后可以在开发板上确认设备节点是否创建# 查看I2C总线上是否挂载了gt911设备 ls /sys/bus/i2c/devices/ # 查看input设备 cat /proc/bus/input/devices正常情况会看到一个名为Goodix Capacitive TouchScreen或者类似名字的输入设备对应一个/dev/input/eventX节点。如果你看到的设备名不对比如是HID 0416:0180或unknown那说明驱动没有正确识别芯片可能进错了HID驱动或者其他驱动。/proc/bus/input/devices里能看到输入设备的详细信息I: Bus0018 Vendor0000 Product0000 Version0000 N: NameGoodix Capacitive TouchScreen P: Physi2c-1-005d/input0 S: Sysfs/devices/soc/.../1e000400.i2c/i2c-1/1-005d/input/input0 U: Uniq H: Handlersevent0 B: PROP2 B: EVb B: KEY400 0 0 0 0 0 0 0 0 0 0 B: ABS2608000 3Phys里的i2c-1-005d就是I2C总线1上的地址0x5d设备和我们的预期完全对得上。出现这个之后接着用evtest测试触摸事件这一步通过就说明驱动链路已经从硬件到内核完全打通了。4. 触摸校准、测试与上层对接4.1 用evtest验证坐标上报在开发板上安装evtest工具然后指定触摸输入设备节点evtest /dev/input/event0触摸屏幕终端会实时打印事件信息Event: time 1234567890.123456, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 512 Event: time 1234567890.123456, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 300 Event: time 1234567890.123456, type 3 (EV_ABS), code 48 (ABS_MT_SLOT), value 0 Event: time 1234567890.123456, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1 Event: time 1234567890.123456, type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 1 Event: time 1234567890.123456, type 0 (EV_SYN), code 0 (SYN_REPORT), value 0看到坐标值在变化并且是合理的触摸位置说明驱动链路已经通了。如果坐标和手指位置不对应比如横向滑动变成纵向变化那是设备树里touchscreen-size-x和touchscreen-size-y与实际屏幕分辨率不一致导致的坐标映射问题。我再强调一下这里看到的坐标值是设备树里配置的touchscreen-size-x/y范围内的原始值比如1024x600。应用层拿到之后可以通过矩阵变换做坐标映射也可以直接使用。4.2 tslib校准与环境变量配置裸机的坐标值和应用层的逻辑坐标之间往往存在偏差尤其是屏幕边缘位置这个时候需要用tslib做校准。tslib是Linux触摸屏校准和滤波的常用库编译安装后需要配置环境变量export TSLIB_TSDEVICE/dev/input/event0 export TSLIB_CALIBFILE/etc/pointercal export TSLIB_CONFFILE/etc/ts.conf export TSLIB_PLUGINDIR/usr/lib/ts export TSLIB_CONSOLEDEVICEnone export TSLIB_FBDEVICE/dev/fb0首次使用执行五步校准ts_calibrate校准程序会依次在屏幕5个位置显示十字光标用触摸笔依次点击。校准完成后会生成/etc/pointercal文件里面保存了一组7个参数的坐标变换矩阵。这个文件在之后每次启动时会被tslib读取。校准过程中如果出现光标位置和触摸点位置差异很大最常见的原因是设备树里的touchscreen-size-x/y和实际屏分辨率不匹配。要确保这两个值对应的是触摸屏实际的坐标范围而不是显示分辨率。比如1024x600的屏触摸范围通常就是1024x600左右有些屏模组的触摸范围比显示区域略大具体以规格书为准。4.3 Qt或精简GUI对接触摸事件在FPGA平台上跑图形界面最常用的方案是Qt通过tslib提供的库对接触摸输入。在Qt中设置环境变量export QT_QPA_PLATFORMlinuxfb export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS/dev/input/event0 export QT_QPA_FB_DRM/dev/fb0如果Qt版本较老用QWS方式export QWS_DISPLAYLinuxFb:/dev/fb0 export QWS_MOUSE_PROTOTslib:/dev/input/event0 export QWS_KEYBOARDoffQt对接触摸事件后应用里直接使用标准的QTouchEvent和鼠标事件处理用户输入。需要注意的是Qt的linuxfb后端和tslib对接时触摸事件被模拟成鼠标事件单击和拖动都能正常工作但多点触摸手势比如双指缩放需要开启evdevtouch插件配置不复杂但不同版本差异较大建议以Qt版本对应的文档为准。如果你不想用Qt也可以在应用里直接读/dev/input/eventX节点解析input_event结构体里的坐标值然后做自己的界面刷新逻辑。这种方式灵活度高适合轻量级HMI但要做好事件缓冲和非阻塞读取处理否则容易丢事件或者卡界面。5. 常见问题排查与避坑实录5.1 触摸无反应的全链路排查顺序触摸无反应是这个项目里遇到最多的问题我总结了一个排查顺序按这个顺序走能节省大量时间排查步骤操作命令/方法预期结果1. 检查I2C设备i2cdetect -y 1扫描到地址 0x5d 或 0x282. 测试寄存器通信i2cget -y 1 0x5d 0x8140返回芯片ID值 0x9113. 加载驱动模块modprobe goodix无报错dmesg有驱动初始化日志4. 查看input设备cat /proc/bus/input/devices出现Goodix对应设备项5. 测试事件evtest /dev/input/event0触摸时打印坐标事件6. 校准ts_calibrate校准完成触摸与光标对应第2步i2cget读0x8140寄存器能返回芯片ID 0x911这步是判断I2C通信是否正常的硬指标。如果读不到说明芯片根本没有正常工作或者地址不对。有一个坑是i2cget默认调用的I2C读函数是SMBus读部分芯片对这种读方式支持不好这时候要用i2cget -y 1 0x5d 0x8140 w强制以word方式读取或者用i2ctransfer指定具体的读写时序。5.2 坐标乱跳与中断风暴触摸坐标乱跳的故障现象是手没有碰屏幕但事件流一直在刷或者点击屏幕时坐标在多个位置之间闪跳。我用逻辑分析仪抓过几次发现有两个主要原因第一触摸芯片供电纹波大导致内部ADC采样不稳。屏幕上电瞬间背光电路和触摸芯片共用电源如果电源滤波不干净触摸坐标就会出现周期性漂移。这个在硬件上解决触摸芯片电源脚附近加一个10uF100nF的电容组合并且尽量远离背光驱动电路。第二中断引脚配置有问题。interrupts里如果用IRQ_TYPE_LEVEL_LOW而不是IRQ_TYPE_EDGE_FALLING在某些驱动处理逻辑下中断处理函数返回后如果IRQ引脚没有立即恢复高电平会触发无限中断重入导致系统卡死或者事件风暴。这个把触发模式改掉就能解决。更隐蔽的是中断引脚没使能内部上拉引脚悬空时电平漂移也会产生随机中断。5.3 驱动probe失败且找不到匹配设备dmesg里看到goodix驱动报failed to find matching device或者类似信息排查点集中在设备树检查设备树节点是否挂在正确的I2C总线上。I2C控制器在设备树里的节点路径是i2c1但有的BSP用的别名是i2c0或者i2c2这个要看实际原理图连接。黑金板子里触摸芯片挂在I2C1上对应Linux里的/dev/i2c-1如果你扫描i2cdetect -y 1没反应而i2cdetect -y 0有反应那就是挂错总线了。还需要检查设备树编译是否生效。在U-Boot里用fdt print /soc/i2c...查看实际加载的设备树内容或者启动后去/proc/device-tree下确认节点是否存在ls /proc/device-tree/soc/i2ce0004000/gt9115d/如果设备树文件修改了但没生效通常是编译生成的dtb文件没有更新到启动分区或者U-Boot使用了内置的dtb而不是外部加载的那份。5.4 触摸屏休眠唤醒后失效这个问题在带屏产品里很常见系统进入休眠后触摸不亮evtest也没有事件但dmesg没报错。排查思路是确认触摸芯片有没有跟随系统进入休眠以及唤醒后驱动有没有重新初始化芯片。GT911支持低功耗模式系统进入suspend时驱动应该调用goodix_suspend把芯片切到低功耗唤醒后调用goodix_resume重新初始化。如果你用的内核里goodix.c没有注册suspend/resume回调或者设备树里没有配置wakeup-source属性唤醒后芯片可能处于半睡半醒状态。一个快速修复方案是写一个简单的唤醒脚本在系统从低功耗恢复后重新绑定I2C设备驱动echo 1-005d /sys/bus/i2c/drivers/goodix/unbind echo 1-005d /sys/bus/i2c/drivers/goodix/bind1-005d是I2C总线号-从机地址的完整标识。这个办法治标不治本但能在项目联调阶段快速恢复触摸功能长期方案还是要补全驱动里的电源管理回调。5.5 坐标方向翻转与旋转7寸屏的安装方向多种多样有时候屏幕竖着放有时候横着放触摸坐标和显示坐标之间就差一个旋转关系。Linux内核提供了统一的坐标变换方式。在设备树里可以通过touchscreen-inverted-x、touchscreen-inverted-y、touchscreen-swapped-x-y这些属性处理简单的翻转和交换。比如屏幕逆时针旋转90度安装触摸X轴对应显示Y轴需要在设备树里加touchscreen-swapped-x-y;如果这些标志位不够用比如安装角度是任意角度就需要在驱动里注册input设备时设置input_set_abs_params后用input_mt_report_slot_state上报坐标变换放在goodix_process_events上报之前或者用tslib的矩阵配置做旋转。从实际经验来看最简单可靠的方式还是硬件上把屏幕装一个固定方向再用设备树标志位微调尽量避免在驱动里做复杂旋转变换。驱动里坐标变换做多了不仅影响触摸响应速度还会给后续升级维护增加隐患。项目收尾后的几点体会这轮在FPGA平台上调试Linux触摸屏驱动整个过程走下来有几点体会想单独说一下。第一FPGA平台上做Linux驱动难点往往不在驱动代码本身而在硬件链路和技术栈的交叉。FPGA逻辑、PS端配置、设备树、内核源码、应用层工具每一层都要能看懂出问题的时候才能快速定位。很多人卡在触摸屏没反应这一步就是因为只在某一层里反复试没有全链路排查的思路。第二不要迷信开发板自带的出厂Linux镜像。黑金官方镜像里有时候会预装一些厂商改过的模块这些模块和设备树加载顺序可能和你要做的改动冲突。做驱动开发时尽量在干净的内核源码上自己编译、自己裁剪这样后期好维护也方便排查问题。第三GT911这款芯片的资料比较分散官方数据手册和驱动代码里的寄存器定义偶尔对不上。遇到这种问题别死磕手册直接看驱动源码里的宏定义和寄存器初始化流程以实际运行为准。目前这个项目已经跑通了单点触摸、多点触摸、Qt界面对接和休眠唤醒之后我准备在FPGA逻辑侧加入LVDS和MIPI等不同接口的屏幕适配继续完善这套Linux图形交互方案。接下来会整理FPGA侧屏幕时序配置和触摸屏联合调试的细节有更新再分享。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。