尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FPGA+Linux七寸GT911触摸屏驱动开发实战

FPGA+Linux七寸GT911触摸屏驱动开发实战 说实话拿到黑金开发板和那块7寸触摸屏的时候我一开始真没当回事。裸机例程里触摸点得飞起屏幕划得顺顺当当。但同样的板子一旦把Linux灌进去屏幕点亮了触摸却像失联了一样/dev/input/eventX一个影子都见不着。这种“FPGA能亮屏、Linux认不出触摸”的尴尬是FPGALinux组合开发里特别典型的一关。问题的本质在于FPGA负责了屏的时序和总线对接而触摸屏驱动的灵魂——输入子系统、设备树、I2C驱动框架全都在Linux那一侧。这个项目正好压在硬件和软件的边界线上。如果你正在做Zynq或MicroBlazeLinux的显示类项目或者手里有带7寸屏的开发板想把它彻底跑起来这篇应该能帮你省下好几个通宵。我会按照真正的开发顺序从方案架构、硬件拆解、驱动开发讲到联调和排错最后把踩过的坑一并交代——全程都是实际调板子的视角不整虚的。1. 方案选型与整体架构FPGA和Linux到底各管哪一段1.1 先搞明白FPGA在触摸屏项目里的定位很多人一开始会陷入一个误区一提到“触摸屏驱动”就以为全是在Linux里写代码的事。实际做下来你会发现FPGA在项目里的角色非常明确——它是屏幕和CPU之间的“翻译官”。7寸RGB屏的接口是并行的像素数据、行场同步信号和像素时钟这种时序对CPU来说太“硬”了必须由FPGA里的视频时序控制器VTC或者外部的DisplayPort转RGB方案来产生。而在Zynq这种SoC上PL端的逻辑天然就是干这个的。我在黑金开发板上做的方案是用FPGA生成1024x60060Hz的RGB时序同时把AXI总线上挂的VDMA数据流送到LCD控制器。这个环节解决的是“屏幕亮起来”的问题。但触摸芯片的数据是走I2C总线的它不关心像素时钟是多少也不关心你是RGB888还是LVDS它只知道有手指按下来的时候要通过I2C把坐标寄存器更新。所以触摸部分在架构上属于“低速控制类外设”交给Linux侧驱动是最合理的。还要注意一点FPGA逻辑里如果例化了AXI IIC控制器那你需要确认Linux设备树里对应的i2c节点是否使能。很多工程把I2C控制器做出来了但设备树节点没配导致系统里根本看不到这条总线触摸自然没戏。这是硬件和软件之间最常见的断点。1.2 为什么触摸部分必须交到Linux侧去处理有人可能问既然FPGA这么强大为什么不用FPGA直接读触摸芯片再把坐标通过寄存器或者串口丢给Linux从技术上说这样确实可行很多老式项目也是这么干的。但这样做的代价是你失去了Linux输入子系统的整套红利。Linux的input子系统把触摸屏、键盘、鼠标等等统一抽象成事件设备应用层通过/dev/input/eventX读取标准事件结构体Qt、GTK、X11都能直接消费这些事件。如果你自己搞一套私有的寄存器接口那上层应用就得为你一个人写适配层以后每换一个触摸屏型号你都要改一遍协议。而使用内核现成的触摸驱动框架驱动只需按规范上报坐标事件其余交给统一API处理。拿我这次的7寸电容屏来说触摸芯片是GT911内核源码里就有现成的goodix驱动有i2c_driver的匹配逻辑有中断线程读取坐标有设备树属性的标准化解析。我只需要做三件事选对内核配置、写对设备树节点、确认中断和复位引脚。剩下的坐标上报、多点触控协议、电源管理驱动全部接管。这就是“把驱动交给Linux侧”的最大价值——你不需要重新发明轮子只需要把轮子对准车轴。1.3 硬核Zynq与软核MicroBlaze的差异黑金开发板有两种形态一种是Zynq-7020这类带硬核ARM的SoC另一种是Artix-7这种纯FPGA芯片要跑Linux就得在PL里搭MicroBlaze软核。两种方案在触摸屏驱动层面的原理几乎一致但设备树节点差异明显。Zynq硬核方案的I2C控制器在PS端设备树里使用的是i2c0这种节点控制器驱动由内核里的Cadence I2C驱动提供。而MicroBlaze方案没有PS端外设硬件工程师得在FPGA里例化Xilinx的AXI IIC或AXI GPIO外设设备树中对应的是axi_iic_0节点compatible属性通常带xlnx厂商前缀。GPIO中断也不一样Zynq用PS GPIO中断号是MIO编号MicroBlaze用AXI GPIO中断统一挂在中断控制器intc下面。实操中我建议如果是Zynq触摸芯片的I2C尽量接在PS端MIO上别占用PL资源。如果芯片引脚不够必须接PL端那I2C控制器的时钟频率和中断号要特别留意AXI IIC的时钟最好配置成100kHz以下否则长线上通信不稳。如果是MicroBlaze先把AXI IIC和AXI GPIO引脚分配好在XSA中确认中断连接再进入Linux的设备树阶段。否则后面驱动一直报错你都不知道是FPGA逻辑的问题还是设备树的问题。2. 七寸触摸屏硬件拆解从RGB时序到I2C地址切换2.1 7寸屏的关键参数与RGB时序7寸屏是个很大的品类市面上常见的是800x480和1024x600两种分辨率RGB888接口。黑金配套的这块屏是1024x60060Hz数据手册上标了一堆时序参数像素时钟约51.2MHz水平总周期1344像素左右包含水平前沿、同步、后沿垂直总周期635行左右。这些参数在裸机例程里通常写死在FPGA逻辑中Linux侧反而不怎么关心因为Linux只管把帧缓冲数据交给VDMA具体什么时候出像素、什么时候拉行场信号全由FPGA的VTC模块决定。如果你在Linux层看到屏幕偏移、闪烁或者出现竖条纹问题多半出在FPGA里的时序参数没匹配上屏的手册而不是内核配置有问题。这时候要回到Vivado工程去检查v_tc的时序寄存器或者看有没有动态配置的驱动接口。总之Linux侧搞定的是“画什么内容”FPGA侧搞定的是“用什么节奏把内容送出去”。2.2 电容触摸控制器GT911的地址与复位机制7寸电容屏的触摸控制器常见的有GT911、GT928、FT5x06等我手上这块用的GT911。这类芯片挂在I2C总线上两条线加上电源、复位和中断信号不算复杂但有几个细节会让新手抓狂。首先是I2C地址。GT911的7位地址可能是0x5D也可能是0x14取决于上电复位过程中INT引脚的电平状态。对应到8位地址就是0xBA和0x28。很多人在i2cdetect扫描时看到0x5D或者0x14就以为是两片芯片其实只是地址选择不同。如果你设备的reg属性写的是0x5D但板子上硬件把INT拉到了高电平导致实际地址变成0x14驱动就匹配不上probe直接失败。其次是复位时序。GT911上电后需要拉低复位脚、再拉高期间还要配合INT引脚的电平状态来确定地址。内核goodix驱动在probe时会根据设备树里的reset-gpios和irq-gpios自动执行这个流程。如果驱动没有成功复位芯片日志里会卡在固件信息读取失败触摸完全没反应。最后是中断。触摸芯片的INT脚是开漏输出一般需要外部上拉。驱动把它注册为下降沿触发的中断有触摸事件时INT拉低唤醒中断线程读取坐标。如果你发现设备能识别但完全没有触摸事件首先要查的就是中断号、中断控制器和触发极性这三样只要有一个不对事件就进不了内核。2.3 从触摸点到应用层事件完整信号链整个触摸链路可以用一句话描述手指按下 - 触摸IC采样 - I2C总线传输 - Linux i2c-core找到对应client驱动 - 驱动注册input设备 - 中断触发 - 读取坐标寄存器 - 上报EV_ABS事件 - 应用层通过/dev/input/eventX读取。我调试时习惯先在脑子里过一遍这条链然后逐段验证。最常用的验证点有三个I2C层通过i2cdetect确认芯片地址、驱动层通过dmesg查看probe日志、输入层通过evtest查看事件。任何一个验证点有异常都能快速缩小问题范围。这条思路在嵌入式调试中特别实用比漫无目的地改代码高效得多。另外提一句有些7寸屏标注为“红外触摸屏”用的可能是touchkit方案。这种一般不是I2C触摸芯片而是通过USB HID或者串口上报坐标。如果是USB接口Linux下基本免驱系统会把它识别成鼠标设备打开/dev/input/mouseX就能看到移动事件如果是串口需要选配专门的touchkit驱动或者自己写行协议解析。所以动手之前务必看一眼屏背面的型号和接口定义别照着电容屏的I2C方案去调试红外屏方向错了怎么调都没戏。3. Linux侧驱动开发实操内核配置、设备树、驱动源码解读3.1 内核配置选对触摸驱动模块内核的输入子系统包含大量触摸屏驱动GT911对应的模块在较新内核里是goodix老厂商BSP里可能叫gt9xx。打开内核配置菜单后路径是Device Drivers - Input device support - Touchscreens - Goodix touchscreen controller不同版本文字略有差异。建议先编成模块方便调试时反复加载等稳定了再编进内核。配置完成后一条非常快的验证命令是查内核配置是否生效zcat /proc/config.gz | grep TOUCHSCREEN如果输出里有CONFIG_TOUCHSCREEN_GOODIXm或者y说明模块已经就位。如果是m还需要确认模块文件在文件系统里存在并且/lib/modules/$(uname -r)/下的依赖关系已经更新用depmod -a重新生成modules.dep再modprobe goodix加载。如果黑金的BSP里已经内置了触摸驱动建议先别自己重新拉内核版本直接在BSP源码上改省去交叉编译环境匹配的很多麻烦。自己编模块最常遇到的问题就是内核头文件版本不匹配insmod后报unknown symbol或者invalid module format。解决思路很简单模块源码和编译环境必须对应当前运行内核用uname -r检查运行内核版本再确认源码树版本一致。3.2 设备树编写详解每个属性都要写对设备树是Linux下描述硬件资源的标准方式。触摸屏节点通常挂I2C控制器下我给出一个Zynq平台的参考写法i2c0 { status okay; clock-frequency 100000; gt9115d { compatible goodix,gt911; reg 0x5d; interrupt-parent gpio0; interrupts 54 IRQ_TYPE_EDGE_FALLING; irq-gpios gpio0 54 GPIO_ACTIVE_HIGH; reset-gpios gpio0 55 GPIO_ACTIVE_HIGH; touchscreen-max-x 1024; touchscreen-max-y 600; touchscreen-inverted-x; touchscreen-inverted-y; }; };逐个说下关键属性。compatible是驱动匹配的关键字符串必须与内核源码中i2c_device_id或of_match_table里写的完全一致不能多空格、不能改大小写。reg是I2C地址注意这里是7位地址很多数据手册写的是8位地址换算时把8位地址右移一位比如0xBA变成0x5D。interrupt-parent和interrupts是中断资源Zynq里PS GPIO作为中断控制器时中断号对应MIO/EMIO引脚编号需要查具体BSP中GPIO控制器的编号规则别想当然写死。irq-gpios和reset-gpios是驱动用来控制复位和获取中断引脚的属性这两个的属性值是有命名规范的驱动里用devm_gpiod_get_optional函数根据属性后缀去拿GPIO。GPIO的第三个参数表示有效电平ACTIVE_HIGH还是ACTIVE_LOW要以原理图为准写反之后复位时序会完全乱掉。touchscreen-max-x和touchscreen-max-y是坐标范围驱动会把这两个值写到触摸芯片的寄存器务必与屏幕分辨率一致。最后这两个inverted属性是坐标翻转开关屏幕装反或者走线交叉时可以在这里修正不用改驱动代码。如果你是MicroBlaze方案设备树节点通常长这样axi_iic_0 { clock-frequency 100000; gt9115d { compatible goodix,gt911; reg 0x5d; interrupt-parent intc; interrupts 7 IRQ_TYPE_EDGE_FALLING; irq-gpios axi_gpio_0 0 GPIO_ACTIVE_HIGH; reset-gpios axi_gpio_0 1 GPIO_ACTIVE_HIGH; touchscreen-max-x 1024; touchscreen-max-y 600; }; };这个例子里interrupt-parent换成了软核的中断控制器intc中断号取决于Vivado工程里AXI IIC和AXI GPIO在中断控制器上的连接顺序。很多人在这里卡住是因为中断号根本不是从0开始排的正确做法是在Xilinx Vitis里查看system.xsa的中断连接表格或者看生成的设备树头文件。别凭空猜猜错了只会浪费时间。3.3 驱动源码导读goodix驱动probe到上报事件的路径打开内核源码drivers/input/touchscreen/goodix.c你会看到驱动的生命周期非常清晰。首先probe函数从设备树读取config数据然后调用goodix_reset进行复位复位期间会按照前面说的INT电平确定芯片I2C地址并发送读取命令。接着驱动读取产品ID寄存器通过dev_info打印类似“Goodix-TS”的日志这一步是判断驱动和触摸芯片是否通信成功的第一现场。通信验证通过后驱动会申请中断使用request_threaded_irq注册一个中断线程。GT911在有效触摸事件来临时会把INT脚拉低触发下降沿中断中断线程里读取坐标buffer解析出触点编号、X坐标、Y坐标然后通过input_report_abs和input_mt_sync上报多点触控协议事件最后input_sync告诉上层本轮数据结束。内核驱动成熟度很高一般情况下不需要你改驱动代码。真正需要你熟悉这些流程的原因是日志和错误现象不会直接告诉你“设备树reg写错了”而是表现为probe失败或者中断不触发。你看得懂代码才能顺着日志定位到具体是哪一步断了。3.4 编译部署从源码到板载的完整流程驱动编译通常有两种方式。一种是随内核编译把设备树和驱动的修改放进BSP工程用petalinux-build或者make dtbs生成镜像重新烧写启动。另一种是单独编译驱动模块方便迭代。单独编译模块的命令大概是make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- modules生成的.ko文件拷贝到板子的/lib/modules/$(uname -r)/目录后执行depmod -a modprobe goodix模块加载成功后拔插一次或者重新复位触摸屏再确认dmesg里是否有驱动初始化成功的日志。设备树编译类似在kernel源码目录执行make dtbs生成的dtb上传到boot分区覆盖旧文件重启生效。我习惯把驱动先编成模块调试设备树用“可加载的dtbo”方式验证如果系统支持configfs overlay这样改设备树不用反复重启能省不少时间。等确认所有属性正确再编回内核里做一次干净启动防止误依赖动态加载顺序。4. 上电联调与坐标校准一条命令一条命令排查4.1 确认硬件和I2C总线i2cdetect是第一条防线驱动加载之前先用i2cdetect确认触摸芯片出现在正确的总线上。Zynq的PS I2C控制器通常对应i2c-0或者i2c-1板子上可能有多个I2C通道依次扫描i2cdetect -y -r 0 i2cdetect -y -r 1如果扫描结果里出现0x5D或0x14说明I2C链路是通的。如果什么都没扫到原因大概率出在四方面第一复位引脚一直拉低芯片没有正常上电第二I2C地址本身就是0x14你扫描时总线参数不对第三I2C上拉电阻没焊或者断了第四触摸芯片的电源没起来。这些从软件上都救不回来只能回头查硬件。扫到地址后再看dmesgdmesg | grep -i goodix正常的日志会打印产品ID和固件版本例如goodix_read_version完成后会输出类似“GT911”的标识。如果日志里没有任何Goodix相关信息说明驱动根本没有执行probe优先检查设备树节点是否被正确解析i2c子系统的地址匹配有没有生效。最后用一条命令看看输入设备记录cat /proc/bus/input/devices | grep -A 5 -i goodix看到这个输出说明输入设备已经注册到了内核下一步就可以直接观察事件了。4.2 evtest实测从事件流看懂触摸行为evtest是Linux下查看输入事件的标配工具。先通过ls -l /dev/input/event*找到触摸设备对应的event节点不确定的话用evtest无参数运行它会列出所有输入设备你根据名称里包含“Goodix”或者“Goodix-TS”选中。evtest /dev/input/event2运行后用手指在屏幕上滑动正常情况下会不断打印EV_ABS事件value显示X和Y坐标。按压力度大的话还会有EV_KEY中的BTN_TOUCH事件。如果这里能看到坐标数值变化说明从触摸芯片到内核事件队列整条链路都已经贯通剩下的问题就只有坐标校准和方向修正。如果你看到事件偶尔有、偶尔没有先别急着怀疑驱动大概率是触摸芯片的供电或者I2C线受到干扰。用示波器看I2C的SCL/SDA波形确认时钟频率和信号质量。如果波形边沿很缓、脉冲宽度抖动检查上拉电阻的阻值是不是太大或者I2C总线走线过长。4.3 坐标校准从命令到GUI的环境配置触摸屏事件坐标是硬件原始值最终显示到屏幕还要经过坐标变换。Linux下有几种校准方式我按场景分类说明。在Qt应用场景下如果你用eglfs或linuxfb作为显示后端Qt的evdevtouch插件直接消费输入事件的绝对坐标设备树里touchscreen-max-x/max-y设置正确坐标就能对得上基本不需要额外校准。如果你用的是X11桌面环境需要安装xinput_calibrator工具生成校准矩阵写到Xorg配置里。校准命令是xinput_calibrator老派的tslib方式主要用于电阻屏电容屏一般不建议使用。如果项目沿用tslib流程是交叉编译tslib后export TSLIB_TSDEVICE/dev/input/eventX再运行ts_calibrate生成pointercal文件。不管用哪种校准方式我强烈建议优先通过设备树属性修正坐标方向而不是依赖用户空间校准。因为用户空间校准矩阵在换内核、换屏幕时很容易失效而设备树里的touchscreen-inverted-x、touchscreen-inverted-y、touchscreen-swapped-x-y是硬件板级描述跟着设备树走不会丢。4.4 坐标范围异常的处理max值对不上的坑有次我调试发现触摸事件的值域在0到4095之间乱跑但屏幕分辨率是1024x600坐标明显不对。问题出在GT911芯片内部的Max X/Y寄存器没有初始化驱动虽然上报范围是设备树里的1024x600但芯片内部坐标计算还是按默认范围来做导致触摸位置和视觉位置对不上。解决方法是确认设备树touchscreen-max-x和touchscreen-max-y跟屏幕一致然后重新加载驱动让驱动把这两个值写入芯片。如果驱动版本比较老不一定会主动回写那就得在驱动probe里手动调一次寄存器写入或者用i2cset命令在系统起来后动态设置等确认可靠再固化到驱动。这种坐标范围错位的问题现象上很隐蔽——触摸不是完全没反应而是每点一个位置指针就跑到别的地方。遇到这类问题第一时间打印原始坐标值用dmesg或者临时加上printk看驱动上报的底层数据别急着在校准工具里硬调。5. 高频故障与调试心得我踩过的坑你最好绕开5.1 触摸完全没反应按链路逐段排雷触摸没反应是最磨人的故障因为你能看到的只有一块安静的屏幕。我的排查顺序固定为先看/proc/bus/input/devices里有没有触摸设备再看dmesg里有没有驱动初始化日志再用i2cdetect扫描地址接着看/proc/interrupts里中断有没有触发。哪一步断了就重点查哪一步。如果你发现设备树、驱动、中断都正常但触摸还是没反应最后一步检查触摸芯片固件自检。GT911内部有一个“配置信息”区域上电后由主机写入配置和坐标范围如果这个区域是空的或者校验失败芯片虽然能通信但不会正常上报坐标。驱动日志里会提示配置校验失败重新用厂商工具烧写配置块可以解决问题。这种情况在二手屏、拆机屏上特别常见一定要留意。5.2 坐标漂移、跳动先检查电源和信号完整性电容触摸芯片对电源纹波非常敏感。我遇到过触摸点固定不动时事件里坐标数值会缓慢漂移几十个单位的情况后来发现是触摸芯片的VDD和背光供电走得很近背光工作时纹波耦合进了触摸采样电路。解决办法是在触摸芯片电源脚就近加一个10uF钽电容并联100nF陶瓷电容并把模拟地和数字地在单点汇合。I2C速率过高也会导致坐标偶发错乱。GT911官方支持最高400kHz但实际布线不理想时我建议把设备树里的clock-frequency改成100000牺牲一点响应速度换取稳定。调试这种问题不要凭感觉改代码先降速率排除通信问题再回头查布线。5.3 触摸偶尔失灵中断处理与休眠恢复触摸偶尔失灵但复位后又能用一段时间这种间歇性问题最难受。有一次查了很久发现是驱动申请的中断和另一个外设共享了线程优先级触摸中断偶尔被长时间阻塞。这种情况下可以在设备树里给触摸中断设置更高的优先级或者改成独立的hardirq触顶线程模式。电源管理休眠恢复也是一个坑。GT911在系统休眠恢复后内部状态可能错乱需要重新执行复位和配置初始化。新版内核驱动已经在suspend/resume回调里处理了复位但如果你自己改了电源管理或者使用了老版本BSP恢复后触摸无响应又没输出任何日志那就手动在resume后重置一次驱动状态。5.4 常见问题速查表我把调试中遇到的高频问题整理成表格方便遇到类似问题时快速定位现象排查方向典型解决办法完全没有触摸事件设备树匹配、I2C地址、中断i2cdetect确认地址检查compatible字符串检查reset时序驱动probe失败复位引脚、I2C通信用示波器观察复位时序确认irq-gpios与reset-gpios有效电平坐标漂移或跳动电源纹波、I2C速率加大电容滤波降低I2C速率到100kHz坐标方向不正确轴方向配置设备树加inverted-x/inverted-y/swapped-x-y坐标范围与屏幕不匹配芯片坐标寄存器未初始化确保设备树max值正确并重新加载驱动回写设备能识别但事件不触发中断注册或电平极性查看/proc/interrupts确认触发极性是否下降沿系统休眠后触摸失效电源管理处理缺失升级新版驱动在resume回调中复位触摸芯片红外触摸屏只有鼠标事件接口类型识别确认USB HID还是串口串口需专用驱动这张表基本覆盖了FPGALinux触摸屏开发里80%的常见故障。剩下20%通常来自板级硬件问题比如虚焊、阻抗不连续、触摸屏排线接触不良这些用软件看不出来必须拿表笔和示波器去量。6. 写在最后一点个人的体感这套东西做完我的体感是FPGA和Linux工程师之间真正的断层不在FPGA也不在Linux而在于“谁都不熟悉对方的边界”。FPGA工程师习惯把所有逻辑都做成状态机看到Linux的设备和驱动框架觉得繁琐Linux工程师看到FPGA里的时序和寄存器又觉得反直觉。但触摸屏驱动恰恰是两者之间的黏合剂FPGA把电路变成总线Linux把总线变成设备设备树则是一张沟通双方的图纸写错一个属性两边都找不到北。如果你也在做类似的项目我的建议是先花半天时间把开发板原理图里触摸部分的引脚关系全部理清把复位、中断、I2C地址确认到位再进Linux的设备树和驱动。硬件信息不清楚软件写得再熟练也是盲人摸象。最后再分享一个小技巧调试时别急着改代码先确认信号到哪了——I2C扫得到地址吗中断有计数吗坐标事件有上报吗按这个顺序排查绝大多数问题都能在十分钟内定位归零。
返回列表