这段时间一直在调泰山派的10.1寸MIPI屏在Ubuntu 22.04下从设备树到触摸校准折腾了整整两个晚上屏幕点不亮、触摸没反应、竖屏显示不对这些坑基本上全踩了一遍。网上关于泰山派的资料虽然不少但大多停留在“跑个demo”的层面真正细化到怎么改设备树、怎么让触摸跟着旋转走的教程非常零散。这篇就把自己完整趟过一遍的流程记录下来从硬件接线、MIPI DSI原理、设备树配置到触摸校准和竖屏设置一条线串到底。这篇文章适合手里正好有泰山派开发板、想外接第三方面板的人也适合正在调试RK3566/RK3568平台MIPI显示、对设备树和DRM框架还不太熟的朋友。我尽量把每一步为什么这么做讲清楚附件里能直接照着抄的配置代码也会给全这样哪怕你手里的屏幕型号和我不一样也能根据同样的思路迁移过去。1. 先搞清楚硬件屏幕规格、接口与系统环境确认1.1 泰山派开发板接口速览泰山派用的是瑞芯微RK3566这颗四核A55处理器GPU是Mali-G52视频编解码单元齐全做低成本Linux平板、工业HMI和智能终端都很合适。板上带一个40Pin GPIO排针、千兆网口、USB 3.0还有专门用于显示输出的MIPI DSI接口这在同价位开发板里是比较少见的。我用的这块泰山派是2GB16GB的版本系统是官方发布的Ubuntu 22.04内核版本5.10。这个内核自带比较完整的Rockchip DRM/KMS驱动MIPI DSI控制器部分用的是dw-mipi-dsi-rockchip驱动后面我们主要就是跟这个驱动打交道。接线部分要特别留意10.1寸屏幕一般是4-lane MIPI DSI板子和屏幕之间用的是0.5mm间距的30Pin/40Pin FPC排线。泰山派开发板上的DSI座子位置靠近USB口插的时候注意排线金属触点朝内插到底后把卡扣压紧别插反了。这个环节看起来简单但我第一次就遇到屏幕不亮查半天最后发现是排线一面没压到底接触不良。触摸模块单独走I2C接口。市面上的10.1寸屏组一般把触摸的FPC单独引出来引脚定义是VCC、GND、SCL、SDA、INT、RST这六根。泰山派的官方屏幕扩展接口里已经预留了触摸引脚或者你也可以直接把这六根线接到40Pin排针上。1.2 屏幕参数与系统环境确认我用的屏幕是某宝常见的一款10.1寸IPS屏分辨率1280x800MIPI接口驱动IC是ST7701S触摸IC是汇顶的GT911供电3.3V。买之前一定要向卖家确认两个关键信息第一屏幕的DSI Lane数量4-lane和2-lane的设备树写法完全不同第二驱动IC的型号ST7701S和JD9365虽然都可以用、但初始化命令序列不一样混着用只会白屏。系统环境的确认比较简单先登进Ubuntu看一下内核和DRM模块状态uname -a ls /sys/class/drm/ modetest -c执行modetest -c后如果能看到一个未连接的DSI接口就说明内核DRM框架已经检测到这个接口了只是没有panel的输出数据。如果连DSI接口都看不到问题大概率出在内核编译配置或设备树没有使能DSI控制器。另外建议提前准备好一个能用的HDMI显示器因为屏幕一旦点不亮后面改设备树的迭代全靠串口和SSH会非常痛苦。我自己的做法是SSH登录开发板操作把HDMI显示器作为备用环境这样即使MIPI屏配置崩了也不影响改文件。2. MIPI DSI 与 ST7701S 驱动的核心原理2.1 DSI 协议其实没有想象中复杂MIPI DSIDisplay Serial Interface本质上是一种高速差分串行接口物理层叫D-PHY时钟通道和数据的划分是固定的1对时钟差分线Clock Lane 若干对数据差分线Data Lane。数据lane数越多理论带宽越高。1280x80060fps、24位色深的场景用4-lane是标准做法2-lane虽然也能跑但余量很小。如果你手里有示波器可以在屏亮起来的时候看看MIPI Clock Lane的波形通常是一个稳定的高频差分正弦波频率几百MHz幅度在200mV左右。热搜词里提到的“mipi时钟信号示波器波形”其实很多人是在点不亮屏幕时用示波器排查MIPI是否有输出。这个思路是对的如果MIPI DSI时钟lane根本没有波形那问题多半在内核显示链路比如没绑定panel、controller没启动如果有波形但画面不对问题多半在初始化序列或时序参数。D-PHY另一个特点是没有专门的同步信号线。HSYNC、VSYNC、像素时钟这些信息被打包成包格式在数据lane上按事件模式传输。这也是为什么MIPI屏幕“点不亮”的排查思路和RGB屏幕完全不一样——不是接根线就能出画面的必须让DSI控制器按约定的时序把数据发出去。2.2 ST7701S 屏幕驱动芯片的工作方式ST7701S是一颗比较常用的MIPI DSI转RGB驱动芯片它从主控接收DSI差分信号解析出RGB数据和同步信息再转换成LCD面板需要的TFT时序同时负责产生GIP栅极驱动信号。换句话说主控不直接驱动液晶面板而是通过ST7701S这个“翻译官”间接驱动。ST7701S这类的MIPI驱动IC上电后不会自动进入正常工作状态。它内部有一套寄存器控制扫描方向、信号极性、VCOM、伽马等参数这些寄存器必须通过DSI的DCS命令逐条下发。不同的面板厂家会给你一份初始化RegList寄存器命令序列这几十上百条命令是死人账少了哪条画面就可能是花的、偏色的、或者直接不亮。所以在设备树里经常看到类似下面这样的初始化序列定义每一行都是一个寄存器地址加一个值panel-init-sequence [ 39 00 04 FF 98 81 03 15 00 02 01 00 15 00 02 02 00 15 00 02 03 73 ... ];这里面的39 00代表DCS长包写命令15 00代表短包写命令具体含义在方案商给的手册里都会有。我拿到屏的第一件事就是找卖家要这份初始化序列文档没有这个后面的配置根本无从谈起。2.3 时序参数和像素时钟怎么算时序参数porch、blanking、clock决定屏幕刷新率和稳定性。设备树里常见的是这样一组值display-timings { native-mode timing0; timing0: timing0 { clock-frequency 71100000; hactive 1280; vactive 800; hback-porch 20; hfront-porch 40; hsync-len 30; vback-porch 20; vfront-porch 28; vsync-len 4; hsync-active 0; vsync-active 0; de-active 0; pixelclk-active 0; }; };其中clock-frequency就是像素时钟很多新手第一次配置时喜欢拍脑袋填结果屏幕要么闪烁要么不显示。正确做法是根据下面的公式反推Pixel Clock ≈ (hactive hfront-porch hback-porch hsync-len) × (vactive vfront-porch vback-porch vsync-len) × 刷新率拿上面的参数算横向总长是12804020301370纵向总长是80028204852乘积1370×8521167240再乘60Hz刷新率得到约7003.4万次每秒也就是70MHz。所以我们填7110000071.1MHz就是一个合理的像素时钟。DSI链路的数据速率一般取“像素时钟 × 24bit / Lane数”最终还要留出包头包尾的开销。4-lane情况下一般就是像素时钟×7左右得到约500Mbps每lane这个范围对于D-PHY来说是常规工作区间。时序参数的“活性”active信号极性也容易踩坑。HSYNC、VSYNC的极性如果反了有些屏能显示但画面上下或左右偏移、边缘有黑带有些屏干脆就不同步。我自己习惯是先按屏厂规格书填尤其是hback-porch和hfront-porch不要随便改很多屏对这两个参数非常敏感。3. 设备树 overlay 编写与编译全流程实操3.1 为什么用 overlay 而不是直接改内核 DTSRK3566平台的原厂BSP里播放显示设备树文件一般叫rk3566-evb.dts或rk356x-board.dts里面已经包含了DSI节点、背光节点等框架。按照“最粗暴”的方式我们完全可以改这个dts文件然后重新编译整个内核但在实际开发板方案里我更推荐用overlay设备树插件来做。原因很现实第一overlay可以独立编译、独立加载不会把内核镜像搞乱第二调试阶段需要频繁修改参数直接改dts意味着每次都要重编内核一次编译几分钟到十几分钟overlay只需要编个dtbo几秒钟第三如果配置有问题需要临时切回到HDMI显示排查overlay可以快速卸载而直接改内核就得刷整个镜像。瑞芯微的Ubuntu镜像一般通过extlinux引导里面预留了overlay目录和加载机制。下面这个流程我实际操作下来是稳定可行的以RK3566 kernel 5.10为例。3.2 手写一个屏幕 overlay 的完整 DTS先建一个文件10.1-mipi-panel.dts内容我直接按我调试通过的写法给出来。注意把里面所有GPIO编号、I2C总线编号替换成你板子实际使用的。/dts-v1/; /plugin/; / { fragment0 { target-path /; __overlay__ { backlight: backlight { compatible pwm-backlight; pwms pwm4 0 25000 0; brightness-levels 0 40 80 120 160 200 240 255; default-brightness-level 5; power-supply vcc_sys; enable-gpios gpio4 RK_PA2 GPIO_ACTIVE_HIGH; status okay; }; panel: panel { compatible sitronix,st7701s; status okay; backlight backlight; reset-gpios gpio4 RK_PA3 GPIO_ACTIVE_LOW; enable-gpios gpio4 RK_PA4 GPIO_ACTIVE_HIGH; pinctrl-names default; pinctrl-0 lcd_panel_reset; }; }; }; fragment1 { target-path /display-subsystem; __overlay__ { route_dsi0: route-dsi0 { status okay; connect dsi0_in_vp0; logo,rotate 0; }; }; }; fragment2 { target dsi0_in_vp0; __overlay__ { status okay; }; }; fragment3 { target mipi_dsi0; __overlay__ { status okay; panel0 { compatible sitronix,st7701s; reg 0; status okay; backlight backlight; reset-gpios gpio4 RK_PA3 GPIO_ACTIVE_LOW; pinctrl-names default; pinctrl-0 lcd_panel_reset; ports { #address-cells 1; #size-cells 0; port0 { reg 0; panel_in_dsi0: endpoint { remote-endpoint dsi0_out_panel; }; }; }; display-timings { native-mode timing0; timing0: timing0 { clock-frequency 71100000; hactive 1280; vactive 800; hback-porch 20; hfront-porch 40; hsync-len 30; vback-porch 20; vfront-porch 28; vsync-len 4; hsync-active 0; vsync-active 0; de-active 0; pixelclk-active 0; }; }; panel-init-sequence [ /* 这里填屏厂给你的初始化命令序列 */ ]; }; }; }; fragment4 { target mipi_dsi0_out; __overlay__ { remote-endpoint panel_in_dsi0; }; }; };这段overlay里有几个关键点我单独说下。panel-init-sequence是整个面板能否正常显示的核心不同型号屏幕差别很大。你买的30块钱和100块钱的10.1寸屏虽然都写着“ST7701S”但初始化序列可能不一样这里必须用屏厂提供的、对应你这款玻璃和模组的版本。reset-gpios用的是GPIO4_A3这个GPIO在泰山派的官方40Pin定义里能查到但不同版本PCB可能不一样。我之前在一台板子上发现reset接错GPIO屏幕反复上电、初始化看起来像是花屏实际上去查GPIO复用状态才发现好几个引脚被Pinmux占用。3.3 编译与加载 overlay 的具体命令写好dts之后用设备树编译器把它编成dtbo文件dtc -I dts -O dtb - -o 10.1-mipi-panel.dtbo 10.1-mipi-panel.dts参数-一定要加它会在编译时生成overlay动态加载需要的symbol表不加这个符号信息后面加载时会报找不到目标节点。如果系统里没装dtc先执行sudo apt install device-tree-compiler。编译完成后把dtbo放到引导加载目录。泰山派的Ubuntu一般使用extlinux机制目录通常是/boot/extlinux/也有可能是/boot/dtbs/rockchip/overlay/。以extlinux为例sudo mkdir -p /boot/extlinux/overlays sudo cp 10.1-mipi-panel.dtbo /boot/extlinux/overlays/然后修改/boot/extlinux/extlinux.conf在append那行末尾增加overlay加载参数。不同发行版写法有差异瑞芯微的BootloaderU-Boot支持通过fdtoverlays指定fdtoverlays /boot/extlinux/overlays/10.1-mipi-panel.dtbo如果你用的不是U-Boot的extlinux方案还可以走configfs动态加载效果一样适合调试sudo mkdir -p /configfs/device-tree/overlays/10inch sudo sh -c cat /boot/extlinux/overlays/10.1-mipi-panel.dtbo /configfs/device-tree/overlays/10inch/dtbo加载成功后可以用dmesg | tail -n 30看看有没有报错检查一下/sys/class/backlight/下是否出现了backlight目录。4. 屏幕点亮之后的显示层校验4.1 从 dmesg 和 DRM 状态确认连接嵌入式开发里最常见的问题就是“屏幕没反应”但没说清楚是背光没亮、信号没出来、还是时序不对。良好的排查顺序是从内核日志开始。配置文件重启之后先用dmesg | grep -i dsi和dmesg | grep -i panel过滤关键日志正常会看到panel驱动的probe调用成功、DSI控制器初始化完成。如果只看到DSI控制器初始化没有看到panel问题在设备树overlay没生效或者compatible与驱动匹配不上。DRM层面可以用modetest验证。如果没有安装modetestUbuntu下先sudo apt install libdrm-tests。然后执行modetest -M rockchip -c正常输出里会有一个名为DSI-1的connectormode列表里包含1280x80060。如果能看到这个mode说明从DRM到panel整条链路已经通了一半接下来就是显示画面的问题。为什么要用DRM这个视角因为在现代Linux显示架构里屏幕最终怎么呈现是由DRM/KMS框架统一管理的设备树只是告诉内核“有这个面板、参数是什么”。你能在modetest里看到DSI-1就说明DRM已经把panel当成一个真正的显示器管理起来了。4.2 背光调节与内核帧缓冲验证点亮屏幕的过程中背光和画面是两个独立的事情。有时候背光不亮不代表LCD没有工作你可以用强光手电筒贴着屏幕照如果能看到细微的画面内容说明LCD已经在显示了只是背光没起来。背光验证很简单ls /sys/class/backlight/正常情况下会看到一个backlight目录然后设置亮度echo 100 /sys/class/backlight/backlight/brightness如果写了没反应去查背光GPIO使能引脚。我遇到过的情况是PWM背光通道内核虽然注册了但GPIO使能脚被其他设备占用导致背光始终为0。内核帧缓冲层面可以做最基础的画面测试cat /sys/class/graphics/fb0/virtual_size如果显示1280,800说明framebuffer分辨率是对的。然后可以往fb0直接写一个纯色测试图dd if/dev/urandom of/dev/fb0 bs1024 count100这个操作会在屏幕上刷出随机噪点只要画面有动静就说明数据链路是通的。如果屏上没有反应再怀疑是不是GPIO、时序或者初始化序列的问题。4.3 X11 或 Wayland 桌面如何接管显示泰山派的Ubuntu桌面默认会通过weston或Xorg接管DRM显示。开发板场景下常装的是轻量级桌面如果屏幕能被modetest识别桌面一般也能正常输出。但很多同学遇到的情况是“启动后HDMI有桌面MIPI黑屏”。这个问题的根源在于桌面合成器默认选择了DP或HDMI作为主输出MIPI屏幕没有拿到合成缓冲区。解决方法取决于你用的显示服务。如果你用的是Xorg比如ubuntu-desktop默认是GNOMEXorg可以通过xrandr直接设置输出xrandr --listmonitors xrandr --output DSI-1 --mode 1280x800 --right-of HDMI-A-1这样把DSI-1放在HDMI旁边桌面就会扩展或镜像过去。实测下来泰山派在Xorg下对MIPI屏幕的支持还是不错的不需要额外装驱动。5. 触摸校准与竖屏联动设置5.1 确认GT911触摸节点与设备树配置GT911是汇顶很常见的一颗电容触摸控制IC支持5点或10点触控通过I2C接口和主控通信。泰山派Ubuntu系统一般已经内置了goodix触摸驱动但前提是设备树里要正确声明GT911所在的I2C总线、中断GPIO和复位GPIO。我实际使用的触摸节点如下注意address是0x5d。GT911的I2C地址有两种0x5d和0x14由触摸屏模组上的电阻决定用i2cdetect -y 5扫描后确认设备在哪里。这里-y 5是I2C总线编号泰山派上GT911通常挂在I2C5但不同屏幕转接板不一样一定先扫出来再填。设备树中GT911节点的通用写法i2c5 { status okay; gt911: gt9115d { compatible goodix,gt911; reg 0x5d; interrupt-parent gpio4; interrupts RK_PA5 IRQ_TYPE_EDGE_FALLING; reset-gpios gpio4 RK_PA6 GPIO_ACTIVE_HIGH; irq-gpios gpio4 RK_PA5 GPIO_ACTIVE_HIGH; touchscreen-swapped-x-y; touchscreen-inverted-x; touchscreen-inverted-y; status okay; }; };真假参考一下就好。实际修改设备树的时候只保留与屏幕实物方向一致的旋转属性。GT911的驱动在调试时经常碰到一个坑IRQ引脚配的是level触发还是edge触发很多人配错成IRQ_TYPE_LEVEL_LOW结果内核在IRQ handler里一直循环跑CPU占用率飙到100%触摸还一点反应都没有。用IRQ_TYPE_EDGE_FALLING配合官方的irq-gpios属性是比较稳的组合。驱动生效后用dmesg | grep -i goodix能看到“Goodix GT911 detected”之类的打印。如果一直没有检查I2C总线和中断GPIO是否被Pinmux复用。5.2 触摸设备与校准矩阵GT911驱动加载后会生成一个输入事件设备一般是/dev/input/event2。在Xorg环境下这个设备会通过libinput自动识别为触摸屏。但麻烦的是10.1寸屏幕在做竖屏显示时只有画面旋转了触摸坐标并不会自动跟着旋转必须手动设置旋转矩阵。先看一下当前的触摸设备名字xinput list输出中会有一行类似Goodix Capacitive TouchScreen的设备记下它的id。然后使用xinput设置坐标变换矩阵xinput set-prop id libinput Calibration Matrix 0 -1 1 1 0 0 0 0 1这个矩阵对应的是屏幕顺时针旋转90度的坐标映射。矩阵的数学原理是触摸屏输出原始坐标(u,v)经过3x3矩阵变换后映射到显示坐标(u,v)。旋转90度时坐标的关系是u 1 - v v u对应成矩阵就是上面写的那组数值。如果是逆时针90度矩阵变成u v v 1 - u即0 1 0 -1 0 1 0 0 1。如果你同时需要镜像矩阵里的负号和偏移值还要继续调整。我实际遇到的情况是旋转之后触摸点只在一个方向是反的那就是只有inverted-x或inverted-y需要设置不要把四个属性全部加上去否则点起来非常混乱。为了每次开机都生效新建一个自启动脚本/usr/local/bin/touchscreen-fix.sh把xinput设置矩阵的语句写进去然后在桌面环境的自启动目录里加一个.desktop文件或在rc.local里调用。5.3 竖屏显示的完整设置流程竖屏设置分三个层面。第一层是内核帧缓冲层第二层是桌面合成器层第三层是应用层。做平板或竖屏HMI时一般三个层面都要改。内核层面修改/boot/extlinux/extlinux.conf中内核命令行加上fbconrotate:1这样连终端和framebuffer控制台都会在MIPI屏上竖着显示纯命令行场景下这一步就够了。桌面层面如果你用Xorg在Xorg配置或会话启动时执行xrandr --output DSI-1 --rotate left桌面环境如果是Wayland/westonweston.ini里设置[output] nameDSI-1 transform90应用层面如果你跑的是Qt应用可以在启动时设置环境变量QT_QPA_PLATFORMlinuxfb:fb/dev/fb0:transformrot90或者Qt的-platform eglfs:rotation90。如果是GTK应用一般跟随桌面环境旋转即可。这里给一个我自己在用的组合示例适合“竖屏终端交互界面”的场景xrandr --output DSI-1 --rotate left xinput set-prop Goodix Capacitive TouchScreen libinput Calibration Matrix 0 -1 1 1 0 0 0 0 1执行完这两条后屏幕显示是竖着的触摸也保证“点哪里就出现在哪里”。6. 常见问题排查实录与避坑速查表6.1 屏幕点不亮先按这个顺序排查如果套用上面的配置后屏幕还是不亮我的排查顺序是固定的先验证内核DRM链路是否通再看背光最后查设备树细节。第一步先看modetest能不能识别DSI-1识别不了就往下查内核日志。第二步看背光如果背光能亮但无画面大概率是时序或初始化序列问题把里边的hback-porch之类的参数对照规格书挨个核对。第三步看Reset GPIO和电源使能GPIO是否真的输出正确的电平很多点不亮的原因就是Reset时序不对比如复位信号拉低时间不够屏幕芯片没有正常上电复位。还有一个容易被忽略的点DSI的lane-pols极性。设备树里有一组属性lane-polarities它规定了5对差分线里每一对的正反极性。如果你的FPC排线设计时把某对数据线接反了在这里指定极性就能绕过去不用改硬件。不过绝大多数官方板卡的设备树里已经根据原理图配好了自己画板时才需要关注。6.2 常见问题速查表下面这个表格是我实际踩过、以及社群朋友经常问到的典型问题的汇总可以直接作为排障清单现象常见原因排查/解决方向背光不亮PWM通道没注册、背光使能GPIO被占用查/sys/class/backlight/是否有设备检查GPIO复用背光亮但全白屏DSI没有检测到panel电平配置有问题查dmesg中的panel probe检查compatible和复位时序花屏/显示错位时序参数porch、clock不对对照屏厂规格书逐项核对timing参数只亮一下马上黑屏电源或复位GPIO冲突屏幕被强制复位检查使能GPIO是否同时被其他驱动占用有画面但特别暗背光PWM占空比默认太低修改brightness-levels表或default-brightness-level触摸完全没反应GT911的I2C设备没注册i2cdetect确认设备地址检查中断GPIO配置触摸坐标错乱旋转之后没有设置对应旋转矩阵按实际旋转方向设置libinput Calibration Matrixxrandr看不到DSI-1Xorg没有正确初始化DRM尝试用weston或直接跑modetest确认DRM链路画面能显示但闪屏像素时钟余量不足适当降低刷新率或调整blanking参数6.3 关于“分辨率/刷新率改来都不对”的一点心得调屏过程中最容易让人崩溃的是参数改了无数遍屏幕始终不是我们要的效果。我后来总结出一个经验不要一次性改多个参数。很多初学者拷贝了一份“网上别人的参数”后在完全不了解含义的情况下把clock、porch、初始化序列三样东西一起改了最后出问题根本不知道是哪一项导致的。正确做法是每次只改一个变量观察效果再改下一个。比如先用同一组时序参数分别测试厂商提供的初始化序列和自己的睡眠初始化序列初始化序列确认没错了再微调时序参数。这个过程虽然慢但每一次改动都能给你明确反馈实际上反而是最省时间的方式。另外提一句在买屏和找方案商的时候尽量要一份“该型号屏幕在Linux下的调试记录”或者至少一份寄存器配置表。不是每个卖家都会给但给的话能让整个调试过程从“盲人摸象”变成“照葫芦画瓢”。我自己在泰山派上最终稳定运行的组合里面其实还有一个小细节是网上很少提到的默认驱动加载的是RGB888格式但部分ST7701S的模组配置成了RGB666这种情况下画面虽然能出来但颜色会明显偏淡、像蒙了一层灰。解决方法是检查panel驱动里有没有输出格式的配置确认与你屏幕模组的接口一致。最后再分享一个小习惯所有调通的配置我都会保存一份到git仓库包括overlay dts、触摸校准脚本、自启动配置然后给每个文件写好注释注明是哪块屏幕、哪块板子、哪个内核版本。嵌入式显示调试本身就是很吃经验和细节的工作有了这些记录下次换屏、换板子或者升级内核的时候都能快速定位问题少走不少弯路。