资讯动态

RK3588 Android 12 屏幕方向、分辨率与密度适配指南

发布时间:2026/9/30 5:10:04 来源:尧图企业网站定制
1. 先理清概念RK3588 上一块屏的参数其实分成三套RK3588 这块芯片我从 Android 11 一路折腾到 Android 12显示相关的坑大概能写满两个笔记本。很多人拿到板子,改屏幕方向、分辨率和密度这三个需求,第一反应是去 build.prop 里改属性,改完发现要么不生效,要么开机黑屏,要么 App 布局全乱。原因很简单:在 RK3588 Android 12 这套组合上,方向、分辨率、密度这三个东西各自跨越了完全不同的软件层次,你把参数写在错误的层次里,系统要么读不到,要么读到之后又被上一层覆盖掉。RK3588 的显示子系统叫 VOP2,内部有四个 Video Port(VP0 到 VP3),VP0 和 VP1 能扛 8K 输出,VP2 和 VP3 各扛 4K。芯片对外的物理接口有 HDMI0、HDMI1、DP0、DP1、eDP0、eDP1 以及两组 MIPI DSI。四个 VP 到这些接口的映射关系不是写死的,而是在设备树里通过vp0_out_hdmi0这类节点和route_hdmi0路由节点来绑定。这意味着你改屏幕参数的时候,先要搞清楚你手上的屏接在哪一路、走的是哪个 VP,不然改了半天都在改一条根本不是当前输出通道的链路。Android12 上 Rockchip 用的是标准 DRM/KMS 加自家 HWC2 实现,最终显示出来的东西要经过内核 DRM 驱动、HWC、SurfaceFlinger、WindowManager 四层。分辨率主要在前两层定死,方向可以在前三层里任意一层处理,密度则基本由 Android 框架层和后两层决定。这三者的处理位置不同,导致它们之间会互相影响:比如你把分辨率临时改小了但没有同步调密度,App 拿到的 dp 宽度就会变小,界面直接挤成一团;又比如你在内核层旋转了 90 度但没告诉框架层,触摸坐标就会和画面对不上。提示:改任何一个显示参数之前,先把当前状态完整记录下来。adb shell wm size、adb shell wm density、adb shell dumpsys display | head -80、getprop | grep -iE rotation|lcd_density|hdmi|fb_这四条命令跑一遍,把结果存成文本。后面出问题的时候,这份快照就是你唯一的还原依据。1.1 从 DTS 到 App 窗口的一条完整链路我们按数据流的方向把这条链路捋一遍,后面每一节都在这个框架里定位。最底层是屏幕模组本身,它的物理像素是固定的,比如 1920x1080。这块屏通过 MIPI DSI 或者 HDMI 接到 RK3588。如果是 MIPI 屏,屏幕初始化序列和时序参数写在设备树的panel节点里;如果是 HDMI,时序是显示器通过 EDID 上报的,内核读取 EDID 后生成一组可用模式。这一层决定了物理上能出多大的画面。再往上是 U-Boot 和内核的 logo 显示。RK3588 的开机 logo 通常是 U-Boot 阶段就点亮的,这一层的分辨率由 U-Boot 的显示驱动和 DTS 里的 logo 节点共同决定。很多人遇到开机 logo 花屏、拉伸、只在屏幕左上角显示一小块,问题就出在这里:U-Boot 用的时序和内核 panel 时序不一致。然后是 Android 的 SurfaceFlinger。它通过 HWC 拿到底层支持的分辨率列表,挑选一个作为主显示的逻辑分辨率。SurfaceFlinger 内部会维护一个mInternalDisplayOrientation的概念,这个值决定了整个合成器是不是要旋转。如果这里设成 90 度,后面所有 App 拿到的坐标系都会被旋转。最上层是 WindowManager 和 App 看到的 DisplayMetrics,包含widthPixels、heightPixels、densityDpi、density、scaledDensity。你在 App 里写的 dp 值最终要乘以density densityDpi / 160换算成像素。密度设小了,同样一个 100dp 的按钮在物理屏幕上就变小;设大了,界面上所有东西都变大,内容显示不全。理解这条链路之后,你会发现分辨率这个词在不同层次含义完全不同:物理分辨率、framebuffer 分辨率、逻辑分辨率、App 可用窗口大小。你改的是哪个,决定了谁来响应你的修改。1.2 三个参数各自的边界和耦合点方向、分辨率、密度在链路里各有自己的责任层,但它们之间有明确的耦合点,这是实际调试中最容易翻车的地方。方向的核心耦合点在触摸。你在显示侧把画面转了 90 度,但触摸驱动上报的坐标还是原始坐标,如果不做对应变换,用户点左边屏幕右边响应。这个变换要么在内核的触摸驱动里做,要么在 Android 的 InputReader 里做,要么在 HWC 层统一处理。Rockchip 的 SDK 里通常会有配套的旋转配置项,但如果只改了显示没改触摸,就会出现显示正常但触摸错位的经典问题。分辨率的耦合点在开机 logo 和密度。U-Boot logo 用的分辨率如果和内核 panel 时序不一致,会出现 logo 只显示一部分;Android 逻辑分辨率如果和物理分辨率不一致,系统会进入兼容模式,把画面整体缩放,文字发虚。密度则是和分辨率绑在一起的:物理分辨率变了但密度没变,App 的 dp 宽度就变了,布局比例全乱。反过来说,如果你只是想让 App 看起来更大一点而不改物理分辨率,那应该改的只有密度。密度的耦合点主要在资源加载和兼容模式。Android 会根据 densityDpi 选择drawable-hdpi、drawable-xhdpi、drawable-xxhdpi里的图片资源,密度档位一变,加载的图可能就换了一套。如果某个密度档位对应的资源缺失,系统会做缩放,显示效果下降。更麻烦的是 Google 的兼容模式:当wm size强制改了一个非物理分辨率,WindowManager 会给 App 加一个CompatibilityInfo,把 App 按比例缩放绘制,这时候 App 内部的getWidth()拿到的是原始宽度,但实际绘制被缩放了,做像素级操作的功能(截图、图像处理、Unity 渲染)就会出偏差。所以三个参数是一个整体,改之前先想清楚目标是什么:是硬件就是竖屏所以要把系统转过来,还是硬件横屏但某个 App 要求竖屏显示,还是只是想让界面元素大一点。目标不同,应该动的层完全不同。2. 屏幕方向:三条路径和各自的代价2.1 最省事的做法是从 U-Boot 和硬件走线就把方向定死如果你的产品就是一台固定竖屏的设备,比如竖屏广告机、竖屏收银机,最省心的方案是在硬件走线和 U-Boot 层面就把方向解决掉,而不是在 Android 层旋转。具体做法是:屏幕模组的排线按竖屏 1080x1920 的方式接,panel 的display-timings直接写成hactive 1080、vactive 1920,也就是让系统认为这块屏天生就是竖屏。这样一来,整条软件链路从上到下都是竖屏的,触摸驱动也按竖屏坐标标定,Android 启动后拿到的就是竖屏 DisplayMetrics,不需要任何旋转代码。缺点是这样一来,如果同一个主板要兼做横屏产品,就得改 DTS 重新编内核,没法通过改配置切换。我在一个竖屏点餐机的项目里就是这么干的。最开始图省事在 Android 层用属性旋转,结果发现每次开机动画会先横着显示一下再转过来,肉眼可见地闪一下,后来改成 U-Boot 和 panel 时序直接竖屏,这个问题彻底消失。开机动画的方向问题是很典型的框架层旋转副作用:U-Boot 和 bootanimation 阶段 SurfaceFlinger 的旋转还没生效,所以画面方向是原始的。2.2 内核 DTS 层的旋转方案如果硬件布线没法改,但你又希望方向在系统启动早期就生效,那就考虑在内核显示驱动层做旋转。RK3588 的 VOP2 硬件支持输出旋转,但支持的旋转角度和具体 VP 有关,不是所有 VP 都能任意旋转。在一些 Rockchip 的板级 DTS 里可以看到 panel 节点下带有旋转相关的配置,比如在route节点或者 panel 节点上标注旋转角度。需要说明的是,不同 SDK 分支里这个配置项的命名和位置差别比较大,有的版本在 panel 节点的属性里,有的版本需要改驱动代码。所以动手之前先做一件事:在你手上的 SDK 里全局搜一遍。grep -rn rotation kernel/arch/arm64/boot/dts/rockchip/rk3588*.dts* | head -30 grep -rn rotation kernel/drivers/gpu/drm/rockchip/ | head -30搜出来之后对照看,能很快判断你这条分支到底支持哪种旋转方式。如果内核里根本没有对应的处理逻辑,那就别在这层纠结了,直接往下走 Android 层。内核层旋转的代价是:所有屏幕,包括开机 logo,都会跟着转,一致性最好;但灵活性差,改动需要重新编译内核和 DTB。另外还有一个容易忽略的点:内核旋转之后,/sys/class/drm下暴露出来的模式尺寸可能也会跟着变,如果你有脚本依赖这个路径读分辨率,记得同步改。2.3 Android 层旋转:两个属性,一个要验证Android12 上做显示旋转,主要有两个属性入口需要关注,具体哪一个生效取决于你的 SDK 分支。第一个是ro.sf.primary_display_orientation,这是 AOSP 里 SurfaceFlinger 在 Android 12 引入的属性,取值形如ORIENTATION_0、ORIENTATION_90、ORIENTATION_180、ORIENTATION_270。SurfaceFlinger 启动时会读取它,决定内部主显示的方向。这个属性的好处是它作用在合成器层,比较正统,但它的存在与否和具体版本以及厂商合入情况有关,不能想当然认为一定可用。第二个是ro.sf.hwrotation,这是更早版本流传下来的做法,Rockchip 的不少分支仍然保留了相关的读取逻辑,在 HWC 层做处理。很多 RK 平台的移植文档里写的就是这个属性。我的建议是,先在你的源码树里搜一下这两个字符串,确认哪个真正被读取:grep -rn primary_display_orientation frameworks/native/services/surfaceflinger/ | head grep -rn hwrotation frameworks/native/ hardware/rockchip/ | head属性是在device/rockchip/下的产品 mk 文件里设置的,通常是PRODUCT_PROPERTY_OVERRIDES。比如:PRODUCT_PROPERTY_OVERRIDES \ ro.sf.primary_display_orientationORIENTATION_90 \ ro.sf.hwrotation90改完重新编译 system 和 vendor 镜像刷进去。这里有个实战经验:不要两个属性都设成不同的值,会打架。先只设一个,验证效果,不行再换另一个。我遇到过两个属性同时存在、彼此覆盖导致方向时好时坏的情况,排查了大半天。注意:如果只在 Android 层旋转,触摸坐标大概率需要同步处理。开机后先用一个全屏的测试 App 或者开发者选项里的指针位置功能验证触摸是否对齐,再往下做别的。2.4 三种方案怎么选方案层次改动位置生效时间触摸是否需额外处理适用场景硬件/DTS 时序panel 的 display-timingsU-Boot 阶段起一般不需要,坐标天然对应产品形态固定,单一方向内核驱动旋转驱动代码或 DTS 属性内核加载后通常需要同步配置需要在早期就统一方向Android 属性旋转产品 mk 文件里的属性SurfaceFlinger 启动后基本都需要验证同一固件要兼容多种方向从我的实际经验看,固定形态的产品一律选第一种,最干净;需要在同一固件里通过配置切换方向的,选第三种;第二种属于能用但一般不推荐,因为它把方向逻辑藏在了驱动里,后面接手的人不容易发现。3. 分辨率:从 panel timings 到 wm size 的完整链路3.1 DTS 里 display-timings 的每个字段怎么算MIPI 屏的分辨率是在设备树里写死的,这一节把参数算法讲透,后面再看 HDMI 的动态分辨率就很容易理解了。一个典型的 RK3588 MIPI DSI panel 节点大概长这样:dsi0 { status okay; dsi0_panel: panel0 { compatible simple-panel-dsi; reg 0; backlight backlight; reset-delay-ms 60; enable-delay-ms 60; prepare-delay-ms 60; unprepare-delay-ms 60; disable-delay-ms 60; dsi,flags (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET); dsi,format MIPI_DSI_FMT_RGB888; dsi,lanes 4; panel-init-sequence [ 05 78 01 11 05 32 01 29 ]; display-timings { native-mode timing0; timing0: timing0 { clock-frequency 120000000; hactive 1920; vactive 1080; hfront-porch 60; hsync-len 24; hback-porch 52; vfront-porch 16; vsync-len 8; vback-porch 12; hsync-active 0; vsync-active 0; de-active 0; pixelclk-active 0; }; }; }; };这里最关键的两个字段是hactive和vactive,它们就是有效分辨率,也就是你最终在屏幕上看到的 1920x1080 或 1080x1920。其余的前后肩和同步长度决定了消隐区,必须严格按屏厂给的规格书写,写错了轻则画面偏移、重则点不亮。clock-frequency是像素时钟,可以按如下方式估算:把一整个扫描行需要的总像素数乘上一帧的总行数,再乘刷新率。以这个例子为例,一行的总像素是 1920 60 24 52 2056,一帧的总行数是 1080 16 8 12 1116,如果按 60Hz 刷新,需要 2056 × 1116 × 60 ≈ 137.7 MHz。例子里的 120000000 说明这个屏是按 52Hz 左右的刷新率配的,实际以屏厂规格为准。反过来说,如果你手里只有一个目标分辨率和刷新率,也可以这样反推像素时钟,再去看 MIPI 的 lane rate 能不能撑住。MIPI 的带宽要和分辨率和刷新率对得上。以 1920x1080、60Hz、RGB888(24bit)、4 lane 为例,理论像素率是 1920 × 1080 × 60 ≈ 124.4 Mpps,考虑消隐按 1.25 倍算大约 155 Mpps,乘 24bit 得到约 3.73 Gbps,分到 4 lane 上每 lane 约 0.93 Gbps,所以常见的配置里 lane rate 会给到 1000 Mbps 左右。这个数值如果配小了,现象是画面有横向抖动或者直接不亮;配大了浪费功耗,但对稳定性影响不大。实操心得:MIPI 屏调试先只改hactive和vactive,其它时序参数一律照抄屏厂规格书,不要凭感觉改。等画面稳定了,再考虑优化刷新率或者像素时钟。我见过有人为了提高刷新率随手把 front porch 改小,结果画面周期性闪烁,排查了一下午才回到规格书。3.2 U-Boot logo 和开机动画的分辨率对齐分辨率这块最容易被忽略的是开机阶段。很多人在内核和 Android 都改好了,唯独开机 logo 和开机动画还是老样子:logo 只占屏幕左上角一小块,或者被拉伸得比例失真。U-Boot 阶段的 logo 分辨率取决于 U-Boot 显示驱动初始化时使用的模式。对 MIPI 屏来说,它跟着 panel 时序走,一般不会出问题;对 HDMI 和 DP 来说,U-Boot 会挑选一个支持的显示模式,这个模式可能和内核、Android 选的不一样,于是就出现了从 U-Boot 到内核的那一下分辨率跳变。解决办法是让 U-Boot 的内核命令行和内核保持一致,典型做法是通过 bootargs 里的video参数固定模式,比如videoHDMI-A-1:1920x108060,这样 U-Boot 和内核拿到的模式就是同一个。开机动画的问题更常见。bootanimation.zip里的desc.txt第一行写着动画的宽度、高度和帧率,比如1080 1920 30。如果这行和屏幕实际方向不匹配,动画就会被系统拉伸到全屏,看起来变形。竖屏设备做开机动画,desc 头一行一定要写成竖屏的宽高。这个文件在device/rockchip/common/或者具体产品目录下,找到之后改掉重新打包即可。另外还有一层:如果 Android 逻辑分辨率和物理分辨率不一致,系统会进入兼容模式,这时候开机动画也可能受影响。所以我的做法是,开机动画的分辨率永远和物理分辨率严格一致,不做任何缩放。3.3 Android 侧分辨率的运行时调整adb 里有两个命令能立刻改分辨率和密度:wm size和wm density。这两个命令在调试阶段非常好用,但用在量产固件上要非常小心。# 查看当前 adb shell wm size adb shell wm density # 临时修改 adb shell wm size 1920x1080 adb shell wm density 240 # 恢复默认 adb shell wm size reset adb shell wm density reset这两个命令底层写的是 SettingsProvider 里的全局配置项:wm size写的是display_size_forced,wm density写的是display_density_forced。因为是持久化配置,重启后依然生效,所以调试完一定要 reset,不然会误以为改了代码没生效。想直接操作底层配置也可以:adb shell settings put global display_size_forced 1920,1080 adb shell settings put global display_density_forced 240 adb shell settings delete global display_size_forced adb shell settings delete global display_density_forcedwm size最大的坑是兼容模式。当你设置的分辨率和物理分辨率不一致时,WindowManager 会给所有 App 加上缩放,App 认为自己是 1920x1080,实际绘制结果被缩放到物理屏上。这个机制对普通列表类 App 还算友好,但对做像素级操作的功能就是灾难:截图尺寸不对、Camera 预览比例失真、图像处理结果偏移、Unity 的渲染视口和实际不符。我在一个 RK3588 的机器视觉项目上吃过这个亏,应用层拿摄像头做标定,结果因为之前同事留了个wm size 1280x720没清掉,标定数据全部作废。如果要让某个分辨率在量产固件里默认生效,正确做法是改产品配置文件,而不是用wm size。常见的做法是在device/rockchip/下对应产品的 mk 文件里设置密度和通过 cmdline 传分辨率,具体变量名各版本有差异,建议直接搜:grep -rn lcd_density device/rockchip/ | head -20 grep -rn TARGET_SCREEN device/rockchip/ | head -20搜出来的位置就是你该下手的地方。老一些的 RK 平台会在内核 cmdline 里用androidboot.fb_width和androidboot.fb_height指定 framebuffer 尺寸,RK3588 的 Android12 分支上这条路径不一定还走,别盲目照抄老文档。判断方法很简单:设上之后看dumpsys display里的尺寸有没有跟着变,没变说明这条路走不通。3.4 多屏异显时分辨率互相打架怎么办RK3588 能同时带好几路输出,多屏场景下最容易出现的问题是:你想让 HDMI 跑 4K、MIPI 屏跑 1080p,但实际两组输出被强行设成了同一个分辨率。原因通常有两个。一是 DTS 里的 VP 路由没配对,两路输出被映射到了同一个 VP 上,自然只能共用一个模式。RK3588 的四个 VP 各自独立,理论上可以分别驱动不同接口,但路由关系必须在 DTS 里明确写出来,类似vp0_out_hdmi0、vp1_out_dsi0这样的绑定,再加上对应的 route 节点使能。如果漏配或者配重了,系统就会退化到共用模式。二是 Android 侧的默认显示配置。有些产品配置里会把主显示的分辨率写死在属性中,多屏时其它显示会继承这个值。验证的方法是看每个显示设备上报的模式列表:adb shell dumpsys display | grep -A 20 DisplayDeviceInfo adb shell cat /sys/class/drm/card0-HDMI-A-1/modes adb shell cat /sys/class/drm/card0-HDMI-A-1/status如果/sys下的模式列表本身就不对,那说明问题在内核或 DTS;如果内核给对了但 Android 选错了,问题就在 HWC 或者默认配置。另外 HDMI 的status如果是disconnected,说明线缆或者 EDID 读取有问题,这时候列出的模式可能只是兜底模式,别拿它做判断依据。4. 密度:densityDpi 的算法、改法和副作用4.1 密度公式和档位选择Android 的密度用densityDpi表示,基准是 160,换算关系是px dp × densityDpi / 160。也就是说,densityDpi越大,同一个 dp 值占的物理像素越多,界面看起来越大。对于一块具体屏幕,合理的densityDpi计算方法很简单:先算屏幕对角线的像素数,再除以对角线的物理英寸数,得到的就是这块屏的每英寸像素数,拿它作为densityDpi是最贴近真实尺寸的。我自己算几个常见的例子:屏幕尺寸分辨率对角线像素计算值常用取值5.5 英寸1920x1080约 2202.9约 400.5400 或 4207 英寸1024x600约 1186.8约 169.51608 英寸1280x800约 1509.4约 188.7190 或 20010.1 英寸1920x1200约 2264.2约 224.2220 或 24015.6 英寸1920x1080约 2202.9约 141.2140 或 160计算值只是一个起点,实际取值还要考虑两个因素。一是资源匹配,如果你的 App 里只放了drawable-xhdpi(320)的图,结果把密度设成 400,系统要么拉伸图要么去找drawable-xxhdpi(480)缺失后缩放,画质会下降。二是行业惯例,工业设备上常见的取整习惯是按 160 的倍数走,比如 160、240、320,这样能对上 AOSP 里预置的资源目录。不过我要说一句反直觉的话:对固定设备的产品,不必死抠物理正确的密度。如果产品定义是界面元素看起来大一点,方便戴手套操作,那把密度从计算值往上调一档是完全合理的,只要 App 自己的布局能扛住。我做过一个 10.1 寸的工业面板,参考值是 224,最后定的是 240,原因是 240 能让操作按钮的触摸区域更大,用户体验反而更好。所以算出来的值只是参考,最终以产品需求为准。4.2 三处修改位置的取舍密度的修改位置有三个,效果和风险各不相同。第一个是产品配置里的属性,也就是通常所谓的ro.sf.lcd_density。这是编译期写死的,随固件出厂,不能被用户或调试命令覆盖掉(除非用wm density强制)。位置在device/rockchip/对应产品的 mk 文件里,常见写法是:PRODUCT_PROPERTY_OVERRIDES \ ro.sf.lcd_density240改完要重新编译刷机。这是量产固件的正确做法,推荐指数最高。第二个是wm density,运行时生效,写进 SettingsProvider,重启后保留。适合调试和临时验证,不适合量产。它最大的问题是:如果固件里同时又设了ro.sf.lcd_density,两者的关系会让人困惑——谁生效取决于 WindowManager 的取值顺序,而且调试完忘记 reset 就成了历史遗留诡异问题。我在接手别人的板子时,第一步永远是先wm size和wm density看一眼有没有被改过。第三个是在 App 内部通过Configuration覆盖,这属于应用层的事,系统级适配用不上,但在做多屏、分屏或者特殊分辨率适配时会用到。注意:不要同时用ro.sf.lcd_density和wm density,两个值不一样的时候,现象会非常难以定位。定下用哪一个之后,另一个清干净。4.3 改密度之后 App 布局崩了的处理改密度之后最典型的抱怨是界面变丑了内容显示不全文字被截断。这通常不是系统的问题,而是 App 本身没有按 Android 的适配规范写。最常见的原因是布局里用了大量固定 px 或者固定 dp 的宽高,比如给一个横向排列的四列按钮每个写死 300dp。密度一变大,每个按钮实际占的像素变多,一排放不下就换行或者被裁掉。正确的写法是用layout_weight做比例分配,或者用ConstraintLayout的百分比约束。这个锅理论上应该由 App 背,但在嵌入式项目里,系统工程师经常被要求别改密度了,改回原来的,因为改 App 的代价更大。我的处理原则是:如果项目周期紧张,先按 App 能接受的密度定稿,把这个值记进产品文档,后面谁再动都要走评审。第二个原因是密度改变触发了不同的资源目录。Android 选资源时按密度就近匹配,densityDpi从 160 跳到 240,原来加载drawable-mdpi的图会改去加载drawable-hdpi。如果这个目录下的图缺了几张,就会出现部分图标清晰、部分模糊的情况。排查方法是在 App 里打印getResources().getDisplayMetrics().densityDpi,再结合adb shell dumpsys package看资源目录,能很快定位。第三个原因是纯文字渲染。scaledDensity会影响sp单位的字号,密度变化会直接改变文字大小。如果 App 里用的是sp且字号设得比较极限,密度一变就可能超出容器高度被截断。这种情况我会建议把关键文本的容器高度改成wrap_content或者用minHeight兜底。一个很实用的验证流程是:改完密度后,先跑系统设置界面、系统图库和拨号盘这类自带 App,如果它们显示正常,说明系统侧没问题,剩下的就是三方 App 的适配;如果自带 App 都乱了,那八成是密度值选得过于离谱,或者densityDpi和分辨率组合出的 dp 宽度小到不合理。adb shell dumpsys window displays | grep -E Display:|mBaseDisplayInfo|init|cur adb shell dumpsys display | grep -E mBaseDisplayInfo|density这两条命令能直接看到当前逻辑宽高和密度,是判断到底是谁在生效的最快方式。5. 常见问题与排查实录5.1 黑屏、花屏、画面偏移的排查顺序屏幕点不亮的时候,不要一上来就怀疑 Android 配置,先把范围缩小到最底层。第一步看内核有没有识别到屏。MIPI 屏看 dmesg 里 panel 驱动的 probe 日志,HDMI 则看/sys/class/drm/card0-HDMI-A-1/status是不是connected。如果内核压根没识别到,后面 Android 层做什么都没意义。第二步看内核有没有报时序相关的错。dmesg | grep -iE vop|dsi|hdmi|drm里如果出现failed to get mode、unsupported、timeout这类关键字,基本可以定位到 DTS 时序或者 lane rate 配错。花屏、横纹、周期闪烁绝大多数都是时序问题,不是软件问题。第三步看 U-Boot 到内核的交接。现象是 logo 正常显示,一进内核就黑屏,或者反过来。这种情况十有八九是两边的时序或者模式选得不一样,重点检查 bootargs 里的video参数和 panel 时序是否一致。第四步才轮到 Android 层。如果内核日志干干净净,但从某次改动后 Android 就黑屏了,那就对比改动前后的dumpsys SurfaceFlinger输出,重点看合成器有没有拿到有效的显示设备。画面偏移是另一类问题,表现为图像整体向左或向右偏了一段,边缘有黑边。这几乎一定是时序参数的问题:前后肩或者同步极性写错了。特别是hsync-active、vsync-active、de-active、pixelclk-active这四个极性参数,从屏厂规格书抄错一个方向,就可能出现整屏偏移或者颜色反相。我遇到过把de-active抄反了导致整屏偏绿的情况,查了挺久。5.2 改了参数不生效的六种原因排查改了没反应这类问题,有个固定套路:先确认改的是不是当前生效的那一层,再确认有没有更高优先级的配置覆盖,最后确认改动有没有真正进入固件。现象常见原因验证方法改 build.prop 后密度没变同时存在wm density强制值adb shell wm density看是否非默认改 DTS 后分辨率没变DTB 没重新编译或没烧进去对比dmesg里的时序和 DTS 是否一致方向改了触摸错位只改了显示没改输入开发者选项里打开指针位置看坐标HDMI 分辨率改不动显示器 EDID 不支持该模式cat /sys/class/drm/.../modes看模式列表开机 logo 异常U-Boot 和内核模式不一致对比 bootargs 的 video 参数和 panel 时序改完刷机仍无效有多个 overlay 或者产品配置覆盖grep -rn搜属性名,确认唯一来源这里面最坑的是最后一条:同一个属性可能在多个 mk 文件里被定义,Rockchip 的 SDK 里device/rockchip/common/和device/rockchip/rk3588/下都有配置,产品层可能还有一份。谁最后生效取决于编译时的包含顺序,光看一个文件是看不出来的。解决办法是用 grep 把整个device/rockchip/扫一遍,一次看清楚。grep -rn ro.sf.lcd_density\|primary_display_orientation\|hwrotation device/rockchip/5.3 一套完整的显示适配自检清单把上面这些串起来,我形成了一套固定的自检流程,每次改完显示参数都跑一遍,基本能覆盖九成问题。先看当前状态:wm size、wm density、dumpsys display、getprop | grep -iE rotation|lcd_density|fb_。确认没有残留的强制值。再看内核:dmesg | grep -iE vop|dsi|hdmi|drm|panel,确认时序被正确识别,模式列表正确。再看 DRM 节点:cat /sys/class/drm/card0-*/status和modes,确认硬件层面给出的能力是正确的。然后看 SurfaceFlinger:dumpsys SurfaceFlinger | grep -A 10 Display,确认合成器拿到的逻辑分辨率和方向符合预期。最后跑三个验证:全屏 App 看是否满屏无黑边、开发者选项打开指针位置验证触摸对齐、用系统自带 App 看文字和图标是否清晰不模糊。这三条跑通,基本就可以判定适配完成。实操心得:把上面这几条命令写成一个 shell 脚本,放到板子的/data/local/tmp/下,每次改完一键执行并把输出存到文件。这样做的最大好处是可回溯,出问题的时候能把改动前后的输出直接对比,比凭记忆判断快太多。最后分享一个我自己的习惯:任何一次显示参数改动,我都会在提交信息里写清楚改了什么、为什么改、验证了哪些场景。RK3588 这类多路输出的平台,后面再加一块屏的时候,前面这些记录就是最省时间的参考资料。我手上有个项目前后加了三次屏,第三次能在一小时内搞定,靠的就是前两次留下的这套记录。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