OpenHarmony硬件调试三板斧:串口、设备树与崩溃定位实战
2026/9/6 9:38:35 网站建设 项目流程

1. 为什么硬件调试首当其冲:先找准你的三类调试缺口

刚接触 OpenHarmony 系统开发的时候,我走过一段很长的弯路。那时候手里拿的是一块 RK3568 开发板,烧录完官方镜像,上电以后屏幕没反应,串口终端也没输出,我一度以为是板子坏了。后来才发现,问题不出在硬件上,而是我根本不知道在这个系统里该到哪里去看"它到底卡在哪一步"。那次经历让我彻底明白了一个道理:在 OpenHarmony 这种从内核到框架再到应用全链路的系统里,能不能高效定位问题,不取决于你手里有多少工具,而取决于你清不清楚每个调试手段对应的观测层次。

硬件调试这件事,很多人第一反应是示波器、万用表、逻辑分析仪这些物理工具。但在 OpenHarmony 这种运行在通用 SoC 上的开源系统开发里,真正的第一步其实是建立"系统可见性"。说得直白一点,就是让系统在运行的每一个关键节点都"开口说话"。这里我总结出了自己的"硬件调试三板斧":串口日志、设备树适配、崩溃分析与远程调试。这三板斧分别对应系统开发里的三类典型问题——系统起不来、硬件外设不工作、运行期崩溃。

  • 串口日志解决的是"系统现在到底跑到哪一步了",是整个调试的地基。
  • 设备树解决的是"系统认为这块板子的硬件是什么",是硬件初始化的灵魂。
  • 崩溃分析与调试器解决的是"系统跑飞以后留了什么线索",是定位疑难杂症的关键。

这篇文章不打算给你堆一堆文档链接,而是把我在 RK3568 + OpenHarmony 环境里踩过的坑、验证过的方法、排查过的真实 Case 都拆开讲清楚。无论你是刚把 OpenHarmony 跑起来的初学者,还是正在做系统移植、某个外设驱动的老手,这套思路都通用。即便你用的是不是 RK3568,只要还是 Linux 内核 + OpenHarmony 用户态这套体系,三板斧的招式就不会变。

2. 第一板斧:串口——最原始但最不可替代的观测窗口

2.1 接线与波特率:串口没输出,九成是这三个低级错误

很多人在串口这一步就被卡住了,但真正的问题往往很简单。RK3568 开发板上的调试串口通常是三根线:TX、RX、GND,个别板子还会引出 VCC,但这个一般不用接。我曾经图省事,直接用杜邦线把 TX 和 RX 反着插,结果终端上全是乱码。后来养成一个习惯:拿到板子先看原理图,确认哪个是调试串口,再看丝印方向,永远先接 GND,再交叉接 TX/RX。

波特率是另一个经典翻车点。Rockchip 平台的调试串口默认波特率不是 115200,而是 1500000,也就是 1.5Mbps。这个速率在很多通用串口工具的下拉列表里不会直接出现,需要手动输入。我第一次用 minicom 的时候,直接在配置里选了个 115200,终端确实有输出,但全是不可读的乱码,我还以为是固件刷坏了。实际上只要波特率不匹配,接收端看到的基本都是一堆"烫烫烫"一样的字节流。

推荐直接用 picocom,它对 1500000 这种自定义波特率支持得比较好:

sudo picocom -b 1500000 /dev/ttyUSB0

如果要用 minicom,也没有问题,但需要进入配置界面手动输入 1500000:

sudo minicom -s

这里有一个非常容易忽略的选项——硬件流控。串口调试线上一般没有 CTS/RTS 信号,所以终端工具的流控必须关掉。minicom 的默认配置里,Hardware Flow Control 有时候是开启的,这会导致你只能收到一部分日志,而且表现非常诡异,时好时坏。我在调一块 RK3568 板子的时候,一直被这个问题困扰,最后发现是硬流控开着,关掉以后整个世界都清净了。

2.2 U-Boot、内核、用户态:串口日志的三层含义

串口接好了,下一步是学会看日志。OpenHarmony 的启动过程分三个阶段,每个阶段的日志来源不一样,排查问题的侧重点也不一样。

