1. 先认清一个反直觉的事实:触摸屏驱动不是FPGA的"主业"
1.1 Zynq平台把FPGA工程师推到了Linux门口
提到FPGA开发,很多人的第一反应是Verilog时序、Vivado综合、引脚约束,再进阶一点就是MIPI、LVDS、PCIe这类高速接口。这些确实是FPGA的舒适区。但黑金AX7Z020这类Zynq SoC出现后,情况发生了很有意思的变化:芯片内部一半是双核ARM Cortex-A9处理器,一半是标准的7系列FPGA逻辑,两者用AXI总线连起来。当你拿到配套的7寸触摸屏时,这颗屏的触摸芯片往往挂在PS端的I2C控制器上,而不是PL端。
这就带来一个关键选择:触摸屏的读取逻辑到底放在PL里用状态机实现,还是放到PS端用Linux驱动实现?
我从一开始就强烈建议选后者,不是因为我偏爱Linux,而是从工程角度算完账就会发现,用FPGA逻辑去做触摸解析是一件非常不划算的事情。触摸屏的数据量极小——一个触摸事件无非就是X坐标、Y坐标、压力值、触点ID,一次上报几十个字节,一秒钟几百次上报,这点数据量对硬件吞吐来说连九牛一毛都算不上。但触摸IC的协议却比较啰嗦,比如GT911上电要做时序握手,要读写配置寄存器,还要响应中断、做坐标滤波,用状态机硬啃这些逻辑,既浪费LUT资源,又会拖延整个项目的验证周期。
反过来看,Linux内核的input子系统对触摸屏的支持已经非常成熟,驱动框架现成,设备树写几个节点就能把硬件信息告诉内核,剩下的读寄存器、上报事件、处理多点触摸全由内核帮你搞定。FPGA侧真正应该操心的是高带宽、高并行、时序敏感的任务,比如把7寸屏的RGB显示数据流处理好,或者做图像采集、缩放、ISP处理,这些才是PL资源的用武之地。
1.2 黑金云课堂这条技术线的逻辑
黑金云课堂把"FPGA技术教程"和"Linux开发"放在一起,其实是把Zynq开发者的两条能力线拧成了一股绳:一条线是FPGA逻辑设计能力,另一条线是基于Linux的系统开发能力。单独会其中任何一条,在Zynq上都做不出完整产品。你就算把PL侧的显示时序做得再完美,如果PS侧没有人能把触摸屏驱动跑通,这个带交互界面的设备依然是一块会亮的哑巴屏。
亲眼见过很多FPGA工程师拿到Zynq板子后,启动Linux没问题,但一旦要新增一个外设就头疼。他们对内核编译、设备树修改、驱动模块加载这套流程不熟,遇到问题宁可在PL里绕路,也不愿意去碰内核。这种状态短期能撑,长期做项目一定吃亏。因为Zynq的SoC优势恰恰是"硬核CPU+可编程逻辑"协同,PS侧每多接管一个外设,PL侧就少一分负担,系统的整体稳定性和可维护性都会好很多。
所以这篇文章就是按照黑金云课堂这条主线,把"7寸触摸屏驱动"从硬件链路到设备树配置、再到驱动编译和实际调试的完整路径过一遍。适合两类读者:一类是长期写FPGA逻辑、想真正开始玩Zynq Linux的人;另一类是已经写过一些Linux驱动、但没在FPGA+SoC工程里完整走通流程的人。整个过程中我不打算跳过任何一个"看起来简单"的环节,因为很多坑恰恰藏在那些"简单"里。
2. 触摸屏硬件链路拆解:从手指按压到内核事件
2.1 三层结构:传感器面板、控制IC与主机接口
一块7寸触摸屏模块,从物理结构上可以拆成三个层次,想清楚这三个层次再去看驱动,整个逻辑会顺很多。
最底层是传感器面板。电容屏的面板内部有一层氧化铟锡透明导电层,蚀刻成行列交叉的电极矩阵。手指靠近时,手指与电极之间形成耦合电容,导致该交叉点的电容值发生变化。触摸控制IC通过不断扫描这些交叉点的电容变化,就能判断出哪个区域被触碰到。电阻屏的原理更古老也更简单,两层导电薄膜中间用微小的绝缘点隔开,按压时上下两层短路,通过分压关系计算位置。红外触摸屏则完全绕开了面板触控,在屏幕边框四周排列红外发射管和接收管,形成一个横竖交叉的红外线矩阵,手指挡住某条红外线时,对应的行列坐标就被记录下来。
中间层是控制IC。这颗芯片负责把传感器面板的模拟信号转换成数字坐标,同时做滤波、去抖、多点追踪这些脏活累活。对Linux驱动来说,控制IC就是一个可以通过I2C或USB接口访问的外设。电容屏常用GT911、FT5x06这类芯片,电阻屏常见TSC2007,红外触摸框常用Touchkit这类方案。不同的控制IC对应不同的通信协议,驱动代码自然也不一样。
最上层是主机接口。控制IC和处理器之间的物理连接方式决定了驱动怎么写。GT911通过I2C接口输出坐标数据,Touchkit红外触摸框多半是USB接口,插上后内核自动枚举成一个标准USB HID触摸设备,连专属驱动有时候都不需要,直接用内核的usbtouchscreen或hid-multitouch驱动即可。部分工控触摸屏还会用串口输出坐标协议,那就需要额外的串口驱动和协议解析层。
搞清楚这三层结构后,你再看内核里的触摸屏驱动就会觉得非常亲切。驱动做的事情无非是:通过接口读取IC中的坐标数据,做必要的格式转换,然后交给内核input子系统上报给应用层。中间没有玄学。
2.2 黑金7寸屏最常见的两种触摸配置
黑金的Zynq开发板搭配的7寸屏,我接触过的常见配置大致分两类。第一类是RGB接口的LCD面板,触摸部分通过I2C与PS端连接,触摸IC常见GT911或GT811系列。这类屏幕显示数据和触摸数据是分离的:显示走RGB并行总线,触摸走I2C。这也是7寸屏最常见的架构。第二类是USB接口的触摸配置,屏幕本身可能还是RGB/LVDS显示,但触摸框做成独立USB设备,插到开发板的USB Host口就能用,典型代表就是Touchkit红外触摸屏。
这两种配置在Linux驱动上走的是完全不同的路径。GT911属于I2C触摸屏设备,需要在设备树里显式声明I2C子节点,让内核知道哪个I2C控制器上挂了什么设备、用哪个中断引脚。Touchkit属于USB HID设备,设备树里不需要为它建节点,USB总线自己会做枚举,内核里的usbtouchscreen驱动根据VID/PID识别并绑定。很多第一次做触摸屏驱动的人容易在这一点上绕晕:明明文档里写了要加设备树节点,换了USB触摸屏后发现根本没有添加的地方。原因就在这里,不同接口类型的驱动工作方式完全不同。
从实践角度说,我更推荐初学者先拿GT911这类I2C触摸屏练手。因为I2C的链路更透明,出了问题可以用i2cdetect直接探测地址,可以抓I2C波形,调试手段丰富,能帮助你理解"设备树告知硬件拓扑"这个核心机制。USB触摸方案的调试相对黑盒,插上就动,插上不动时排查路径反而有限。
2.3 Linux input子系统:驱动和应用之间的"标准插座"
触摸屏驱动并不能直接把坐标数据随便交给应用层,中间必须要经过Linux的input子系统。你可以把input子系统理解成一个标准的插座:上游是各类输入设备驱动,下游是各类应用。触摸屏驱动通过input子系统注册一个input_dev设备节点,以后每次检测到触摸事件,就用input_event函数把事件上报给内核,内核经过事件分发后把数据写到对应的/dev/input/eventX设备节点。应用层只要打开这个节点,用read或者poll就能拿到触摸坐标。
无论驱动内部多么复杂,最终上报给input子系统的都是标准化的事件三元组:type、code、value。type指事件类型,触摸常用EV_ABS绝对坐标事件和EV_KEY按键事件;code具体到是哪个参数,比如ABS_MT_POSITION_X表示多点触摸的X坐标;value就是坐标值本身。多点触摸协议还有Type A和Type B之分,Type A按触点索引上报完整状态,Type B则用slot来跟踪触点生命周期,功能更强。现在主流的电容触摸屏都支持Type B多点上报,GT911驱动默认也是走Type B。
理解这层标准化机制非常重要。以后你无论接什么触摸屏,代码风格可能千差万别,但只要最终能把坐标事件通过input子系统正确上报,应用层的Qt、GTK、tslib就都能正常工作,完全不关心你驱动里用的什么IC。这种解耦设计是Linux外设驱动普遍遵循的哲学,也是为什么一个驱动人才能够快速上手不同硬件的原因。
3. 动手前的工程骨架:工具链、硬件规划与设备树基础
3.1 交叉编译环境:内核版本、工具链和文件系统要配套
Linux内核编译是交叉编译,必须在一台高性能PC上用交叉工具链生成ARM平台可以运行的镜像。我在工程里使用的组合是:黑金AX7Z020开发板,配套的Linux内核版本来自黑金资料包(基于Xilinx官方内核4.14或5.4分支),交叉编译工具链是32位ARM的arm-linux-gnueabihf-gcc。
需要注意的是,工具链版本和内核版本不要差太多,否则可能在编译内核时遇到奇怪的报错。我遇到过GCC 12编译老内核失败的情况,最后换回GCC 9就顺利通过。如果项目还没有固定工具链,我建议直接用Xilinx Vitis/PetaLinux自带的工具链,或者用Linaro提供的官方工具链,版本选与内核年代相近的。文件系统方面,建议初学者直接用黑金资料包里做好的rootfs,也就是镜像文件解压出来就能用的那种。如果自己用Buildroot或Yocto从零构建,调试阶段会多出很多不必要的变量,比如缺库、缺模块路径,容易把驱动问题掩盖掉。
内核编译时先设置两个环境变量,然后再执行配置和编译:
export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- make xilinx_zynq_defconfig make menuconfig make zImage -j8 make dtbsxilinx_zynq_defconfig是Xilinx内核提供的基础配置,会打开Zynq基本的外设支持。menuconfig打开的图形界面里,我们要找到触摸屏驱动相关的配置项。做完配置后编译zImage内核镜像和设备树dtb文件,这两个文件就是之后启动系统要用到的核心产物。
3.2 Vivado硬件工程里必须提前做对的引脚规划
设备树文件写的硬件信息,不是凭空编出来的,它必须和Vivado硬件工程里的实际连接一致。在Zynq里,I2C控制器和GPIO控制器都是PS侧的硬核外设,它们的物理引脚分配是在Vivado里通过配置PS端的MIO来实现的。
规划引脚时主要有两类信号要关注:触摸控制IC和PS之间的I2C通信引脚,以及触摸IC的中断输出引脚。GT911这类芯片一般有一个INT中断脚,用来通知主机"我这里有新触摸数据了"。这个INT脚可以接到PS端的某个MIO GPIO上,也可以接在PL的通用引脚上。如果I2C和中断都走PS端MIO,整个驱动流程会最顺畅,因为Linux内核可以直接使用PS端GPIO控制器来申请中断。如果INT脚接到了PL端,那么GPIO控制器就是PL里的AXI GPIO IP核,设备树写法会变得稍微复杂,还得额外挂接PL中断控制器。
我个人的建议是:如果是自己设计底板或者修改引脚约束,尽量把I2C和触摸中断都规划到PS MIO上。这样设备树简洁,中断申请逻辑清晰,板卡调试也更方便。记得在Vivado的硬件工程里使能对应的I2C控制器,配置好MIO引脚,综合导出XSA文件后,后续生成设备树时才会包含对应的节点。
3.3 设备树基础:设备树到底在干什么
设备树这个问题,很多FPGA背景的开发者第一次接触时都会觉得抽象。可以打个比方:设备树就是一份"硬件说明书",系统启动早期由BootROM读取并传给内核,内核通过这份说明书知道"这个板子上有什么设备、挂在哪个控制器下面、用了哪些资源和地址"。传统ARM Linux靠一堆写死的board file来硬编码这些信息,后来统一被设备树取代,目的就是让同一个内核能适配不同硬件板卡。
设备树文件以dts源码形式存在,编译成dtb二进制后供系统使用。Zynq开发流程中,设备树可以由Xilinx的device-tree-generator脚本从XSA文件生成基础版本,但生成的只是默认拓扑,触摸屏这种I2C子设备必须你自己往里面加节点。
我写一段典型的GT911设备树节点,方便对照后面的内容:
&i2c0 { status = "okay"; clock-frequency = <100000>; gt911@5d { compatible = "goodix,gt911"; reg = <0x5d>; interrupt-parent = <&gpio0>; interrupts = <36 IRQ_TYPE_EDGE_FALLING>; irq-gpios = <&gpio0 36 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio0 37 GPIO_ACTIVE_HIGH>; touchscreen-max-x = <800>; touchscreen-max-y = <480>; touchscreen-size-x = <800>; touchscreen-size-y = <480>; }; };compatible属性是驱动匹配的核心,内核的goodix驱动会检查这个字符串是否在自己的of_match_table里。reg属性是I2C设备地址,0x5d是GT911常见地址之一。interrupt-parent和interrupts描述中断信号接在哪个控制器、用的是哪个引脚。后两行irq-gpios和reset-gpios是驱动用来操作中断引脚和复位引脚的关键。touchscreen-max-*这些属性会把屏幕物理坐标范围告诉驱动,驱动再转换成内核事件上报。
关于Zynq设备树里的GPIO编号,简单说明一下:PS端GPIO控制器的节点名是gpio0,引脚编号采用一体化方式,MIO 0到MIO 53在节点里对应编号0到53,后面的编号留给PL端EMIO扩展。所以例子里<&gpio0 36>指的就是MIO36。
4. 两类主流触摸屏驱动的落地实操
4.1 GT911电容屏驱动:从内核配置到完整编译
GT911在内核里对应的源文件是drivers/input/touchscreen/goodix.c,历史悠久,稳定性很好。要让它生效,先在menuconfig里把驱动打开。路径是Device Drivers -> Input device support -> Touchscreens,找到Goodix touch controller,按Y编入内核,或者按M编成模块。如果已经通过defconfig把内核配置好,也可以直接在配置文件中添加CONFIG_TOUCHSCREEN_GOODIX=y。
打开驱动后,重新编译内核:
make zImage -j8 make dtbs编译完成后,把新的zImage和设备树dtb拷贝到启动卡或者通过网络加载启动。系统启动后在串口终端观察日志,如果一切正常,会看到类似"goodix-ts 0-005d: GT911 detected"的内核打印,这说明probe成功,驱动已和硬件正常匹配。
启动之后可以进一步确认设备节点是否存在:
cat /proc/bus/input/devices如果驱动正常工作,这个文件里会出现一个名字类似的输入设备条目,比如"Goodix Capacitive TouchScreen",后面跟着Handlers字段,显示eventX节点的编号。看到这个节点,说明驱动已经通过input子系统注册成功,接下来就是应用层测试的事了。
4.2 Touchkit红外触摸屏:USB HID方案的驱动思路
Touchkit红外触摸屏与GT911最大的不同在于接口类型。它通常是USB接口,系统启动后USB控制器会自动枚举这个设备,内核里的usbtouchscreen驱动会尝试识别并绑定。要验证设备是否被识别,插上触摸屏后执行lsusb,查看厂商ID和设备ID。内核drivers/input/touchscreen/usbtouchscreen.c里内置了一张已支持的设备ID表,如果Touchkit的VID/PID在表中,驱动会自动绑定,/proc/bus/input/devices里就会出现对应设备。
如果不在表中,就需要修改usbtouchscreen.c,把Touchkit的VID/PID添加到usbtouch_devices数组里,然后重新编译内核或模块。这种情况往往出现在一些小众触摸框上,逻辑并不复杂,难的是你要会改内核源码数组并重新编译。注意USB设备不需要设备树节点,因为USB本身就是可枚举总线,设备的身份信息由USB协议自带。
usbtouchscreen驱动注册后,同样通过input子系统上报事件。和GT911相比,它不涉及I2C地址、复位时序、中断引脚这些硬件级配置,调试上相对简单:插上就动,插上不动就从USB枚举环节开始查。
4.3 编进内核还是独立模块:两种方式的取舍
触摸屏驱动的编译形态有两种选择:编进内核,或者编译成.ko模块。内核镜像中编入驱动的好处是开机即用,不需要额外加载模块,适合最终产品形态;缺点是每次修改驱动代码都要重新编译整个内核,调试效率低。模块方式的优点是单独编译、单独加载,开发阶段可以反复insmod/rmmod,不需要频繁烧内核,但要求驱动代码与当前运行内核版本严格匹配,否则模块加载时会出现vermagic不匹配的error,报"Invalid module format"。
我自己的开发习惯是:调试阶段把所有触摸驱动编成模块,配合一个简单的init脚本自动加载;验证没问题后再重新配置内核把这些驱动编进去。这样既保证调试效率,又能保证最终产品干净。如果文件系统支持,也可以把模块文件拷贝到/lib/modules/$(uname -r)/目录下,用depmod生成模块依赖,再通过modprobe动态加载,这样做更接近发行版的管理方式。
关于模块加载后的事件节点,还需要提醒一点:/dev/input/eventX的编号是由内核动态分配的,不是固定的。应用层最好通过/dev/input/by-path或者通过event界面名字去匹配设备,不要硬编码eventX编号,否则系统重启或者添加新输入设备后,应用可能会访问到一个完全错误的设备节点。
5. 调试链路全解:从完全没反应到手指流畅滑动
5.1 先回答"硬件有没有被认到"
拿到一块触摸屏,无论接口是I2C还是USB,调试的第一步永远是确认硬件有没有被内核认到,而不是急着改驱动代码。确认方式就是看内核日志。系统启动后执行dmesg | grep -i goodix,或者dmesg | grep -i touch,如果有probe成功的信息,很快就能进入下一环节。如果没有,问题往往出在设备树配置、I2C地址或引脚连接上。
对于I2C接口的GT911,应该用i2cdetect工具扫描I2C总线,看看目标地址上是否有ACK响应。命令格式:
i2cdetect -y 0这个命令会扫描i2c-0总线上的所有7位地址,有设备响应的地址会显示为编号。正常情况下,0x5d或者0x14这个位置会显示设备编号,具体是哪个取决于GT911的上电时序决定的工作地址。如果扫描不到设备,很大概率是触摸IC没有正常工作,或者I2C线的引脚接错,再或者是设备树里的reg地址与芯片实际配置不符。这时就要回到上电时序去查。
GT911有一个非常典型的上电时序要求:芯片的RST引脚先拉低,INT引脚也拉低,然后电源稳定,等待若干毫秒后把RST拉高,再等待一段时间后INT释放为高。这个时序如果出错,芯片会进入从机地址切换模式,导致I2C地址和预期不一致。很多"我用i2cdetect找不到设备"的问题,最终都出在这个时序上。如果你的板卡由Linux侧驱动控制复位和中断引脚,可以在驱动probe过程里仔细核对这段时序逻辑。
5.2 从proc文件系统打到真实事件数据
硬件被认到只是第一步,驱动注册了input设备之后,还要确认事件能不能正常上报。最快的验证方法是读取/proc/bus/input/devices确认event节点编号,然后用hexdump直接读设备节点:
hexdump /dev/input/event1手指点击触摸屏时,终端里会持续刷出二进制数据。这些数据按input_event结构体排列,包含时间戳、type、code、value。如果hexdump有输出,说明驱动已经成功上报事件,整条链路已经通了。如果hexdump没有任何输出,大概率问题出在中断没有触发,或者驱动解析触摸IC数据失败。
这一步最常见的坑是设备树里中断号或中断触发类型配置不正确,导致触摸IC的中断信号没有正确上传到内核。排查时可以先用debugfs确认GPIO中断是否注册,或者临时在驱动里加printk观察中断处理函数有没有被调用。另外,确认触摸IC是否真的把INT引脚拉低了——如果INT引脚配置为上拉,而触摸IC通过拉低来通知主机,那么引脚电平状态不对,中断就不会触发。
5.3 tslib坐标校准:解决"点不准"的问题
触摸事件能上报,但屏幕上手指位置和光标位置有偏差,这是另一个高频问题。触摸坐标和设备显示分辨率之间并不是天然对齐的,比如7寸屏物理分辨率是800x480,但触摸IC输出的坐标范围可能不是完整的0到799、0到479,加上屏幕贴合角度、安装偏移等因素,直接映射就会造成偏差。内核里通过touchscreen-max-x和touchscreen-max-y属性做了一次粗映射,但真正高精度的对齐需要应用层校准。
在嵌入式Linux里,tslib是事实标准。tslib提供了一组库和工具,包括校准和滤波功能。使用前要配置环境变量指向正确的事件设备和校准文件:
export TSLIB_TSDEVICE=/dev/input/event1 export TSLIB_CALIBFILE=/etc/pointercal ts_calibratets_calibrate会显示几个十字校准点,依次点击后,tslib会根据触摸坐标和屏幕坐标的对应关系计算出转换参数,保存到校准文件里。之后应用通过tslib读取的坐标就已经是校准后的屏幕坐标。Qt程序里如果设置了QT_QPA_FB_TSLIB环境变量,就会自动使用tslib的设备,配合触摸屏使用非常顺手。
如果校准点非常不准,并且点击顺序错乱,建议检查电容触摸屏的坐标原点定义。触摸IC的坐标原点可能在左上角,也可能在右下角,设备树里的坐标属性能调整方向,比如touchscreen-inverted-x和touchscreen-inverted-y,它们的作用是翻转坐标轴向,解决镜像和颠倒问题。
5.4 一张表收拢七大高频故障
调试过程中最怕的是问题现象千奇百怪,定位时没有头绪。我根据自己的经验整理过一张速查表,每次遇到类似问题,先按表里的优先级顺序排查。
| 故障现象 | 大概率原因 | 首选排查动作 |
|---|---|---|
| i2cdetect扫不到设备 | GT911上电时序不对或I2C地址错误 | 检查复位/中断时序,尝试0x14和0x5d两个地址 |
| dmesg报设备树probe失败 | compatible字符串与驱动不匹配 | 比对设备树compatible和驱动of_match_table |
| 设备节点注册了但无上报数据 | 中断配置错误或INT引脚接错 | 检查设备树interrupts与硬件连接是否一致 |
| hexdump出现乱码/无规律数据 | I2C地址冲突或总线不稳定 | 降低I2C时钟频率,检查上拉电阻 |
| 事件正常但Qt/应用收不到 | 环境变量未指向正确event节点 | 检查TSLIB_TSDEVICE及Qt相关环境变量 |
| 触摸方向和屏幕相反 | 设备树缺少坐标翻转属性 | 添加touchscreen-inverted-x/y |
| 一段时间后触摸失灵 | 驱动与芯片固件配置不同步 | 检查内核版本,必要时更新驱动 |
6. 踩坑记录与写给FPGA工程师的学习路径
6.1 三个让我印象深刻的坑
第一个坑是MIO编号的错位。Zynq设备树里PS GPIO的编号从0开始对应MIO,但很多从PL转过来的工程师习惯把PL引脚的编号也往里塞,结果写成<&gpio0 36>这种本该对应MIO36的写法,实际连的是某个PL引脚,导致中断完全挂不上。这个问题的排查其实更快的方法是把引脚信息重新核对一遍,确认Vivado里定的引脚与设备树节点完全一致。
第二个坑是GT911的I2C地址两个都试过,都失败。后来发现不是地址问题,而是驱动里读取配置数据时校验失败,报"invalid config data"。这个问题通常是因为设备树里声明的坐标尺寸与芯片内已有配置不一致,或者IC内部的配置区被写入了错误数据。重新上电执行一次复位时序,让驱动重新发送配置参数,问题就消失了。这种"软件不像软件、硬件不像硬件"的问题最磨人,但也是成长最快的地方。
第三个坑是内核模块的vermagic不匹配。自己用新工具链编译出的goodix.ko,加载到开发板之后系统报Invalid module format。原因是模块编译时的内核版本、编译器版本与运行内核不一致。解决方案是使用与构建内核同一套工具链和同一份内核源码来编译模块,或者直接把驱动编进内核。这个坑在初学者中几乎是必踩的,但踩过一次之后,对内核模块机制的理解会上一个台阶。
6.2 按这个顺序学,能帮你少走很多弯路
很多FPGA工程师想转Linux驱动开发,一上来就看内核源码,结果被各种机制淹没。我个人的建议是分三步走。第一步先学会改设备树、编内核、启动系统,把"修改设备树->编译内核→运行→验证"这条基本链路跑通,不需要关心代码细节;第二步挑一个简单的驱动demo,比如GPIO按键驱动,看懂probe、open、read这些回调函数,体验一遍模块加载和调试的完整过程;第三步再碰触摸屏这类带中断、带input子系统的设备驱动,因为这里同时涉及异步事件上报和框架对接,更有挑战性。
触摸屏驱动本身并不复杂,但它是一个理想的训练项目:I2C通信、设备树、中断、input子系统、应用层交互,整条链路全部涉及。把一个7寸触摸屏从零跑到随手滑动,其实已经把一个嵌入式Linux工程师最常用的知识面全部覆盖了。做完这个项目,你对"从硬件到应用"的整体链路会有一个完整的认识。
6.3 触摸屏跑通之后,这条路还能往哪里延伸
触摸屏驱动只是黑金云课堂里"FPGA Linux开发"的一个入口。跑通触摸交互后,后面的扩展路径其实非常顺:同样是I2C接口,可以去接各种传感器、EEPROM、RTC;同样的中断+input框架,可以去接按键、编码器、遥控器;再往高性能方向走,就可以把FPGA的看家本领拿出来,用PL端做MIPI或LVDS图像采集,用AXI DMA把图像数据搬到内存,然后在Linux应用层做显示和处理,这个时候你手边的这块7寸屏就成了一个实时图像显示终端。
我个人在实际操作中的体会是,Zynq开发最让有成就感的地方,不是某一根信号走得多顺,也不是某个时序收敛得多漂亮,而是你亲眼看到PL和PS两个世界在同一个系统里协同工作:PL负责把海量数据流转起来,PS负责把人机交互和系统调度跑起来,中间由Linux设备树和驱动来牵线搭桥。把这条链路理解透,之后再接任何外设、做任何项目,底气都会完全不同。