
简介面向嵌入式 Linux 开发者的 TP28xx 系列芯片与 TP9930 模块内核驱动源码包适用于需要对接 TP2828/TP2831 系列芯片及 TP9930 触摸屏控制器的物联网、工业控制设备场景。压缩包共 14 个文件以 C 源码为主含 9 个 .c 驱动实现、2 个 .h 头文件以及 3 个 Makefile 编译脚本整体仅 76KB结构简洁。源码覆盖设备初始化、寄存器配置、数据传输及 ioctl 控制等关键逻辑可帮助驱动开发人员快速理解 TP28xx 与 TP9930 的交互方式同时提供 Makefile 便于直接编译内核模块也适合作为学习 Linux 字符设备驱动编写的参考。已有 1429 人学习下载适合具备一定嵌入式基础、希望绕过手册直接阅读真实驱动的开发者。 干嵌入式Linux这行隔三差五就会从方案商或者原厂手里拿到一个.tar.gz驱动包。最近项目里轮到一块新触摸屏对方甩过来的就是tp28xx_kdrv_tp9930.tar.gz。看名字就知道这是一颗TP28xx系列触摸控制芯片具体型号TP9930的Linux内核驱动源码kdrv就是kernel driver的缩写。这篇文章就记录我从解压、改Makefile、编译到最终在设备上把触摸调通的完整过程还包括几个我在调试时踩过、也帮别人排过的坑。如果你也在跟触摸驱动或者类似的I2C外设驱动打交道这篇东西应该能帮你少走点弯路。1. 先搞清楚这个包里装的是什么1.1 tp28xx_kdrv与TP9930的来龙去脉拿到一个文件名第一件事不是急着解压而是先看懂命名。tp28xx_kdrv_tp9930.tar.gz这个字符串拆成三段就很好理解了tp28xx是芯片系列名说明这颗芯片属于TP28xx系列kdrv是kernel driver的缩写表明这份代码是内核态驱动不是用户态库tp9930是具体芯片型号通常对应特定封装和丝印版本。也就是说这份源码是给TP9930这颗触摸控制芯片用的运行在Linux内核态。这里得先给刚入门的朋友补个背景触摸屏模组本身只是一块感应玻璃电容式或电阻式真正把手指位置算出来的是那颗触控IC。TP9930就是这样的触控IC它通过I2C或SPI接口和主控芯片通信有触摸事件时通过中断IRQ通知主控“有人摸了一下”主控再通过I2C读回坐标数据上报给Linux的input子系统最后变成你在屏幕上看到的点击和滑动。这类芯片出现在什么场景里我这次是给一块10.1寸的工控触摸屏做驱动适配。工控屏、车载中控、智能终端这类场景有几个共同点屏幕尺寸不会太小通常7寸到15.8寸环境干扰比较重电磁噪音、温度变化对触摸响应有实时性要求。所以TP9930这类芯片一般会把手势识别、噪声滤波、坐标上报这些活儿在内部干完主控端只需要负责读数据、做校准逻辑上并不复杂。1.2 tar.gz这种包为什么Linux驱动圈这么爱用顺带说一句tar.gz。很多刚接触Linux的朋友会问为什么内核驱动源码都喜欢发.tar.gz而不是直接给deb或rpm原因很简单驱动要跟着内核版本走同一个驱动在不同内核版本上要重新编译分发二进制的deb/rpm意义不大。tar.gz本身就是Linux世界最通用的源码打包格式。tar负责把一堆文件打成一个归档gzip负责压缩。你平时看到的e2fsprogs 1.46.6.tar.gz、Linux内核源码tar.xz、OpenSSL源码包全是同一套路。它不依赖任何包管理器只要有tar和gzip就能解开。对驱动发布方来说把源码、Makefile、README、dts示例装进一个tar.gz一发收件方拿到手就能开干这是所有方案商的默认选择。所以见到tp28xx_kdrv_tp9930.tar.gz心里要有数这是一份需要自己编译、自己适配内核的源码不是装上就能用的二进制。后面所有步骤都是围绕“怎么把它变成能跑起来的.ko模块”展开的。2. 解压与编译前的环境准备2.1 解压tar.gz的几个操作细节解压.tar.gz很多人上来就是tar -zxvf这也对但我建议再多走一步。先看压缩包里有什么内容再决定解压方式tar -tzf tp28xx_kdrv_tp9930.tar.gz-t参数是列出内容-z是处理gzip-f指定文件名。这一步能在解压前看清里面有哪些文件、目录结构是什么样的避免解压出一堆文件把当前目录弄得乱七八糟。如果看到压缩包根目录不是单一文件夹最好先建一个临时目录再解mkdir tp9930_driver cd tp9930_driver tar -xzf ../tp28xx_kdrv_tp9930.tar.gz注意我用了-xzf而不是-xzvf。-v参数会把每个解压的文件名都打印出来小包无所谓大包刷屏刷得心烦真正解压用-xzf就够了。解压出来后包里通常会有这几类东西tp28xx_kdrv.c和tp28xx_kdrv.h驱动源码和头文件、Makefile驱动编译脚本、dts或dtsi目录设备树片段示例、README或Readme.txt编译说明可能还有一个tp9930.cfg这类芯片寄存器配置文件。这里有个强烈建议先读README。方案商写的README基本都会写明内核版本要求、编译方式、设备树修改方法。跳过README直接改代码是新手最容易踩坑的路径。我见过不止一次有人在README已经写清楚“内核版本需5.10及以上”的情况下还拿着4.19的内核去编结果报一堆错其实问题根源就在版本不匹配。2.2 编译驱动前内核环境要备齐什么驱动不是普通应用它不能独立编译成可执行文件最终是以内核模块.ko的形式加载进内核。所以编译前要满足三个条件有内核源码树或者至少完整的kernel headers内核源码树的版本和最终运行的内核版本一致有和目标平台匹配的交叉编译工具链。如果是在PC上先编译验证直接装对应版本的linux-headers包就行。但在嵌入式目标板上通常要拿到板卡厂商的BSPBoard Support Package里面包含完整内核源码。我这次用的是官方BSP的内核源码路径在~/workspace/kernel交叉编译工具链是aarch64-linux-gnu-目标平台是aarch64架构。确认内核源码目录已经配置过生成过.config。如果还没配置先进内核目录执行cd ~/workspace/kernel make ARCHarm64 defconfig # 或者 make ARCHarm64 menuconfig内核模块编译依赖内核源码根目录下生成的Module.symvers等文件。如果BSP没预先编译过内核最好先把内核完整编译一遍确保符号文件齐全否则后面驱动编译时会遇到大量“unknown symbol”的报错。这一步比较耗时间但省不掉我踩过这个坑白等了好几个小时的交叉编译。3. 驱动编译与安装实操3.1 修改Makefile让模块编得起来打开tp28xx驱动目录下的Makefile基础的模块Makefile长这样obj-m tp28xx_kdrv.o KERNEL_DIR ? /lib/modules/$(shell uname -r)/build PWD : $(shell pwd) all: $(MAKE) -C $(KERNEL_DIR) M$(PWD) modules clean: $(MAKE) -C $(KERNEL_DIR) M$(PWD) clean在交叉编译场景下这个Makefile肯定要改。KERNEL_DIR要指向目标板的内核源码树还要加上ARCH和CROSS_COMPILEobj-m tp28xx_kdrv.o KERNEL_DIR ? /home/user/workspace/kernel ARCH ? arm64 CROSS_COMPILE ? aarch64-linux-gnu- all: $(MAKE) -C $(KERNEL_DIR) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) M$(PWD) modules clean: $(MAKE) -C $(KERNEL_DIR) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) M$(PWD) clean这里有个容易踩的点obj-m那一行指定的是编译出来的模块目标但如果驱动源文件不止一个比如tp28xx_kdrv.c还依赖tp9930_hal.c就得这么写tp28xx_kdrv-objs : tp28xx_kdrv.o tp9930_hal.o obj-m tp28xx_kdrv.o如果只写obj-m tp28xx_kdrv.o而源文件又多几个make会直接报找不到源文件不少人会卡在这一步。先排查Makefile里的源文件列表对不对再怀疑别的。改完Makefile直接make。看到tp28xx_kdrv.ko出现说明编译过了。但编译通过只代表语法和类型对得上驱动能不能正常加载还要看接下来的步骤。3.2 设备树、insmod与input节点TP9930这类触摸IC一般挂在I2C总线上。要让内核驱动找到这颗芯片光加载.ko还不够得让设备树告诉内核“I2C总线上有一个TP9930地址是0x38中断脚是GPIO1_2”。设备树片段大概是这样的i2c2 { status okay; tp9930: tp993038 { compatible tp,tp28xx_kdrv; reg 0x38; interrupt-parent gpio1; interrupts 2 IRQ_TYPE_EDGE_FALLING; reset-gpios gpio1 3 GPIO_ACTIVE_LOW; touchscreen-size-x 1024; touchscreen-size-y 600; }; };compatible字段必须和驱动源码里的of_match_table匹配。如果驱动里写的是tp,tp9930设备树写了tp,tp28xx_kdrv驱动怎么都匹配不上。这个坑特别隐蔽因为编译不报错insmod也可能不报错但probe函数就是不跑。把设备树节点加进板级dts重新编译设备树烧录或放到boot分区然后加载驱动insmod tp28xx_kdrv.ko # 或者 modprobe tp28xx_kdrv加载成功后在/dev/input/下会多出一个event节点比如event1用hexdump直接读hexdump /dev/input/event1手指在触摸屏上滑动时能看到一串input_event结构体数据说明从芯片到内核的数据链路已经通了。如果hexdump没有任何输出就要进入调试环节了。4. 常见问题与排查技巧实录4.1 编译阶段的两个高频报错先说两个我在这次编译过程中真实遇到的坑都很有代表性。第一个报错是error: implicit declaration of function gpio_request。驱动源码是方案商基于较老的内核API写的在新内核5.x以上里gpio_request这类函数已经被整理或标记废弃了编译时直接报隐式声明。解决办法有两种一是改源码换成新内核推荐的gpiod_*接口二是取巧在Makefile里加个内核版本判断或者直接包含对应头文件。我这次因为方案商代码量不大直接把gpio_request换成了devm_gpio_request_one顺便把释放函数删掉省得在probe失败路径上忘记释放造成资源泄漏。顺便说一句devm_开头的接口是managed版本资源随设备生命周期自动释放写驱动优先用它能省很多事。第二个报错是Unknown symbol in module。.ko编译出来了insmod却报unknown symbol。这种问题九成是内核版本和编译用的源码版本不一致导致的。比如BSP内核是5.10你用发行版5.15的内核源码去编驱动模块里的内核符号版本校验过不去。解决办法很简单但很考验耐心确认运行的uname -r和KERNEL_DIR指向的内核源码版本完全一致重新编译一遍驱动。4.2 触摸没反应问题不一定在驱动驱动加载了event节点也出来了但触摸屏幕没反应。我的排查思路是按顺序走的也推荐你这么做。第一步看dmesg。加载驱动后马上执行dmesg | tail -50。如果看到probe成功、注册input设备成功的日志说明I2C通信没问题如果看到类似irq handler或failed to request irq的报错说明中断没配上优先查设备树interrupts字段。第二步确认中断有没有触发。可以在驱动irq handler里临时加一个计数打印或者用gpio命令手动拉一下中断脚看系统有没有响应。实际操作中我发现一种很隐蔽的情况芯片的中断脚是开漏输出板上没有上拉电阻导致中断信号根本送不到主控。这不是软件问题是硬件问题查了一个多小时才定位到。第三步排除坐标校准问题。触摸坐标乱跳或者只有某个区域能触发多半是触摸屏的x/y最大值和设备树里touchscreen-size-x/y不匹配。TP9930这类芯片一般都支持从寄存器直接读raw坐标可以先把驱动改成读raw坐标看看数值范围再回填到设备树。这个技巧在调试新屏幕时非常管用能快速确认是驱动换算问题还是设备树配置问题。4.3 模块加载失败后的三板斧insmod失败的时候除了看dmesg还可以按顺序检查下面三件事先lsmod确认模块是否已经在系统里。重复insmod会报module already loaded先rmmod再试。用modinfo tp28xx_kdrv.ko查看模块的vermagic确认和当前内核版本一致。有时候你对着源码目录觉得没问题但实际跑的kernel是另一个一查vermagic就露馅了。如果加载时提示设备树匹配不上去/sys/firmware/devicetree/base/下找对应节点确认设备树有没有真的刷进去。我遇到过设备树编译成功了但烧录时分区选错导致系统boot的是旧版本设备树的情况。这三板斧能解决八成模块加载问题。我每次遇到加载失败都会先过一遍这三个检查再决定要不要往深了调。4.4 evtest触摸调试的神器说到触摸调试工具必须推荐evtest。它比hexdump友好太多能直接显示是哪个事件类型、哪个按键码、坐标值是多少。很多触摸问题一眼就能从evtest输出看出来比如单点触摸和多点触摸的设备能力位就不一样。安装和基本用法都很简单apt install evtest evtest /dev/input/event1它会实时打印事件流。如果手指动但没有任何输出说明驱动上报链路有问题如果输出坐标值明显超出屏幕范围就是配置问题如果只有按下没有抬起事件可能是中断处理逻辑有缺陷。这些信息比盲改代码快得多。5. 实操心得与避坑总结这节不写代码写点更值钱的经验。第一拿到tp28xx_kdrv_tp9930.tar.gz这种包先确认驱动里默认的I2C地址是不是项目实际使用的。TP9930一般有多个可选I2C地址由芯片地址引脚的电平决定。方案商给的驱动默认地址是0x38但你的板子可能拉到了0x39。如果不改设备树reg字段I2C扫描根本发现不了芯片probe也不会被调用。排查这个问题最快的方法是做I2C总线扫描i2cdetect -y 2在I2C2总线上能看到哪些地址有设备回应对照芯片手册就能确定实际地址。这一步能省下大量猜测时间。第二如果触摸屏表面贴了保护膜或者出厂膜没撕TP9930的电容检测会偏常见表现是触摸偶尔失灵或者坐标漂移。遇到这种现象不要先怀疑代码先让客户把膜撕了再测。这个听起来像段子但真实项目里发生过不止一次。第三驱动编译报错时不要硬着头皮改源码。先确认内核版本、源码路径、交叉工具链是否匹配八成问题都在这里。源码本身有bug的概率远比你环境搭错的概率低。我在好几个项目里总结下来真正需要动源码逻辑的case不到两成剩下都是环境问题。这个包弄完之后我又给它补了电源管理suspend/resume支持和触摸唤醒功能这是TP9930后续要用到的能力。如果后面有机会我再把电源管理部分的移植过程单独写一篇那个坑更多。本文还有配套的精品资源点击获取