第一个阶段是 U-Boot。上电瞬间,U-Boot 会打印板子型号、DDR 初始化结果、启动介质等信息。这个阶段的日志如果卡住,先怀疑硬件问题,比如 DDR 频率太高不稳、eMMC 或 SD 卡识别失败。RK3568 的 U-Boot 日志里会出现"DDR V1.09"之类的字样,看到它说明 DDR 初始化已经过去了。

第二个阶段是内核。U-Boot 加载内核后会打印"Starting kernel ...",然后内核开始输出大量初始化日志。这个阶段最容易暴露设备树选错的问题,比如某个驱动 probe 失败、某个 IO 口被复用冲突、某个 regulator 电压配置不对。内核日志的开头部分会显示 Machine model,这就是当前实际加载的设备树对应的板卡型号。

第三个阶段是系统服务和用户态程序。OpenHarmony 的 init 进程启动后,会依次拉起各种系统服务。这个阶段的日志在串口上默认只输出一部分,更多信息要进系统后用 hilog 查看。如果你看到串口在"Start init"附近卡住,但内核日志一切正常,那问题大概率出在某个服务起不来,而不是硬件。

判断"卡在哪一步"最直接的方法,就是看串口日志最后几行的关键词:

  • 卡在 U-Boot 的 DDR 或存储初始化位置,优先检查硬件连接。
  • 卡在 "Starting kernel" 之后没有任何输出,优先怀疑内核镜像和设备树不匹配。
  • 有内核日志但无用户态日志,优先检查 rootfs 是否完整、init 是否正常执行。

2.3 hilog 和 dmesg:进系统以后的日志筛选姿势

串口终端本身只是一个通道,进系统以后,真正的日志管理工具是 hilog 和 dmesg。dmesg 看内核环形缓冲区,hilog 看 OpenHarmony 用户态日志。两者分工不同,不能互相替代。

我在定位问题的时候,习惯先把两类日志分别存到文件里,再做关键词搜索。手动一行行盯着看,效率太低,而且日志刷得太快,容易漏掉关键信息。

# 进系统后,把内核日志导出 dmesg > /data/local/tmp/kernel.log # 查看最近 200 行 hilog hilog -x

有个小技巧:rk3568 的板子如果出现内核 panic,可以在重启后通过 pstore 把上次崩溃的日志拉出来:

cat /sys/fs/pstore/console-ramoops-0

这条命令在调试类似"重启前到底发生了什么"的问题时简直是救命稻草。因为很多硬件相关崩溃会导致系统直接 reset,串口上没来得及来得及完整打印,pstore 保留的是上次运行时的尾巴。

另外,串口日志虽然直观,但毕竟带宽有限,日志量大的时候会丢。实际开发中,我一般用 hdc 连接设备,直接在 PC 上抓取完整日志:

hdc shell hilog -w start # 开始保存 hilog # 复现问题... hdc file recv /data/log/hilog .

这样既能保留完整日志,又不会被串口的 1.5Mbps 带宽限制影响。串口在这个过程中更多是扮演"保底"角色——如果系统已经卡到连 hdc 都连不上,那串口就是你唯一的观测手段。

3. 第二板斧:设备树——RK3568 的"多设备树"到底怎么选

3.1 一个板子为什么有几十个 dts:先搞清楚设备树在描述什么

在 OpenHarmony 的 RK3568 源码里,随便一翻就能看到一长串设备树文件。很多人第一次面对这个列表是懵的:同一个 SoC,为什么要搞出这么多 dts?

其实原因不复杂。RK3568 是一个通用 SoC,会被不同的硬件厂商做成不同的开发板和产品。有的板子用 eMMC 存储,有的用 SD/NAND;有的带 HDMI 输出,有的只有 MIPI DSI 屏;GPIO 的分配、电源管理芯片型号、音频 codec 型号也各不相同。设备树的作用,就是告诉内核"这块板子上的硬件到底长什么样"。

如果你选错了设备树,轻则某个外设不工作,重则系统直接起不来。之前我拿一块第三方 RK3568 核心板,套用了官方 EVB 的设备树,结果 U-Boot 起来以后内核一直 panic,报了一堆和 timer、interrupt 相关的错误。最后才发现核心板的 PMU 中断引脚和 EVB 板不一样,设备树里配的 GIC 中断号对不上,内核在初始化 stage 就崩了。

3.2 选择设备树的第一原则:看板卡名,不要看 SoC 名

