做OpenHarmony系统开发,尤其是接触板卡适配和驱动移植的同学,最头疼的往往不是业务代码怎么写,而是“板子起不来”“外设不工作”“内核莫名崩溃”这类硬件相关问题。我这些年调过的RK3568、RK3399板子不在少数,踩过的坑也足够填满一个仓库,但回过头总结一下,发现问题定位来来回回就那么几板斧。这次就把这套OpenHarmony系统实战中最核心的硬件调试三板斧完整拆给大家:串口日志、设备树排查、烧录与调试工具链。这三样熟练了,硬件调试基本也就拿下一大半。
这套教程适合两类人:一类是刚接触OpenHarmony系统移植、被板子各种启动问题折磨的开发者,另一类是已经在做应用开发、但想进一步搞懂系统底层到底怎么跟硬件打交道的朋友。不管你用的是RK3568开发板,还是在x86电脑上搭系统环境,这套方法论都能复用。唯一区别在于,ARM板子更依赖串口和设备树,x86走的是ACPI那套体系,后面我会专门讲两者的差异。
1. 硬件调试三板斧是哪三板?
1.1 为什么硬件调试会卡死人
很多做应用开发的人转到系统层面,第一反应是“出问题加日志就行”。但硬件调试比纯应用调试麻烦得多,因为整条链路太长了:电源有没有稳定输出、时钟树配置有没有问题、DDR初始化是否成功、Bootloader能不能跑起来、内核能不能识别到外设、驱动有没有正确probe……任何一个环节出了差错,板子都可能毫无反应。
更关键的是,硬件问题的表现往往带有“连带性”。举个例子:某个GPIO的引脚复用配错了,表面上看只是这个引脚对应的外设不工作,但实际上这个引脚可能同时承担了I2C的SDA功能,结果I2C总线上的所有设备都遭了殃。这时候你单纯看业务代码,看死也看不出问题,必须有一套从底层往上看的排查手段。
这套排查手段,就是圈内常说的硬件调试三板斧:一看串口日志,看系统“说到哪一步了”;二查设备树,看硬件资源“有没有被正确分配”;三用烧录与调试工具,看镜像“到底跑的是什么”。这三板斧是递进关系,串口日志帮你缩小范围,设备树帮你定位配置问题,烧录工具链帮你确认运行的究竟是哪份代码、什么状态。
1.2 三板斧的组成与适用场景
先给一张速览表,让大家对每个板斧的用途有个直观印象。后面每一章我会展开细讲,包括常见参数、操作步骤和踩坑记录。
| 板斧名称 | 核心工具 | 解决的核心问题 | 典型场景 |
|---|---|---|---|
| 第一板斧:串口日志 | 串口终端(minicom、PuTTY等) | 系统启动到哪一步、卡在哪一步 | 板子不开机、死机、启动重启 |
| 第二板斧:设备树DTS | 设备树源码、dtc编译器、编译日志 | 外设资源分配错误、引脚冲突、地址不匹配 | 外设不识别、GPIO不输出、I2C/SPI读不到 |
| 第三板斧:烧录与调试工具 | RKDevTool、upgrade_tool、hdc | 镜像加载是否正确、运行时崩溃现场 | 镜像起不来、内核panic、应用崩溃 |
熟悉这套组合拳以后,不管板卡是哪家的方案,思路都一致。先用串口拿到第一现场,再拿设备树对照硬件原理图,最后用烧录工具和hdc验证结果。下面我按顺序拆。
2. 第一板斧:串口日志——系统开发的“生命线”
2.1 串口日志的配置方法(以RK3568为例)
串口日志在OpenHarmony系统开发里的地位,相当于飞机的黑匣子。系统从Bootloader到内核再到用户态,每一个阶段的关键信息都会通过串口打印出来。RK3568这样的主流开发板,官方资料里一般都会标注串口引脚位置,通常是调试UART2,也就是我们常说的debug uart。
接线这块我多说一句,千万别接反了TX和RX。正常来说,开发板的调试串口TX要接USB转串口模块的RX,开发板的RX接模块的TX,GND必须共地。我见过不少新手上来板子没输出,排查了半天发现是TX和RX接反了。接好线之后,使用minicom或者Windows下的PuTTY连接,波特率在OpenHarmony下通常设置为115200。有些板子的Bootloader阶段会设置更高波特率,但我实测大多数RK3568方案默认115200就能通吃。
如果你用Ubuntu主机,最常用的命令是:
sudo apt install minicom sudo minicom -s在配置界面里选择串口设备,比如/dev/ttyUSB0,波特率改成115200,关闭硬件流控。保存配置后重新打开minicom,插上电源,正常情况下就应该能看到Bootloader的打印信息了。如果这里没有输出,先别急着怀疑系统,优先检查串口线、波特率和供电是否稳定。
2.2 日志等级与关键信息解读
串口日志不像应用日志那样按应用进程区分,它更多是内核日志和系统启动日志。OpenHarmony内核侧沿用了Linux内核的日志分级机制,从低到高分别有debug、info、warn、error、fatal等。打印到串口的消息一般由earlycon或者console参数控制。
以Kernel命令行为例,你会在启动参数里看到类似这样的配置:
console=ttyFIQ0,115200n8ttyFIQ0是Rockchip平台特有的快速中断串口,在内核启动早期就能输出日志。如果发现系统启动阶段串口只打印到某一行就彻底停住不走了,优先怀疑后面的环节出了问题,对照日志把停顿点找到,基本就是问题所在的模块。
读懂日志还有个关键点:要能区分Bootloader阶段、内核阶段和init阶段。Bootloader阶段一般打印U-Boot字样,内核阶段会出现Starting kernel ...,而init阶段能看到Starting init或init service相关的输出。每种阶段卡住的含义完全不一样。Bootloader阶段卡住多半和内存、时钟、存储介质相关;内核阶段卡住要考虑设备树是否匹配、驱动是否初始化失败;init阶段卡住则要关注系统服务依赖和SELinux权限。
2.3 实操:定位启动失败问题
去年我调过一块基于RK3568的国产板子,现象是上电后屏幕一直黑,整机看起来毫无反应。当时第一反应就是接串口看输出,结果发现Bootloader正常启动,内核日志也打到了“Freeing unused kernel memory”,但之后再也没有任何输出。
顺着日志往下分析,卡住的位置其实是init进程启动阶段。没有输出说明不是某个具体服务报错,而是系统在创建init进程后没能继续推进。这种问题,经验上很大概率是根文件系统没有正确挂载,或者system分区镜像损坏。检查了烧录配置后果然发现,烧录时system分区镜像路径填错了,导致根文件系统不完整。重新烧录镜像后,系统正常启动。
这类问题用串口日志是最快定位的,如果没有串口,黑屏状态下你只能靠猜,效率天差地别。所以我的习惯是,拿到任何一块新板子,第一件事就接好串口,确认能正常输出日志,再考虑其他调试工作。
3. 第二板斧:设备树DTS排查——硬件资源分配的“户口本”
3.1 RK3568那么多设备树到底怎么选
如果你用过OpenHarmony在RK3568上的官方发布包,打开内核设备树目录后一定会被一堆dts文件搞晕。网上也经常有人问“openharmony的rk3568有许多设备树到底咋选”,这个问题确实绕不开。
设备树在嵌入式Linux和OpenHarmony中的角色,可以理解成硬件资源的“户口本”。它告诉内核,这块板子上有哪些外设、接在哪个地址上、用哪个中断号、引脚的复用关系是什么。RK3568同一颗SoC,因为各家板卡设计不同,内存大小、显示接口、网口PHY、电源管理顺序都可能不一样,所以必然需要不同的设备树来描述。
挑选设备树的判断依据,核心就三点。
第一,看板卡方案是谁家的。OpenHarmony社区常见的RK3568开发板有润和的DAYU200系列、触觉智能的RK3568系列、优博的RK3568系列等,每个厂商对自己的板子都有对应的dts文件。比如DAYU200相关板卡一般用jhj-rk3568.dts这类命名规则,触觉智能的方案可能是industio-rk3568.dts系列。拿到板卡之后,先找厂商资料里标注的“内核设备树名称”,或者看默认烧录镜像中的设备树配置,这是最稳妥的路径。
第二,看外设接口差异。同一个方案下的不同子型号,往往在显示屏接口上分叉:有的是MIPI DSI屏,有的是LVDS屏,有的是HDMI输出。RK3568支持多种显示接口,但设备树里同一个显示控制器的节点需要绑定对应的输出方式。如果选错了dts,最常见的结果就是屏幕不亮,但系统其实已经正常启动了。这时候接串口看日志,会发现内核日志中显示驱动结构体初始化正常,但实际没有检测到屏幕信号。
第三,看内存容量和型号。RK3568方案有2GB、4GB、8GB等不同配置,设备树中的memory节点虽然很多dts里没有硬编码,但在一些老版本中会在reg属性里标注初始内存区间。如果dts的内存描述与实际内存颗粒不匹配,轻则系统只识别到部分内存,重则会在开机阶段DDR初始化后直接卡死。
我给一个通用的快速判断模板,当时选dts时我常这么自检:
| 维度 | 自查问题 |
|---|---|
| 板卡厂商 | 板子丝印、厂商文档里写了哪套方案? |
| SoC型号 | 是RK3568J还是RK3568B2,封装型号是否一致? |
| 版本号 | 官方内核config中默认加载的是哪个dts? |
| 外设差异 | 屏幕接口、网口芯片、WiFi模组型号是否有差异? |
| 调试口定义 | 调试串口的复用管脚是否和dts中的pinctrl一致? |
把这几个问题理清楚,基本就不会在设备树迷宫里迷路了。
3.2 设备树修改与编译流程
搞定了“选哪个dts”之后,还得知道怎么改、怎么验证。OpenHarmony的设备树分两部分:一部分是内核相关,路径通常在kernel/linux/linux-5.10/arch/arm64/boot/dts/rockchip/;另一部分是HDF驱动相关的描述,分布在vendor/目录下的hdf配置中,和内核设备树节点通过compatible属性对应起来。
修改设备树最典型的操作是调整引脚复用、修改外设地址、增删设备节点。举个例子,如果要把某个UART口配置为普通GPIO模式,需要修改对应节点的pinctrl-0属性:
&uart2 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&uart2m0_xfer>; };如果想查看一个节点当前是否使能,可以看它的status属性。在设备树调试时,“明明配置了但外设没反应”,十次有八次是status没有改成"okay",或者被其他board级dts的disable覆盖了。
编译设备树的流程,建议在完整OpenHarmony编译环境下执行。以RK3568为例,常见的做法是在编译内核时通过make命令指定dtb文件,或者直接在构建系统中带上对应产品配置。每次修改dts后,只重新编译内核镜像和对应dtb即可,不需要重编整个系统:
./build.sh --product-name rk3568 --build-target kernel编译完成后,dtb文件会在内核编译输出目录下。烧录时,通常boot分区中已经包含了dtb,但有些方案会把dtb单独放到resource分区。这时候需要确认自己板卡的烧录配置,明确dtb到底打到了哪个分区,否则就会出现“改了dts但没生效”的假象。
3.3 实操:外设无法工作的排查流程
设备树出问题的表现往往是:系统起来了、串口也有输出、但某个外设就是一直不工作。最常见的几个场景是GPIO控制的LED不亮、I2C传感器读不到数据、SPI屏幕没有画面。
排查流程我一般这么走。
第一步,先在串口日志中搜索对应驱动有没有执行probe。比如I2C传感器驱动,在日志中搜索i2c或者驱动名字,看是否打印驱动注册信息。如果驱动根本没有probe,基本可以确定是设备树没有正确匹配,或者节点status没有打开。
第二步,检查寄存器地址和中断号。对照原理图,确认驱动在设备树中读取的reg地址是否和实际硬件跳线一致。I2C设备经常因为设备地址跳线配置和dts不一致,导致驱动在探测阶段直接失败。
第三步,排查引脚复用冲突。RK3568的GPIO分组很多,同一个引脚复用功能复杂。RK3568引入了一个很经典的坑:在dts中某个外设节点配置的pinctrl,如果和另一个节点配置到了同一个引脚,系统启动时不会报错,但其中一个外设就无法正常工作。遇到这种情况,可以打开内核的pinctrl调试信息,在串口日志中搜索pin config或pinmux相关输出,确认引脚是否被多个节点抢占。
另外还有一个细节很容易被忽略:修改设备树后,如果你没有重新打包resource分区或boot分区,系统加载的仍然是老设备树。这就涉及第三板斧的内容了,烧录与调试工具链可以帮助你确认到底跑的是哪份镜像。
4. 第三板斧:烧录与调试工具链——系统的“最后一公里”
4.1 烧录方式与固件打包
设备和代码都备齐,最终还得通过烧录工具让板子真正跑起来。OpenHarmony在RK3568平台上,烧录方式主要有两类:Windows下的RKDevTool和Linux下的upgrade_tool。两者的原理一致,都是通过USB OTG口让板子进入Loader模式或Maskrom模式,再把分区的镜像逐个写入存储介质。
实际工作环境如果在Ubuntu下操作,最常用的是upgrade_tool。切换到Loader模式的方式是:按住板子上的RECOVERY键不放,再按一下Reset键,然后松开RECOVERY键,系统就会进入Loader模式。此时电脑上执行:
sudo upgrade_tool ld可以看到设备列表里出现一个Loader设备。烧录镜像时,OpenHarmony编译产物一般位于out/rk3568/packages/phone/images/目录,里面常见的分区包括:
| 分区 | 作用 |
|---|---|
| loader | Rockchip引导器 |
| parameter | 分区表 |
| uboot | U-Boot引导程序 |
| boot | 内核、ramdisk、dtb |
| vendor | 厂商私有配置与驱动库 |
| system | OpenHarmony系统主体 |
| userdata | 用户数据区 |
烧录时比较稳妥的方式是把整个images目录一次性烧录,避免分区表不匹配。使用upgrade_tool全量烧录的命令类似这样:
sudo upgrade_tool uf out/rk3568/packages/phone/images/update.img或者直接在官方工具界面里导入配置。要注意的是,如果新版系统分区表有调整,旧烧录工具不认识新parameter,就必须通过单独烧录parameter分区后再烧其他分区,顺序不能乱。这里很多人会踩坑:全量烧录后板子反复重启,串口日志停在U-Boot阶段,最后发现是parameter分区没有更新,分区表错乱导致内核找不到根文件系统。
4.2 远程调试与日志抓取
板子正常开机进入系统之后,并不是所有日志都继续走串口,用户态的应用日志主要由hilog日志系统接管。这时候我们会用OpenHarmony自带的hdc工具,类似于Android开发中的adb,通过USB或者网络连接设备,执行shell命令、传输文件、抓取日志。
hdc连接设备的基础操作很简单:
hdc list targets hdc shell hdc hiloghdc shell进入设备终端后,可以查看进程信息、文件系统、系统服务状态。hdc hilog会持续输出OpenHarmony应用层和系统服务层的日志,配合-e参数可以过滤关键字,比如:
hdc hilog | grep -i "sensor"这套工具在排查应用层服务加载、HDF驱动服务注册失败、权限问题等场景非常高效。串口日志适合看内核和启动早期的问题,hilog则覆盖用户态和HDF框架层,两者配合,硬件调试的覆盖范围才算完整。
4.3 实操:抓取内核Crash信息
内核崩溃是硬件调试中最让人头疼的问题之一,但恰恰是串口加调试工具最能发挥价值的场景。经典的Kernel panic现象是板子突然黑屏重启,串口日志最后出现一堆寄存器信息,其中包含PC指针、调用栈、panic原因。
抓取Crash现场的核心是别急着重启板子,而是让系统在panic时保留完整日志。串口输出会打印类似这样的信息:
Kernel panic - not syncing: FIQ syscall handler CPU: 2 PID: 183 Comm: swapper/0 Not tainted 5.10.x PC is at rk_timer_handler+0x3c/0x80看到这类输出,不要慌张,先记下几个关键字段:PC is at后面的函数名和偏移、Call trace上的调用路径、Kernel Offset的基地址。然后结合自己修改过的代码和设备树节点,基本能锁定是哪个模块的操作引发了崩溃。
碰到比较隐蔽的内存踩踏问题,还可以借助内存相关调试选项重新编一个debug版本内核,开启slub调试和KASAN(地址消毒器)后,崩溃现场会直接打印出被踩内存的分配栈和释放栈。调试完再把相关配置关掉,恢复正式版本。这一套下来,90%的Crash问题都能定位到具体函数和调用路径。
5. 扩展:x86平台下OpenHarmony搭建调试的差异
5.1 x86版本如何选择与下载
很多朋友没有ARM开发板,但想体验OpenHarmony系统,于是会搜“开源鸿蒙x86iso下载”“电脑版x86 openharmony”这类关键词。OpenHarmony官方社区比较早就提供了x86_64架构的通用镜像,主要用于兼容应用开发、系统开发和UI体验场景,可以在普通PC的虚拟机上或者支持UEFI启动的设备上运行。
选择x86版本时需要注意区分:官方仓库和社区版本更新的节奏不一样,一般以OpenHarmony官方发布的主线版本为基准。下载镜像后,在VMware或VirtualBox里新建虚拟机时,建议选择Linux 64位系统类型,内存分配不低于4GB,存储空间不低于32GB。启动方式选择UEFI,部分老版本镜像对Legacy BIOS支持不好,可能出现引导失败。
5.2 x86调试与ARM调试的区别
同样是OpenHarmony开发,x86平台和ARM平台在硬件调试上的差异其实非常大。
最核心的差异是设备树的角色。ARM平台靠设备树描述硬件,x86平台则依赖ACPI固件表。你在RK3568上用的dts思路,在x86上基本派不上用场。x86下的外设资源是固件通过ACPI上报给内核的,串口调试的配置也不一样,x86的早期串口日志可以通过内核启动参数console=ttyS0,115200n8打开。
其次是烧录方式。RK3568这种ARM板卡走Loader模式、分区镜像机制,x86通用版本则更像传统操作系统,镜像以整个磁盘镜像的方式写入,或者通过虚拟机加载ISO安装。调试工具上,hdc在两者上都能用,连接方式和命令基本一致,这一点倒是不用重新学。
第三是适用场景。如果你目标是做硬件适配、驱动移植、板卡bring-up,老老实实买一块RK3568或者其他ARM架构的开发板更有价值,因为真实的硬件调试经验和问题场景只能在实际板子上积累。x86版本更适合做应用开发验证、系统UI测试、HDF框架学习和无法获取ARM硬件时的替代方案。两者不冲突,但方向要分清。
6. 常见问题与排查技巧实录
最后把这些年硬件调试中高频遇到的坑整理成一张速查表,直接对照定位,效率会高很多。
| 问题现象 | 可能原因 | 排查方式 | 解决建议 |
|---|---|---|---|
| 串口无任何输出 | 串口线接反、波特率不对、板子供电异常 | 检查TX/RX/GND接线,确认波特率115200,测量供电电压 | 换线、换USB口,确认电源适配器功率足够 |
| 串口停在U-Boot | 分区表损坏、内存配置不对、启动介质没选对 | 查看U-Boot打印中的存储设备信息 | 重新烧录parameter和uboot分区 |
| 内核启动后反复重启 | rootfs损坏、system分区镜像不对 | 串口查看挂载日志,确认各分区是否正常挂载 | 全量烧录镜像,format userdata |
| 外设一直不工作 | 设备树节点未使能、引脚冲突、地址不匹配 | 日志搜索驱动probe信息,核对dts配置 | 修改dts,重新编译boot/resource分区 |
| RK3568设备树选错 | 同芯片不同板卡方案差异 | 对照板卡丝印和方案商文档 | 找到对应厂商的dts文件或内核config |
| 应用层日志看不到 | HDF服务没起来、hilog过滤器设置问题 | 使用hdc hilog加关键字过滤,检查服务状态 | 确认驱动服务已注册,调整日志级别 |
| 内核panic后无完整栈 | 内核只打了部分日志,或panic后立即重启 | 开启内核panic保留串口输出的配置 | 调试版本开启panic_print和KASAN |
顺手再分享一个排查技巧:修改设备树或者内核配置后,先不要急着全量烧录,很多RK3568板卡支持单独烧录boot分区或resource分区。单独烧录比全量烧录快很多,也降低了对其他分区的干扰,调试循环效率可以翻倍。等到配置基本稳定,再做一次全量烧录验证。
我个人的体会是,硬件调试这件事,经验和手法很重要,但更关键的是要有条理。串口日志告诉你“卡在哪一步”,设备树告诉你“资源配没配对”,烧录和调试工具告诉你“跑的是哪份代码”,三步走完,大部分问题都能收敛到具体模块。调板子调得多的人都知道,调试三板斧不是高深的理论,而是把时间花在真正有价值的信息上,少做无用功。后续这个系列还会继续讲RK3568各外设的驱动适配、HDF框架实战和系统裁剪,大家可以先把手里的板子用这三板斧跑顺,后面才好继续深入。