嵌入式Linux开发里,触摸屏驱动移植算是绕不开的一道坎。尤其是拿到一块新屏幕模组,供应商给过来的往往只有一份PDF规格书、一个可能跑不通的初始化序列,剩下的全靠自己啃。RV1126这颗芯片在视觉和显示类产品里用得很多,JD9366又是一颗常见的MIPI屏驱动IC,两者凑在一起做移植,涉及设备树配置、内核驱动适配、编译烧录、上电验证一整条链路。这篇内容就把我在RV1126上把JD9366触摸屏从源码改到点亮跑通的完整过程拆开讲,包括每一步为什么这么做、参数怎么算、踩过哪些坑。适合正在做瑞芯微平台显示驱动移植的工程师,也适合刚接触设备树和内核编译、想找一个完整案例跟下来的朋友。
1. 先搞清楚JD9366和RV1126各自扮演什么角色
动手改代码之前,得先把这两块东西的定位理清楚,不然后面配置参数的时候很容易张冠李戴。
1.1 JD9366是显示驱动IC,不是触摸IC
很多人一看到"触摸屏驱动"就默认是触摸控制器的事,其实这里有个容易混淆的点。JD9366本质上是一颗MIPI DSI接口的显示驱动IC,它负责的是把主控送过来的图像数据转换成屏幕面板能显示的时序信号,控制的是"显示"这一侧。而真正处理手指触摸坐标的,通常是另一颗触摸控制器芯片(比如常见的电容触摸IC),走的是I2C接口。
所以"JD9366触摸屏驱动移植"这个说法,实际要拆成两条线:
- 显示链路:RV1126的MIPI DSI控制器 → JD9366 → 屏幕面板,这条线靠设备树里的DSI节点和panel配置来打通。
- 触摸链路:RV1126的I2C控制器 → 触摸IC → 中断引脚,这条线靠设备树里的I2C节点和中断配置来打通。
两条线在设备树里是分开描述的,但物理上装在同一个模组里。移植的时候如果只配了显示没配触摸,屏幕能亮但点不动;只配了触摸没配显示,能点但黑屏。这个区分是后面所有工作的前提。
1.2 RV1126的显示子系统结构
RV1126的显示输出通路大致是这样的:VOP(Video Output Processor,视频输出处理器)负责图层合成和时序生成,然后通过MIPI DSI控制器把数据发出去。设备树里需要描述的核心节点包括:
- VOP节点:配置输出时序、分辨率、时钟极性等。
- MIPI DSI节点:配置lane数量、时钟频率、数据格式。
- Panel节点:描述屏幕本身的分辨率、初始化序列、电源时序。
- 背光节点:PWM或GPIO控制背光亮度。
这几块是联动的,改一个地方往往要同步改另一个。比如分辨率改了,VOP的时序和panel的配置都得跟着动,否则会出现画面偏移或者花屏。
1.3 移植前需要拿到的资料清单
在正式动手前,我一般会先把这些资料凑齐,缺一样后面都会卡住:
| 资料类型 | 具体内容 | 用途 |
|---|---|---|
| 屏幕规格书 | 分辨率、接口类型、lane数、时序参数 | 配置panel和DSI节点 |
| 初始化序列 | 厂商提供的寄存器配置命令 | 写进panel的init sequence |
| 引脚定义 | MIPI差分线、I2C、中断、复位、背光引脚 | 核对硬件连接 |
| 触摸IC规格书 | I2C地址、中断触发方式、坐标范围 | 配置触摸节点 |
| 原理图 | 主控与模组的连接关系 | 确认引脚复用和电平 |
规格书里最关键的几个参数是:水平/垂直有效像素、前后沿(porch)、同步信号宽度、像素时钟。这几个数直接决定VOP时序配置,算错了屏幕要么不亮要么显示异常。
2. 设备树配置:显示链路怎么一步步搭起来
设备树是整件事的核心,配置对了基本就成功一大半。我按显示和触摸两条线分别说。
2.1 MIPI DSI节点的关键参数
RV1126的DSI节点在设备树里通常长这样,我把它拆开解释每个字段的含义:
&dsi { status = "okay"; rockchip,lane-rate = <1000>; panel@0 { compatible = "simple-panel-dsi"; reg = <0>; backlight = <&backlight>; reset-gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_LOW>; enable-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>; dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST)>; dsi,format = <MIPI_DSI_FMT_RGB888>; dsi,lanes = <4>; panel-init-sequence = [ /* 厂商提供的初始化命令 */ ]; display-timings { native-mode = <&timing0>; timing0: timing0 { clock-frequency = <68000000>; hactive = <800>; vactive = <1280>; hback-porch = <40>; hfront-porch = <40>; hsync-len = <10>; vback-porch = <20>; vfront-porch = <20>; vsync-len = <4>; }; }; }; };lane-rate这个参数要重点说。它指的是每条MIPI lane的传输速率,单位是Mbps。计算方式是:
lane-rate = 像素时钟 × 每像素bit数 / lane数量
以800×1280、RGB888(24bit)、4 lane、像素时钟68MHz为例:
68000000 × 24 / 4 = 408000000 bps = 408Mbps
但实际配置时一般会留余量,取500Mbps到1000Mbps之间。我这次用的是1000Mbps,跑下来很稳。如果lane-rate设太低,会出现带宽不够导致画面撕裂;设太高又可能超出屏幕支持范围。
2.2 display-timings里的时序参数怎么算
display-timings是决定画面能不能正常显示的关键。这几个参数的含义:
- hactive/vactive:水平/垂直有效像素,就是分辨率。
- hback-porch:水平后沿,同步信号之后到有效数据之前的空闲像素。
- hfront-porch:水平前沿,有效数据之后到下一个同步信号之前的空闲像素。
- hsync-len:水平同步信号宽度。
- vback-porch/vfront-porch/vsync-len:对应的垂直方向参数。
- clock-frequency:像素时钟,单位Hz。
这些参数全部来自屏幕规格书,不能随便填。像素时钟的计算公式是:
clock-frequency = (hactive + hback-porch + hfront-porch + hsync-len) × (vactive + vback-porch + vfront-porch + vsync-len) × 刷新率
以800×1280、60Hz刷新率为例,代入上面的参数:
(800 + 40 + 40 + 10) × (1280 + 20 + 20 + 4) × 60 = 890 × 1324 × 60 ≈ 70.7MHz
规格书给的68MHz是标称值,实际配置时按计算值微调。我实测68MHz和70MHz都能正常显示,差别不大。
2.3 初始化序列的格式转换
厂商给的初始化序列通常是一堆十六进制命令,格式可能是这样的:
0xE0, 0x00 0xE1, 0x93 0xE2, 0x65 ...但设备树里panel-init-sequence需要的是特定格式,每个命令要带上类型、延时、长度、参数。RV1126的格式大致是:
panel-init-sequence = [ 15 00 02 E0 00 15 00 02 E1 93 15 00 02 E2 65 ... ];每个命令的格式是:数据类型 延时 长度 参数...
- 数据类型:0x15表示DCS短写(带一个参数),0x05表示DCS短写(无参数),0x39表示DCS长写。
- 延时:命令执行后的等待时间,单位ms,一般填0x00。
- 长度:参数字节数。
- 参数:具体的寄存器地址和数据。
这个转换是纯体力活,但很容易出错。我的做法是写个脚本批量转换,避免手抖。转换完还要注意命令的顺序不能乱,尤其是上电初始化那段,顺序错了屏幕可能直接不亮。
2.4 背光节点的配置
背光一般用PWM控制,设备树里这样配:
backlight: backlight { compatible = "pwm-backlight"; pwms = <&pwm4 0 25000 0>; brightness-levels = <0 4 8 16 32 64 128 255>; default-brightness-level = <6>; enable-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>; };pwms里的25000是PWM周期,单位纳秒,对应40kHz频率。这个频率要避开屏幕背光驱动电路可能产生的啸叫频段,一般20kHz到50kHz之间比较合适。brightness-levels是亮度分级表,default-brightness-level是默认亮度索引。
背光不亮是最常见的问题之一,排查顺序是:先确认enable-gpios电平对不对,再看PWM有没有输出,最后查背光电路供电。
3. 触摸链路:I2C节点和中断配置
显示配好了屏幕能亮,但要点得动还得把触摸这条线打通。
3.1 触摸IC的I2C节点描述
触摸IC挂在I2C总线上,设备树里要描述它挂在哪条总线、地址是多少、中断接在哪个GPIO:
&i2c1 { status = "okay"; clock-frequency = <400000>; touchscreen@38 { compatible = "vendor,touch-ic"; reg = <0x38>; interrupt-parent = <&gpio0>; interrupts = <RK_PB0 IRQ_TYPE_EDGE_FALLING>; reset-gpios = <&gpio0 RK_PB1 GPIO_ACTIVE_LOW>; irq-gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>; touchscreen-size-x = <800>; touchscreen-size-y = <1280>; }; };几个关键点:
- reg:I2C从机地址,7位地址,规格书里会写。注意有些规格书给的是8位地址,要右移一位。
- interrupts:中断触发方式,电容触摸IC一般是下降沿触发(IRQ_TYPE_EDGE_FALLING)。
- touchscreen-size-x/y:触摸坐标范围,要和显示分辨率对应,否则会出现点击位置偏移。
3.2 中断触发方式的坑
中断触发方式配错是触摸不灵或完全没反应的常见原因。电容触摸IC在检测到触摸时会拉低中断引脚,所以用下降沿触发。但有些IC是电平触发,或者需要配置成双边沿。这个要看规格书,不能想当然。
我遇到过一次,中断配成上升沿,结果触摸完全没反应。改成下降沿后正常。排查这种问题可以用示波器看中断引脚有没有波形,或者用cat /proc/interrupts看中断计数有没有增长。
3.3 坐标映射和方向调整
触摸坐标和显示坐标不一定一致,尤其是屏幕旋转之后。比如竖屏改横屏,触摸的x/y需要交换,还要做镜像翻转。这个在设备树里可以通过属性配置,也可以在驱动里处理。
RV1126平台常见的做法是在设备树里加:
touchscreen-swapped-x-y; touchscreen-inverted-x; touchscreen-inverted-y;具体加哪几个,取决于屏幕的物理安装方向。我的经验是先把屏幕点亮,然后用evtest工具看触摸事件,根据实际坐标和期望坐标的差异来调整这几个属性。
4. 内核编译与烧录:把改动变成能跑的固件
设备树改完只是第一步,得编译进内核并烧到板子上才算数。
4.1 编译环境的准备
RV1126的SDK一般用Buildroot或Yocto,编译内核的命令大致是:
# 进入SDK目录 cd rv1126_sdk # 选择配置文件 make ARCH=arm rv1126_defconfig # 编译内核和设备树 make ARCH=arm rv1126-evb-ddr3-v13.img -j$(nproc)编译前要确认交叉编译工具链已经装好,环境变量配置正确。我习惯先跑一次全量编译确认环境没问题,再改代码增量编译,这样出问题容易定位。
4.2 设备树编译的单独验证
设备树改动频繁的时候,单独编译设备树能省不少时间:
make ARCH=arm dtbs -j$(nproc)编译产物在arch/arm/boot/dts/目录下,.dtb文件就是最终要烧录的设备树。如果编译报错,多半是语法问题,比如括号不匹配、属性名拼错、引用了不存在的节点。RV1126的设备树文件通常按板级组织,改的时候要确认改的是自己板子对应的那个dts。
4.3 烧录方式的选择
RV1126支持多种烧录方式,常用的有:
| 烧录方式 | 适用场景 | 操作要点 |
|---|---|---|
| USB烧录 | 开发调试阶段 | 按住recovery键上电,用工具识别设备 |
| TF卡烧录 | 批量生产 | 制作启动卡,插入后自动烧录 |
| 网络烧录 | 已联网设备 | 通过adb或专用工具推送 |
开发阶段我一般用USB烧录,改一次烧一次,迭代快。烧录工具用瑞芯微官方的升级工具,选择对应的分区镜像,注意boot分区和dtb要一起更新,只更新一个可能不生效。
4.4 烧录后的验证步骤
烧录完别急着下结论,按这个顺序验证:
- 看串口log:上电后串口有没有打印DSI初始化信息,有没有报错。
- 看背光:背光有没有亮,亮度是否正常。
- 看显示:屏幕有没有画面,有没有花屏、偏移、闪烁。
- 看触摸:用
evtest或getevent看触摸事件有没有上报。 - 看坐标:点击位置和实际响应位置是否一致。
这五步走下来,基本能定位问题出在哪一环。
5. 上电调试:那些规格书不会告诉你的坑
前面都是按部就班,真正花时间的往往是调试阶段的各种意外。
5.1 屏幕不亮的第一轮排查
屏幕完全不亮,按这个顺序查:
- 供电:量屏幕模组的各路供电(VDD、VDDIO、AVDD)是否正常,电压对不对。
- 复位时序:reset-gpios的电平变化是否符合规格书要求,复位时间够不够。
- MIPI信号:用示波器或MIPI分析仪看差分线有没有信号。
- 初始化序列:确认命令有没有发出去,可以用逻辑分析仪抓I2C或DSI总线。
我遇到过一次屏幕不亮,查了半天发现是复位引脚配错了GPIO,设备树里写的和原理图对不上。这种低级错误最容易犯,也最容易被忽略。
5.2 花屏和闪烁的成因
花屏通常是时序参数不对或者lane-rate不匹配。排查思路:
- 先确认display-timings里的参数和规格书一致。
- 再确认lane-rate计算是否正确。
- 如果画面有规律地偏移,多半是porch参数错了。
- 如果画面闪烁,可能是像素时钟不稳定或者背光PWM频率干扰。
有一次花屏查了很久,最后发现是hback-porch少填了一个0,把40写成了4。这种数字错误肉眼很难发现,建议配置完和规格书逐项核对。
5.3 触摸不灵敏或漂移
触摸问题一般分几类:
- 完全无反应:中断配置错、I2C地址错、供电没上。
- 时灵时不灵:中断触发方式不对、I2C速率太高导致通信不稳。
- 坐标漂移:坐标范围配置错、校准参数不对。
- 多点触摸异常:触摸IC固件版本问题或驱动不支持。
我碰到过一次触摸漂移,最后发现是touchscreen-size-x/y填反了,竖屏的坐标范围填成了横屏的。改过来就好了。
5.4 调试工具和手段
几个我常用的调试手段:
- 串口log:最直接,内核启动和驱动加载的信息都在里面。
- evtest:看输入设备事件,验证触摸是否上报。
- i2cdetect:扫描I2C总线,确认触摸IC是否被识别。
- 示波器:看MIPI信号、中断波形、PWM输出。
- 逻辑分析仪:抓I2C通信,分析初始化序列有没有正确发送。
这些工具配合使用,基本能覆盖大部分问题。
6. 一些提高效率的实操经验
最后分享几个我在多次移植中总结出来的经验,能省不少时间。
6.1 建立参数核对表
每次移植前,把规格书里的关键参数整理成一张表,配置完逐项打勾。包括:
- 分辨率、lane数、lane-rate
- 像素时钟、前后沿、同步宽度
- 初始化序列命令数和顺序
- I2C地址、中断引脚、复位引脚
- 背光PWM频率和亮度分级
这张表能避免大部分低级错误,尤其是同时改多个参数的时候。
6.2 分阶段验证,别一次改太多
我的习惯是先只配显示,把屏幕点亮;再配触摸,把点击调通;最后调背光和亮度。一次只改一个环节,出问题容易定位。如果显示和触摸一起改,屏幕不亮的时候你都不知道是显示配置错了还是触摸配置影响了。
6.3 保留可回退的版本
设备树改动前先备份,或者用git管理。改崩了能快速回退,不用从头再来。我一般每改一个能跑通的版本就提交一次,注释写清楚改了什么。
6.4 善用厂商FAE资源
屏幕厂商的FAE手里通常有已经调通的配置,遇到卡壳的问题直接问,比自己啃规格书快得多。但要注意,FAE给的配置是针对他们的参考平台的,移植到RV1126上还是要自己适配。
6.5 记录每次调试的现象
调试过程中把每次改动的现象记下来,比如"改了hback-porch从40到50,花屏消失"。这些记录在后续遇到类似问题时非常有用,也能帮你总结出参数调整的规律。
移植这件事,说到底就是耐心加细心。参数一个个核对,问题一个个排查,没有捷径。但跑通一次之后,再遇到类似的屏幕就快多了,因为套路是相通的。RV1126平台上JD9366这套配置,我前后调了大概三天,其中大部分时间花在初始化序列的格式转换和时序参数的微调上。真正卡住的地方不多,但每个卡点都值得记下来,下次就不会再踩。