很多新手选设备树只看"SoC 是不是 RK3568",这是最典型的错误。正确的做法是看设备树文件名的板卡代号,或者看文件内部的 model 字段。

arch/arm64/boot/dts/rockchip/rk3568-evb.dts arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dts arch/arm64/boot/dts/rockchip/rk3568-evb2-lpddr4-v10.dts

这里的 evb1、evb2、v10 都是有含义的。DDR 类型不同、PCB 版本不同,对应的 dts 也不同。如果手里是第三方板子,优先在官方 SDK 里找有没有同名或同系列的 dts;找不到的话,就以 EVB 为蓝本,对照原理图逐项修改。

我总结了一套选树步骤:

  1. 先确认板子的 DDR 类型和容量,这决定了内存节点的基础配置。
  2. 确认存储介质:eMMC、SD 卡还是 SPI NOR,对应修改 sdhci 或 dwmmc 节点。
  3. 确认 PMIC 型号,再看 pinctrl 里的电压配置是否正确。
  4. 对比原理图上所有 I2C 设备的地址,和 dts 中 i2c 节点的 reg 值是否一致。
  5. 最后确认显示和多媒体相关节点,这部分不影响启动,但影响功能验证。

3.3 从 U-Boot 到内核:实际加载的到底是哪个设备树

还有一个很实际的问题:你改了 dts,编译出 dtb,但板子启动时用的真的是这个 dtb 吗?在 RK3568 平台上,U-Boot 会根据硬件探测结果去选择 dtb,这个过程可能和你预想的不一样。

最直接的验证方法是看内核日志最前面的几行。比如:

[ 0.000000] Machine model: Rockchip RK3568 EVB2 LP4 V10 Board

如果这一行显示的不是你想要的板卡型号,说明 U-Boot 选了别的 dtb。此时可以检查 U-Boot 环境和分区里的 dtb 文件:

# 在 U-Boot 命令行下查看当前环境变量 printenv fdtfile

如果没有这个变量,试试在 U-Boot 里手动指定 dtb 索引,或者直接把固件打包时用的 dtb 换成你自己的。很多第三方开发板的烧录工具都支持单独烧录 dtb 分区,烧完以后记得在串口验证一次 Machine model。

3.4 运行时如何确认设备树生效:不要相信编译成功

编译成功只代表语法正确,不代表硬件匹配。我在调一个 I2C 触摸屏的时候,dts 里明明配了 I2C 地址和中断引脚,编译烧录也没报错,但触摸就是没反应。后来进系统一看,发现 probe 根本没有执行。

运行时核对设备树的最终标准,是看它解析出来的实际节点:

# 进入系统后查看 model 确认设备树版本 cat /proc/device-tree/model # 查看某个外设节点的状态 cat /proc/device-tree/i2c@fe5c0000/status

如果 status 是 disabled,说明这个节点在 dts 里被关掉了;如果节点存在但没有任何子节点,说明 I2C 总线上可能没有探测到设备。

还有一种常见情况:dts 里的 pinctrl 配置和实际硬件不匹配,导致某个外设的电源脚没有被拉高。这类问题在内核日志里通常表现为:

rk3x-i2c fdd40000.i2c: timeout, ip version: 0x0

看到这种 timeout,优先查这个外设的电源和复位引脚在设备树里是否配置正确,而不是急着怀疑芯片坏了。

这里要提醒一点:改完设备树后,尽量把 pinctrl、gpio 的申请日志打开,用动态调试确认每个引脚的复用状态:

echo 'file drivers/pinctrl/* +p' > /sys/kernel/debug/dynamic_debug/control

不过 RK3568 的板子有些调试接口默认没开,如果你发现 /sys/kernel/debug 目录为空,先在内核 defconfig 里打开 DEBUG_FS。

4. 第三板斧:崩溃定位与远程调试——比 printf 更高效的手段

4.1 hdc、FaultLogger 和崩溃现场

串口能解决"系统起不来",设备树能解决"外设不工作",但系统真正运行起来之后,最频繁的问题变成了"跑着跑着崩了"。这时候如果还用 printf 加日志的土办法,效率会非常低。OpenHarmony 提供了另一套链路:hdc 远程调试、FaultLogger 归档、工具链反查栈回溯。

