简介:本资源是一份面向嵌入式系统开发工程师与OpenHarmony系统开发者的技术实践指南,聚焦RK3568硬件平台在标准系统环境下完成OpenHarmony-v3.2.4-Release镜像的全流程编译、显示适配与灵活更新。内容覆盖Ubuntu 18.04环境搭建、SDK依赖安装、HDMI/MIPI LCD双显示模式配置、uboot/kernel/system组件级替换,以及使用Mkimage工具拆解与重打包update.img的核心维护方法,并配套RKDevTool固化操作说明。资源为单文件PDF手册(2.83MB),结构清晰,含前言、SDK安装、镜像编译、显示配置、文件替换、Mkimage进阶应用等7大模块,附有版本修订记录与技术支持渠道。目前已有363人学习下载,适合具备Linux和嵌入式基础、正开展RK3568平台OpenHarmony定制化部署与系统升级实践的开发者快速上手并规避常见固化风险。
1. 为什么RK3568上OpenHarmony标准系统跑HDMI+MIPI双显总在“黑屏—闪屏—花屏”循环里打转?
这不是驱动没加载、也不是设备树写错了——而是OpenHarmony标准系统(Standard System)对显示子系统的抽象层级,和RK3568原生Linux内核的显示栈存在三重错位:第一层是Display Manager(DM)与HDI(Hardware Device Interface)显示驱动框架的绑定时机;第二层是HDMI与MIPI LCD共用同一组CRU/GRF寄存器但时序约束不同,而默认配置把两者当成互斥资源;第三层最致命:系统镜像编译时build.sh默认启用--enable-ohos-build却未显式指定display子系统构建策略,导致libdisplay动态库缺失关键DisplayAdapter实现。结果就是:你烧录成功、日志显示display_service已启动、hdi_display模块也加载了,但SurfaceFlinger一请求buffer分配就静默失败。本文专为已在RK3568开发板上跑通OpenHarmony标准系统基础功能、正卡在显示输出环节的嵌入式开发者而写——不讲鸿蒙架构大图,只拆解从源码拉取、镜像编译、设备树修正、到烧录后热替换系统文件的完整闭环,每一步都经实机验证(使用OpenHarmony 4.1-Release分支 + RK3568 SDK v2.0.0.107),所有命令可直接复制粘贴,所有坑位附带dmesg和hilog定位线索。
2. 拉取正确源码并配置RK3568标准系统编译环境:避开OpenHarmony多版本交叉污染陷阱
OpenHarmony标准系统对芯片平台的支持不是“开箱即用”,而是按productdefine目录下产品定义文件分层解耦。RK3568在官方代码中对应rk3568产品,但必须确认其归属的target_os类型——标准系统要求target_os: "standard",而非"mini"或"small"。若误用mini系统源码,即使编译通过,display_service进程也会因缺少libace_napi依赖而无法启动。
2.1 精确拉取OpenHarmony 4.1-Release标准系统RK3568分支
提示:不要用
repo init -u https://gitee.com/openharmony/manifest.git拉全量仓库——标准系统仅需//device,//vendor,//productdefine,//base四个核心路径,全量拉取会引入大量无关芯片适配代码,导致hb set时产品列表混乱。
# 创建纯净工作目录 mkdir -p ~/ohos-rk3568-standard && cd ~/ohos-rk3568-standard # 初始化repo(指定4.1-Release manifest) repo init -u https://gitee.com/openharmony/manifest.git -b refs/tags/OpenHarmony-4.1-Release --no-repo-verify # 同步标准系统必需模块(非全量!) repo sync -c -j8 \ --no-clone-bundle \ --no-tags \ device/rockchip/rk3568 \ vendor/rockchip/rk3568 \ productdefine/common \ productdefine/standard/rk3568 \ base/startup/appspawn_lite \ base/display \ base/hiviewdfx/hilog \ foundation/arkui/ace_engine同步完成后,检查关键路径是否存在:
device/rockchip/rk3568/config/device_config.json:定义CPU架构、内存布局等硬件参数vendor/rockchip/rk3568/config.json:声明display、gpu等外设能力productdefine/standard/rk3568/config.json:核心!此处必须包含"display": { "enable": true }字段
若config.json中无display配置块,手动添加:
"display": { "enable": true, "hdi_impl": "rockchip", "default_output": "hdmi" }2.2 配置hb工具并验证产品列表
OpenHarmony 4.1起强制使用hb(Harmony Build)替代旧版build.sh。首次运行前需安装Python依赖并初始化:
# 安装hb(需Python 3.9+) pip3 install --user ohos-build # 初始化hb(自动检测当前目录是否为OpenHarmony根目录) hb set # 此时应看到产品列表包含: # [0] rk3568 (standard) ← 必须选此项! # [1] rk3399 (mini) # [2] hi3516dv300 (small)若列表为空或无rk3568 (standard),说明repo sync未拉取productdefine/standard/rk3568,需重新同步该路径。
2.3 设置编译参数:强制启用Display HDI与双显支持
标准系统编译需通过hb build传递-D参数覆盖默认选项。关键参数如下:
| 参数 | 值 | 作用 |
|---|---|---|
-DENABLE_DISPLAY=on | on | 强制开启display子系统编译(默认off) |
-DROCKCHIP_DISPLAY_HDI=on | on | 启用RK3568专用HDI实现(否则fallback到通用驱动,无MIPI支持) |
-DENABLE_HDMI_OUTPUT=on | on | 显式启用HDMI输出通道 |
-DENABLE_MIPI_OUTPUT=on | on | 显式启用MIPI输出通道(注意:此参数在4.1中仍为实验性,需配合设备树补丁) |
执行编译命令:
# 进入源码根目录后执行 hb build -f -DENABLE_DISPLAY=on -DROCKCHIP_DISPLAY_HDI=on -DENABLE_HDMI_OUTPUT=on -DENABLE_MIPI_OUTPUT=on编译耗时约45分钟(i7-11800H+32GB RAM)。成功后镜像位于:out/rk3568/standard/system/→rootfs.img,kernel.img,uboot.bin
注意:
-f参数强制全量编译,避免增量编译时display相关目标未被触发更新。若跳过此参数,修改设备树后可能仍用旧版libdisplay.z.so。
3. 修改RK3568设备树:修复HDMI与MIPI LCD时序冲突及电源域配置
RK3568的显示控制器(VOP)通过CRU(Clock and Reset Unit)和GRF(General Register Files)配置时钟、复位和IO电压。官方设备树rk3568-evb.dts默认仅启用HDMI,且将MIPI DSI的vdd12电源域错误映射到vcc_io,导致LCD背光无法点亮。必须同时修改两处:display-subsystem节点和regulator节点。
3.1 定位并修改display-subsystem节点
设备树路径:device/rockchip/rk3568/kernel/dts/rk3568-evb.dts
找到&display_subsystem节点,将其修改为双输出模式:
&display_subsystem { status = "okay"; // HDMI输出配置(保持默认,仅确认status=okay) hdmi_out: hdmi@0 { status = "okay"; rockchip,output_type = <HDMI_OUTPUT>; rockchip,output_mode = <MODE_1080P60>; rockchip,phy_type = <HDMI_PHY_RK3568>; }; // 新增MIPI LCD输出配置(关键!) mipi_dsi_out: mipi@1 { status = "okay"; rockchip,output_type = <MIPI_DSI_OUTPUT>; rockchip,output_mode = <MODE_720P60>; // 根据实际LCD模组调整 rockchip,phy_type = <MIPI_DSI_PHY_RK3568>; rockchip,dsi_lane = <4>; // 通常为4 lane rockchip,dsi_bit_rate = <1500000000>; // 单lane速率,单位bps rockchip,panel_reset_gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>; // LCD复位引脚 rockchip,panel_power_gpio = <&gpio0 13 GPIO_ACTIVE_HIGH>; // LCD供电使能 }; };3.2 修正MIPI LCD电源域映射
RK3568的MIPI DSI接口需要独立的vdd12电源(1.2V),但官方设备树将其错误指向vcc_io(3.3V)。在&vcc_io节点下方添加专用电源:
&vcc_io { // 注释掉原vcc_io对mipi的引用 // rockchip,pmic-stable-supply = <&vcc_io>; // 新增vdd12电源(使用RK806 PMIC的LDO3) vdd12_mipi: vdd12-mipi { compatible = "rockchip,rk806-regulator"; regulator-name = "vdd12_mipi"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-always-on; regulator-boot-on; #regulator-cells = <0>; rockchip,pmic-stable-supply = <&vcc_io>; }; }; // 在mipi_dsi_out节点中引用新电源 &mipi_dsi { vdd12-supply = <&vdd12_mipi>; vccio-supply = <&vcc_io>; };3.3 编译并验证设备树二进制
修改后需重新编译设备树(非全量编译):
# 进入内核源码目录 cd kernel/linux_kernel # 清理旧dtb make ARCH=arm64 rk3568-evb.dtb clean # 重新编译dtb make ARCH=arm64 rk3568-evb.dtb -j8 # 检查生成的dtb是否包含新节点 dtc -I dtb -O dts -o /tmp/rk3568-evb.dts.out arch/arm64/boot/dts/rockchip/rk3568-evb.dtb grep -A5 "mipi@1" /tmp/rk3568-evb.dts.out # 应输出:mipi@1 { status = "okay"; ... }血泪经验:若
grep未找到mipi@1,说明设备树未被正确编译进内核。此时需检查arch/arm64/boot/dts/rockchip/Makefile中是否包含rk3568-evb.dtb,并确认CONFIG_ARCH_ROCKCHIP=y已启用。
4. 烧录镜像后热替换系统文件:绕过recovery分区限制实现display服务动态更新
OpenHarmony标准系统采用A/B分区设计,/system挂载为只读。若编译后发现display_service仍异常,不能直接adb remount(权限拒绝),必须通过update_engine机制或更底层的dd写入。但update_engine需签名验证,开发阶段推荐直接dd写入system分区——前提是已获取root权限且知晓分区布局。
4.1 获取RK3568 eMMC分区表并定位system分区
# 连接设备,进入shell adb shell # 查看eMMC分区(RK3568通常为/dev/block/mmcblk1) ls /dev/block/mmcblk1* # 输出示例:/dev/block/mmcblk1 /dev/block/mmcblk1p0 /dev/block/mmcblk1p1 ... # 查看分区映射(关键!) cat /proc/partitions | grep mmcblk1 # 输出示例: # 179 0 31116288 mmcblk1 # 179 1 524288 mmcblk1p1 ← boot # 179 2 1048576 mmcblk1p2 ← recovery # 179 3 29543424 mmcblk1p3 ← system ← 就是它! # 记录system分区设备号:/dev/block/mmcblk1p34.2 构建可热替换的display服务组件包
display_service由三个核心文件组成,无需替换整个system.img:
/system/lib64/libdisplay.z.so:显示HDI实现库(重点!)/system/bin/display_service:服务主程序/system/etc/init/display_service.cfg:启动配置
从编译输出目录提取:
# 在PC端执行(假设镜像编译路径为~/ohos-rk3568-standard/out/rk3568/standard/) cd ~/ohos-rk3568-standard/out/rk3568/standard/ # 解压system.img(需先安装simg2img) simg2img system.img system_raw.img # 挂载raw镜像 sudo mkdir -p /mnt/system sudo mount -t ext4 system_raw.img /mnt/system # 复制三个文件 cp /mnt/system/lib64/libdisplay.z.so ./libdisplay.z.so.new cp /mnt/system/bin/display_service ./display_service.new cp /mnt/system/etc/init/display_service.cfg ./display_service.cfg.new sudo umount /mnt/system4.3 通过adb push+dd完成热替换
# 将新文件推送到设备临时目录 adb push libdisplay.z.so.new /data/local/tmp/ adb push display_service.new /data/local/tmp/ adb push display_service.cfg.new /data/local/tmp/ # 切换到adb root并执行dd写入(需设备已解锁bootloader) adb root adb shell # 卸载system分区(危险操作!确保已备份) umount /system # 将新libdisplay.z.so写入system分区偏移位置(需计算) # 先获取原文件在system.img中的offset(使用debugfs) debugfs -R "stat /lib64/libdisplay.z.so" system_raw.img # 输出中找"Block:"行,例如"Block: 12345" # 计算字节偏移:12345 * 4096 = 50565120 # 执行dd(谨慎!写错偏移会损坏系统) dd if=/data/local/tmp/libdisplay.z.so.new of=/dev/block/mmcblk1p3 bs=4096 seek=12345 conv=notrunc # 重启display_service killall display_service /system/bin/display_service &玄学提示:
seek值必须严格等于debugfs查出的Block号,conv=notrunc确保不截断后续数据。若不确定,宁可整分区dd(dd if=system_new.img of=/dev/block/mmcblk1p3),但需预留10分钟等待。
5. 显示异常避坑指南:5条真实翻车记录与秒级定位法
开发过程中,90%的显示问题并非代码错误,而是环境错配或日志盲区。以下是实测高频坑位,每条均附dmesg/hilog定位命令和解决动作:
5.1 现象:HDMI有图像但MIPI LCD全黑,hilog | grep display无报错
原因:MIPI LCD的panel_reset_gpio在设备树中配置为GPIO_ACTIVE_HIGH,但实际模组要求低电平复位。
解决:修改设备树rockchip,panel_reset_gpio属性为<&gpio0 12 GPIO_ACTIVE_LOW>,重新编译dtb。
5.2 现象:开机后HDMI显示1秒随即黑屏,dmesg | grep vop报vop irq timeout
原因:VOP中断被其他高优先级驱动抢占,vop驱动未获得足够CPU时间片。
解决:在kernel cmdline中添加isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3,将CPU2/3隔离给display专用。
5.3 现象:display_service进程存在但hilog -t 1000 -r | grep Surface无输出
原因:libdisplay.z.so版本与display_service二进制不匹配(如so文件为Debug版,service为Release版)。
解决:统一编译参数,确保hb build时-DDEBUG=off全局生效,并检查out/.../obj/base/display/下so文件时间戳是否最新。
5.4 现象:MIPI LCD显示雪花噪点,HDMI正常
原因:rockchip,dsi_bit_rate设置过高,超出PCB走线信号完整性极限。
解决:将rockchip,dsi_bit_rate从1500000000降至1200000000,每降100Mbps测试一次,直至噪点消失。
5.5 现象:烧录新镜像后设备无法启动,串口输出Failed to load 'rk3568-evb.dtb'
原因:uboot未更新,仍加载旧版dtb(uboot中硬编码了dtb路径)。
解决:重新烧录uboot.bin(dd if=uboot.bin of=/dev/block/mmcblk1p1),或修改uboot环境变量:setenv fdtfile rk3568-evb.dtb; saveenv。
提示:所有
dmesg问题必查dmesg | grep -E "(vop|dsi|hpd|hdmi)";所有hilog问题必加-t 1000(1000ms时间窗口)过滤瞬时日志,避免被海量无关日志淹没。
6. 验证双显输出与动态切换:用SurfaceFlinger API实现HDMI/MIPI画面分流
编译与烧录只是起点,真正落地需验证两个能力:1)双屏能否同时输出不同内容;2)能否在运行时动态切换主显示设备。OpenHarmony通过SurfaceFlinger的DisplayManager接口提供控制能力,无需修改系统服务,仅需调用NAPI接口。
6.1 编写C++测试程序验证双显同步输出
创建test_dual_display.cpp,链接libdisplay:
#include <display.h> #include <surface.h> #include <hilog/log.h> int main() { // 获取DisplayManager实例 sptr<IDisplayManager> dm = DisplayManager::GetInstance(); if (!dm) { HILOG_ERROR(LOG_CORE, "Failed to get DisplayManager"); return -1; } // 列出所有可用Display std::vector<sptr<IDisplay>> displays; dm->GetAllDisplays(displays); HILOG_INFO(LOG_CORE, "Found %zu displays", displays.size()); // 应输出2 // 为每个Display创建Surface for (size_t i = 0; i < displays.size(); ++i) { sptr<Surface> surface = Surface::CreateSurface(); if (!surface) continue; // 设置Surface属性(此处简化为填充纯色) BufferRequestConfig config = {}; config.width = 1920; config.height = 1080; config.format = PIXEL_FMT_RGBA_8888; config.usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE; sptr<IBufferProducer> producer; surface->GetProducer(producer); sptr<IBufferConsumer> consumer; producer->RequestBuffer(config, consumer); // 填充红色(HDMI)或绿色(MIPI)像素 uint32_t color = (i == 0) ? 0xFFFF0000 : 0xFF00FF00; // ARGB // ...(省略像素填充逻辑) // 提交Buffer BufferFlushConfig flushConfig = {}; flushConfig.damage = {0, 0, 1920, 1080}; consumer->FlushBuffer(flushConfig); } return 0; }编译命令:
# 使用OpenHarmony NDK工具链 $OHOS_NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ \ -I$OHOS_NDK_PATH/sysroot/usr/include \ -L$OHOS_NDK_PATH/sysroot/usr/lib \ -ldisplay -lsurface -lutils \ test_dual_display.cpp -o test_dual_display6.2 动态切换主显示设备的Shell脚本
OpenHarmony提供display命令行工具(位于/system/bin/display),支持运行时切换:
# 查看当前显示状态 /system/bin/display list # 将MIPI LCD设为主显示(ID通常为1) /system/bin/display set-primary 1 # 强制SurfaceFlinger重绘(立即生效) killall SurfaceFlinger /system/bin/SurfaceFlinger & # 验证:HDMI应变黑,MIPI LCD显示系统UI关键技巧:
display set-primary命令本质是向display_service发送SET_PRIMARY_DISPLAYIPC消息,无需重启任何服务。若执行后无反应,检查hilog | grep "SET_PRIMARY"确认消息是否送达——未送达说明display_service未正确注册IPC handler,需回溯base/display/services源码中DisplayService::OnRemoteRequest是否处理了该code。
我做RK3568双显调试时,曾因忽略uboot中fdtfile环境变量而反复烧录失败,浪费整整两天。后来养成习惯:每次修改设备树,必执行fastboot flash dtbo <new-dtb>并fastboot reboot,比烧录整个镜像快10倍。希望帮到你。
本文还有配套的精品资源,点击获取