ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

RK3588开发板MIPI屏旋转实战:从设备树到Android框架的横竖屏适配

RK3588开发板MIPI屏旋转实战:从设备树到Android框架的横竖屏适配 1. 拿到RK3588开发板后屏幕方向为什么总是不对很多人第一次把正点原子的MIPI屏接到RK3588开发板上点亮Android系统之后第一反应往往是画面出来了但方向是歪的。要么整个桌面逆时针躺了90度要么触摸坐标和显示坐标完全对不上点左边响应右边。这个问题在嵌入式Android开发里非常典型尤其是从手机、平板这类消费电子转过来的开发者习惯了系统自动旋转一旦到了开发板上发现横竖屏切换根本不是“设置里点一下”那么简单。RK3588这颗芯片本身支持多路MIPI DSI输出正点原子的MIPI屏通常走的是DSI接口配合Android的SurfaceFlinger和HWC做图层合成。屏幕旋转这件事表面上看是“转个方向”实际上牵扯到三个层面显示控制器VOP的扫描方向配置、Android框架层的旋转策略、以及触摸屏的坐标映射。这三者只要有一个没对齐就会出现画面转了但触摸没转、或者画面转了但开机logo还是歪的、又或者旋转之后性能明显掉帧的情况。我这次拿到的硬件组合是正点原子RK3588开发板加一块10.1寸MIPI DSI屏幕系统是Android 12。目标很明确让屏幕默认以横屏显示同时支持在系统设置里手动切换横竖屏并且触摸坐标要跟着一起转。听起来简单但实际调下来从设备树到HAL层再到应用层每一层都有坑。下面我把整个实战过程拆开讲包括每一步为什么这么做、参数怎么算、以及我踩过的那些坑。提示不同批次的开发板和屏幕模组设备树节点名和GPIO引脚可能不同动手前先用adb shell确认当前系统的显示节点和输入设备节点不要直接照抄别人的配置。2. 先搞清楚RK3588的显示链路和旋转发生在哪一层2.1 从VOP到MIPI DSI再到屏幕的完整通路RK3588内部有多个VOPVideo Output Processor每个VOP可以绑定不同的显示接口比如HDMI、DP、MIPI DSI、eDP等。正点原子的MIPI屏一般接在DSI0或DSI1上对应的VOP可能是VP0、VP1、VP2、VP3中的某一个。设备树里通过rockchip,grf和port节点来描述这个绑定关系。显示数据从GPU或VPU渲染出来之后经过SurfaceFlinger合成再通过HWC提交给VOPVOP按照配置的时序扫描输出到MIPI DSI控制器最后DSI控制器把像素数据打包成MIPI协议包发给屏幕。屏幕旋转如果发生在VOP扫描阶段那就是硬件级别的旋转效率最高如果发生在SurfaceFlinger合成阶段那就是GPU做旋转功耗和延迟都会增加。Android系统里屏幕旋转的决策权在WindowManagerService它根据传感器或用户设置决定当前是0度、90度、180度还是270度然后通过DisplayManager通知SurfaceFlinger做对应的变换。但这里有个关键点如果底层VOP已经配置了固定旋转上层再转一次就会叠加。所以第一步必须确认底层到底转没转。2.2 三种旋转方案的取舍VOP硬件旋转、GPU合成旋转、应用层旋转在实际项目里屏幕旋转有三种常见做法各有适用场景方案实现位置优点缺点适用场景VOP硬件旋转设备树/内核零性能损耗功耗最低配置复杂部分VOP不支持任意角度固定横屏或固定竖屏产品GPU合成旋转SurfaceFlinger/HWC灵活支持动态切换增加GPU负载可能掉帧需要频繁切换方向的应用应用层旋转App内部不影响系统只对单个应用生效特定App适配对于正点原子MIPI屏这种固定安装的产品最推荐的是VOP硬件旋转加框架层动态切换的组合默认用VOP做90度旋转实现横屏同时保留框架层的旋转能力让用户在设置里可以切回竖屏。这样既保证了默认状态的性能又保留了灵活性。但这里有个前提RK3588的VOP是否支持你需要的旋转角度。根据Rockchip的文档VOP2支持0/90/180/270度旋转但某些分辨率下90度和270度旋转会受限于带宽。10.1寸MIPI屏常见分辨率是800x1280或1200x1920旋转后变成1280x800或1920x1200带宽需求会变化需要实际测试确认。2.3 设备树里跟旋转相关的关键节点RK3588的设备树里跟显示旋转直接相关的节点主要有这几个vop节点配置VOP的绑定关系和工作模式dsi节点配置MIPI DSI控制器的时序和通道panel节点描述屏幕本身的时序参数route_dsi节点描述VOP到DSI的路由旋转配置通常写在route_dsi或者vop的assigned-clocks附近具体位置取决于内核版本。Android 12对应的Rockchip内核一般是5.10这个版本里旋转是通过rockchip,plane-mask和rockchip,primary-plane来间接控制的更直接的方式是修改panel节点里的width-mm和height-mm以及display-timings里的hactive和vactive。不过我要提醒一句直接改hactive和vactive来交换宽高并不等于旋转。交换宽高只是改变了时序参数画面内容本身没有旋转结果就是画面被拉伸或压缩。真正的旋转需要在VOP层面做坐标变换这通常通过rockchip,rotation属性或者内核里的vop_plane配置来实现。3. 动手改设备树让MIPI屏默认横屏显示3.1 确认当前屏幕时序和VOP绑定关系在改任何东西之前先把当前状态摸清楚。开发板启动后通过串口或adb连上去执行adb shell cat /d/dri/0/summary这个命令会输出当前显示控制器的状态包括VOP绑定的接口、当前分辨率、刷新率等。如果/d/dri不存在说明内核没开debugfs需要先重新配置内核。另一个有用的命令是adb shell dumpsys SurfaceFlinger | grep -i display这会显示SurfaceFlinger当前管理的显示设备信息包括旋转角度和显示区域。假设我们确认了屏幕接在DSI0上VOP是VP2当前分辨率是800x1280竖屏。目标是改成1280x800横屏。3.2 修改panel节点里的时序参数找到设备树里对应的panel节点通常在arch/arm64/boot/dts/rockchip/rk3588-xxx.dtsi或者板级dts文件里。正点原子的板子一般会有独立的dts文件比如rk3588-atk-dlrk3588.dts。panel节点里会有display-timings子节点里面定义了hactive、vactive、hfront-porch、hback-porch、hsync-len、vfront-porch、vback-porch、vsync-len、clock-frequency等参数。对于800x1280的屏幕原始时序大概是display-timings { native-mode timing0; timing0: timing0 { clock-frequency 68000000; hactive 800; vactive 1280; hfront-porch 20; hback-porch 20; hsync-len 10; vfront-porch 10; vback-porch 10; vsync-len 4; }; };要改成横屏最直接的做法是交换hactive和vactive同时调整porch参数。但正如前面说的这不会旋转画面只会改变扫描方向。真正要旋转需要配合VOP的旋转配置。3.3 VOP旋转配置的具体写法在RK3588的5.10内核里VOP的旋转可以通过rockchip,rotation属性来配置。这个属性通常写在vop节点或者route_dsi节点里。具体写法vop { status okay; assigned-clocks cru ACLK_VOP; assigned-clock-rates 800000000; }; route_dsi0 { status okay; connect vp2_out_dsi0; rockchip,rotation 90; };rockchip,rotation的值可以是0、90、180、270单位是度。90度表示顺时针旋转90度把竖屏变成横屏。但这里有个坑不是所有VOP都支持旋转。根据RK3588的TRMVP0和VP1不支持旋转VP2和VP3支持。所以如果你的屏幕绑在VP0上这个配置不会生效需要先把路由改到VP2或VP3。改路由的方法是在route_dsi0节点里把connect改成vp2_out_dsi0同时确保vp2节点是okay状态。3.4 编译、烧录、验证改完设备树后重新编译内核和设备树make ARCHarm64 rk3588-atk-dlrk3588.img -j$(nproc)然后把生成的boot.img和dtb.img烧录到开发板。烧录工具用Rockchip的upgrade_tool或者正点原子提供的烧录脚本。重启后再次执行adb shell cat /d/dri/0/summary确认分辨率变成了1280x800并且旋转角度显示为90。如果分辨率对了但画面还是竖的说明旋转没生效需要检查VOP绑定和rockchip,rotation属性是否写对了位置。注意有些内核版本里rockchip,rotation属性名可能是rotation或者rockchip,plane-rotation具体以你使用的内核源码为准。用grep -r rotation drivers/gpu/drm/rockchip/可以快速找到支持的属性名。4. Android框架层怎么配合底层旋转4.1 SurfaceFlinger的旋转策略和DisplayManager的关系底层VOP做了90度旋转之后Android框架层看到的显示设备分辨率已经是1280x800了。但这时候如果用户在设置里选择“自动旋转”或者手动切到竖屏WindowManagerService会通知SurfaceFlinger做额外的旋转结果就是画面又被转了一次变成倒着的。所以底层旋转和框架层旋转必须二选一或者做好协调。常见的做法是底层固定旋转框架层锁定方向。具体来说在frameworks/base/core/res/res/values/config.xml里配置bool nameconfig_supportAutoRotationfalse/bool integer nameconfig_lidKeyboardAccessibility0/integer同时修改WindowManagerService里的默认旋转策略让它不要根据传感器自动旋转。如果产品需要支持动态切换那就不能在底层做固定旋转而是要让框架层全权处理。这时候底层VOP保持0度框架层通过SurfaceFlinger的setDisplayProjection来做旋转。这种方案性能差一些但灵活性高。4.2 修改默认屏幕方向为横屏Android系统默认是竖屏portrait要改成横屏landscape需要修改frameworks/base/core/res/res/values/config.xml里的integer nameconfig_defaultDisplayRotation1/integer这个值的含义是00度190度2180度3270度。设成1就是默认横屏。但光改这个还不够因为很多系统UI组件是按竖屏布局的强制横屏后状态栏、导航栏、锁屏界面可能会错位。所以还需要检查SystemUI和Launcher的布局配置。另一个更彻底的方法是在device/rockchip/rk3588/目录下的BoardConfig.mk里添加SF_PRIMARY_DISPLAY_ORIENTATION : 90这个宏会直接影响SurfaceFlinger的初始旋转角度。4.3 触摸坐标映射的同步调整屏幕转了触摸没转这是最让人头疼的问题。Android的输入系统通过InputReader读取触摸事件然后根据显示设备的旋转角度做坐标变换。如果底层VOP转了但输入系统不知道坐标就会错位。触摸坐标映射的配置通常在/vendor/etc/或者/system/etc/下的.idc文件里Input Device Configuration。找到对应触摸屏的idc文件比如gt9xx.idc或者goodix_ts.idc里面会有touch.orientationAware 1这个属性告诉输入系统触摸坐标需要根据显示方向做旋转。如果设成0触摸就不会跟着转。另外还需要在idc文件里指定触摸屏的坐标系和显示坐标系的对应关系touch.size.calibration geometric touch.orientation.calibration none如果旋转后触摸方向反了可以尝试把touch.orientation.calibration改成interpolated或者手动调整touch.orientationAware的值。提示修改idc文件后需要重启输入服务或者重启系统才能生效。可以用adb shell getevent -l查看原始触摸事件确认坐标范围是否正确。5. 实测中遇到的三个典型问题和排查过程5.1 开机logo方向对但Android桌面方向反了这个问题我遇到过两次第一次是因为uboot里的logo配置和内核里的旋转配置不一致。uboot阶段显示logo用的是单独的显示驱动它不读内核的设备树而是读uboot自己的dts或者环境变量。如果uboot里没配旋转logo就是正的内核里配了90度旋转Android桌面就转了90度结果就是logo正、桌面歪。解决办法是在uboot的dts里也加上旋转配置或者干脆让uboot和内核用同一套时序参数。正点原子的uboot源码里显示配置通常在arch/arm/dts/rk3588-atk-dlrk3588.dts或者board/rockchip/目录下。第二次遇到这个问题是因为SurfaceFlinger的初始旋转角度和VOP旋转叠加了。VOP转了90度SurfaceFlinger又转了90度总共180度画面就倒过来了。排查方法是adb shell dumpsys SurfaceFlinger | grep -i orientation\|rotation看SurfaceFlinger报告的旋转角度是不是0。如果不是0说明框架层也在转需要把config_defaultDisplayRotation改回0。5.2 旋转后帧率从60掉到30这个问题的根因通常是VOP带宽不够。RK3588的VOP2在90度旋转时内存访问模式会变化如果分辨率较高比如1920x1200带宽可能超过VOP的处理能力导致帧率下降。排查方法adb shell cat /d/dri/0/summary看输出的bandwidth或者clock信息。如果带宽接近上限可以尝试降低刷新率比如从60Hz降到50Hz减少图层数量关闭不必要的叠加层把旋转改到GPU做虽然功耗高但不受VOP带宽限制我实测下来1280x800分辨率下VOP旋转60Hz是稳的1920x1200就需要降到50Hz或者改用GPU旋转。5.3 触摸屏在旋转后点击偏移越来越大这个问题比较隐蔽通常是因为触摸屏的校准参数是按竖屏算的旋转后没有重新校准。表现是屏幕中心点附近点击还算准越往边缘偏移越大。解决办法是重新校准触摸屏。Android自带TouchCalibration工具但需要root权限。更简单的方法是在idc文件里手动指定坐标变换矩阵touch.coordinate.matrix 0,1,0,-1,0,1,0,0,1这个矩阵的含义是x y, y -x相当于逆时针旋转90度。具体矩阵值需要根据实际旋转方向调整。如果不想改矩阵也可以在驱动层做坐标变换。正点原子的触摸屏驱动通常在drivers/input/touchscreen/目录下找到对应的驱动文件在报告坐标之前加上旋转逻辑。6. 动态横竖屏切换的完整实现思路6.1 通过系统属性控制默认方向如果产品需要在不同场景下切换横竖屏比如展示模式用横屏、手持模式用竖屏可以通过系统属性来动态控制。在init.rc或者build.prop里定义persist.sys.display.rotation90然后在SurfaceFlinger启动时读取这个属性设置初始旋转角度。切换时通过setprop修改属性再重启SurfaceFlingeradb shell setprop persist.sys.display.rotation 0 adb shell stop surfaceflinger adb shell start surfaceflinger这种方式的缺点是重启SurfaceFlinger会导致界面闪烁不适合频繁切换。6.2 利用Android的Settings数据库做持久化更优雅的方式是利用Android的Settings.System.USER_ROTATION数据库项。应用层可以通过Settings.System.putInt()写入旋转角度系统会自动通知WindowManagerService做切换。但前提是底层VOP不能做固定旋转否则会叠加。所以这种方案适合底层0度、框架层全权处理的场景。实现步骤底层VOP保持0度设备树里不配rockchip,rotation在config.xml里开启自动旋转config_supportAutoRotationtrue应用层通过Settings.System.putInt(getContentResolver(), Settings.System.USER_ROTATION, rotation)设置方向触摸屏idc文件里touch.orientationAware1让输入系统自动跟随这种方案的好处是切换流畅不需要重启服务。代价是GPU负载增加续航会受影响。6.3 性能与灵活性的平衡点在实际项目里我一般会根据产品形态做取舍固定安装的广告机、工控屏底层VOP固定旋转框架层锁定方向性能最优需要用户手动切换的平板底层0度框架层动态旋转灵活性优先混合场景底层做180度旋转如果有需要框架层做90度切换避免叠加不管选哪种方案都要在真实硬件上跑一遍完整测试开机logo、开机动画、锁屏、桌面、应用内旋转、触摸响应、视频播放。任何一个环节方向不对都会影响用户体验。7. 几个容易被忽略的细节和我的实操心得第一个细节是开机动画的方向。Android的开机动画是由bootanimation服务渲染的它读取的是/system/media/bootanimation.zip里的图片。如果图片本身是竖屏的而系统默认横屏开机动画就会显示成拉伸或裁剪的。解决办法是准备两套开机动画图片或者让开机动画也走旋转逻辑。第二个细节是视频播放器的全屏方向。很多视频播放器在检测到屏幕旋转后会自动切换全屏但如果底层VOP已经旋转了播放器再转一次就会出问题。这时候需要在播放器的AndroidManifest.xml里锁定screenOrientation或者让播放器读取真实的显示方向而不是传感器方向。第三个细节是adb截图的方向。用adb shell screencap截图时截出来的图片方向可能和实际显示不一致。这是因为screencap读的是framebuffer的原始数据没有经过旋转。如果要做自动化测试需要对截图做额外的旋转处理。我在实际调试中总结了一个快速验证流程改完设备树后先看uboot logo方向再看开机动画方向进入桌面后看状态栏和导航栏位置打开设置看列表滚动方向打开浏览器看网页渲染方向用getevent测试触摸坐标播放视频看全屏切换是否正常这七步走完基本能覆盖90%的旋转相关问题。剩下的10%通常是特定应用的适配问题需要单独处理。注意每次修改设备树或框架层配置后建议先make clean再重新编译避免增量编译导致的配置不生效。Rockchip的内核编译有时候会缓存旧的dtb烧录前确认dtb文件的时间戳是新的。最后说一个我踩过的坑正点原子的MIPI屏有不同的批次有些批次的屏幕默认就是横屏时序有些是竖屏时序。拿到新屏幕时先用adb shell cat /sys/class/drm/card0-DSI-1/modes看一下屏幕报告的原生分辨率再决定怎么配旋转。不要假设所有同型号屏幕的时序都一样实测为准。
返回列表