hdc 是华为开发套件自带的设备连接工具,作用相当于 Android 的 adb。设备开启开发者模式并连接网络后,PC 上执行:

hdc list targets

能看到设备,说明连接通道是通的。接下来抓取崩溃日志:

hdc shell "hilog -x | grep FAULT"

OpenHarmony 的用户态崩溃通常会在 hilog 里留下类似这样的一行:

Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 1234

看到这一行,说明进程因为空指针解引用挂了。FaultLogger 还会把完整的回溯信息写到日志文件里,路径一般在/data/log/faultlog/faultlogger/下。

hdc shell "ls /data/log/faultlog/faultlogger/" hdc file recv /data/log/faultlog/faultlogger/xxx /tmp/

4.2 一次空指针崩溃的完整定位过程

说一个我实际处理过的案例。我们基于 OpenHarmony 写的一个系统服务,启动后运行几分钟必然崩溃,串口日志却看不出任何异常。第一次崩溃后我直接从 FaultLogger 拉出了回溯,发现崩溃点在某个 Native 函数的 memcpy 处,调用栈里有一个对象指针为 null。

接下来的问题是怎么把地址映射回代码行。OpenHarmony 的 Native 代码编译时如果不带符号,回溯里只有函数地址。我在编译服务时打开调试符号,然后用工具链自带的 addr2line 反查:

addr2line -e ./libmyservice.so 0x0000000000142b80

结果直接定位到源码文件第 37 行的 memcpy 调用。最后查出来,是某个配置项没有初始化,结构体里有一个空指针被一路传到了这里。整个过程从抓日志到改代码,不到半小时。换做以前纯靠 printf 加盲猜,可能得折腾一整天。

这里有个经验:在开发阶段,Native 服务尽量编出带符号的 so 文件,不要急着 strip。虽然体积大一点,但对于定位崩溃作用太大了。

4.3 内核态问题:用 ftrace 和 pstore 逼近现场

用户态崩溃有 FaultLogger 接住,内核态的 panic 和死锁处理起来就更麻烦一些。好在 RK3568 的 OpenHarmony 内核默认支持 ftrace,可以在不重新编译内核的情况下做函数跟踪。

内核 panic 的排查思路,首先是看串口上的崩溃现场,找 panic 关键字后面的调用栈。如果板子重启太快导致日志刷屏,就用 pstore 保存上次崩溃记录:

cat /sys/fs/pstore/console-ramoops-0

死锁类的问题更隐蔽,我遇到过内核线程卡死,现象是某个驱动不响应,但系统没有 panic。排查时用 ftrace 跟踪特定函数:

echo function_graph > /sys/kernel/debug/tracing/current_tracer echo 'func_name' > /sys/kernel/debug/tracing/set_graph_function echo 1 > /sys/kernel/debug/tracing/tracing_on

跟踪文件会变得很大,记得控制采集时间。

4.4 软件栈查不出问题时:逻辑分析仪和示波器的用武之地

千万不要忘了,OpenHarmony 开发毕竟还是硬件系统开发,不是纯互联网后端调 bug。软件日志和回溯都查不出问题的时候,外设总线上的电平信号才是最终真相。

我有一次调 I2C 外设,设备树配置、驱动代码、寄存器数值都检查过好几遍,I2C 读出来的数据还是全 0xFF。后来拿逻辑分析仪夹在 I2C 的 SCL/SDA 上,才看到 SDA 线上根本没有 ACK 信号,外设没有应答。查硬件原理图以后发现,是该外设的复位引脚被拉低了,一直处于复位状态。这个过程,你光看软件日志永远发现不了。

所以我的建议是:桌面上常备一个逻辑分析仪,哪怕是最便宜的那种,关键时候能省下很多瞎猜时间。很多软件上"看起来不可能"的问题,最终都出在物理层。

5. 三板斧组合实战:一个从启动崩溃到定位根因的完整案例

前面把三板斧分别讲了一遍,但实际项目里它们从来都是交替使用的。这里用一个我完整走通的案例,带你感受一下真实的排查链路。

5.1 第一阶段:启动卡死,串口定位到"内核还没起来"

接到一块新的 RK3568 定制板,第一次烧录 OpenHarmony 镜像后上电,屏幕无输出,串口日志停在:

Starting kernel ...

之后没有任何输出。按前面说的,先看 U-Boot 日志正常、DDR 初始化正常、内核被正常 load,说明问题出在内核镜像和设备树匹配上。

这一步不多想,直接怀疑设备树。因为板子拿到手的时候,厂商只给了一个"对应 SDK 默认配置"的说法,并没有明确说用的哪棵 dtb。我烧的固件是 SDK 里的默认配置,大概率用的是官方 EVB 的设备树,而这块第三方板子的硬件和 EVB 差别不小。

5.2 第二阶段:换设备树,启动继续推进但出现服务崩溃

找出厂商 SDK 里针对这块板子的 dts 文件,重新编译 dtb 并烧录。再上电,串口能看到完整的内核日志和 init 输出,说明设备树的坑已经过了。

紧接着新的问题出现。系统启动到一半,hilog 里出现一行 FATAL,某个系统服务反复崩溃重启。到这一步,第一板斧和第二板斧已经完成了它们的任务,接下来要换第三板斧上场。

5.3 第三阶段:FaultLogger 反查崩溃点并修复

通过 hdc 进入设备,把 faultlog 目录下的崩溃归档拉回 PC。回溯信息显示,崩溃发生在某个媒体相关服务里。addr2line 定位到源码后,发现是服务启动时尝试访问一个不存在的音频设备节点。这个节点在设备树里根本没配,服务却没有做空值判断,直接拿空句柄去调用底层接口,触发空指针崩溃。

修复方案分两步:先在设备树里把音频节点补上,并在服务代码里加上空值保护。重新编译烧录后,系统正常启动。

这个案例之所以有代表性,是因为它完整覆盖了三板斧的闭环——串口定位"卡在哪一阶段",设备树解决"硬件不匹配",崩溃分析和调试器解决"运行期挂了"。任何一个环节缺失,排查时间都可能翻好几倍。

6. 避坑清单与后续扩展:三板斧之外的实战心得

6.1 我反复踩过的几个坑

  1. 串口工具流控未关闭。看起来是小问题,但会导致日志时断时续,非常误导人。
  2. 盲目使用官方 EVB 设备树。RK3568 的 dts 是高度板级相关的,拿到第三方板子一定要核对原理图。
  3. 编译 dtb 后不验证 Machine model 字段。烧进去就以为一定生效,实际 U-Boot 可能加载了另一棵 dtb。
  4. 开发阶段提前 strip 了符号表。崩溃日志拿到手却无法反查代码行,白白浪费线索。
  5. hdc 连不上就放弃。很多时候是设备网络没配好,其实用串口进系统把网络配好就行。

6.2 一套稳定的日志保存习惯

我现在做 OpenHarmony 系统开发,会固定给设备增加一个开机自启脚本,把 hilog 和 dmesg 都落盘到 /data/log 目录,并按大小滚动。这样遇到问题,哪怕没有串口,也能从设备本地把日志拉出来分析。具体的做法是在 init.cfg 里加一个服务,启动 log_daemon,配合 hilog 的日志文件模式和 logrotate 机制。

如果是多个设备联调的分布式场景,我会让每台设备都把日志文件同步到一台主机上,统一管理。OpenHarmony 的分布式框架调试本身就复杂,如果底层日志都是分散的,排查一个跨设备调用问题几乎无从下手。

6.3 把硬件调试当成系统开发的基础功

回头看,这三个方向——串口、设备树、崩溃调试——其实不是三个独立技巧,而是一套完整的贯穿系统链路可观测性方法。硬件调试的核心,说穿了就是一句话:让系统的每一个关键状态都有迹可循。串口让启动过程可观测,设备树让硬件描述可验证,错误日志和调试器让崩溃现场可还原。三者互相支撑,缺一环,你排查问题的路径就会变得漫长。

在 RK3568 这一类开发板上做 OpenHarmony,几乎每天都会面对"为什么这里不工作"的灵魂拷问。一开始一头雾水很正常,关键是尽早建立这套调试框架。等到你的第一反应不再是"换个板子试试",而是顺手接上串口、熟练地定位设备树节点、在 hilog 里找崩溃栈的时候,你就已经真正迈过系统开发的门槛了。后续无论你转向内核裁剪、驱动移植,还是分布式应用开发,这三板斧都会一直陪着你。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询