OpenHarmony硬件调试三板斧:串口日志、内核崩溃与系统态实战
2026/9/24 22:27:25 网站建设 项目流程

1. 先搞清楚要打交道的硬件环境

1.1 面对RK3568的设备树,先别慌着挑

做OpenHarmony开发,很多人第一道坎不是编译,而是设备树文件怎么选。打开kernel/linux/arch/arm64/boot/dts/rockchip/目录,密密麻麻一堆dts,RK3568相关的就有十几个:rk3568-evb1-ddr4-v10.dts、rk3568-evb2-lp4x-v10.dts、rk3568-nvr-demo.dts、rk3568-evb1-ddr4-v10-linux.dts这类。光看名字长得都差不多,网上搜一圈,答案也五花八门,越看越懵。

实际情况是,OpenHarmony在RK3568上维护了多条设备树,分别对应不同的开发板形态和内存配置。你手上的板子到底是EVB1还是EVB2,用的是DDR4还是LPDDR4x,这直接决定了加载哪套dts。选错了,最典型的症状就是内核起来后外设全部掉线,或者干脆起不来。判断方法也简单:看板子的丝印,有的板子直接印着EVB1或者EVB2;没有丝印就查你买板子时的商品详情页,卖家通常会标注开发板型号;还不行就看内存颗粒的丝印,DDR4和LPDDR4x的颗粒封装和丝印命名规则完全不同。

还有一个坑:OpenHarmony的dts后缀里带linux和不带linux的差异。带linux后缀的,是给Linux内核用的,里面裁剪了OpenHarmony特有的HDF节点;不带后缀的,才是给OpenHarmony标准系统用的,包含了HDF设备描述符,比如gpio、i2c、spi、uart的platform驱动注册。你从鸿蒙官方的device/board目录编译出来的内核镜像,用的是不带linux后缀的那套。很多人图省事,直接从Rockchip的Linux BSP里抄设备树过来用,结果HDF驱动一个都加载不上去,就是这个原因。

1.2 x86电脑版OpenHarmony能帮你什么,不能帮你什么

关于“电脑版x86 OpenHarmony”,这也是社区里最近问得特别多的问题。严格来说,OpenHarmony标准系统目前官方适配的x86平台是有的,但生态和资料相比RK3568少得多。我之前在一台老笔记本上装过x86版,体验是这样的:系统能启动,桌面能进,多窗口能拖拽,基础的应用能跑,但一旦涉及硬件操作,比如GPIO点灯、I2C读传感器、SPI驱动屏幕,基本就歇菜了。因为x86平台上的HDF驱动适配太少,很多外设框架根本没有对应实现。

那x86版的价值在哪?我觉得有三个场景特别实用。

一是学习系统架构和IDE开发流程,不用买开发板,在你的电脑上就能跑起系统,熟悉hdc命令、应用签名、分布式软总线这些纯软件概念;二是驱动逻辑的先行验证,如果你的驱动本身和硬件耦合度不高,比如纯计算型的字符设备驱动,可以先在x86的qemu环境里把逻辑跑通,再移植到ARM板子上;三是做应用层联调,开发分布式应用的时候,一个设备是x86模拟器,一个设备是RK3568真机,正好可以测试跨端迁移和协同。

但要注意,x86平台绝对不能替代真机测试。时序、中断、时钟频率、DMA、电源管理这些硬件强相关的特性,只有在真实的ARM板子上才能验证。我见过有人拿着x86模拟器的测试结果,信誓旦旦说驱动没问题,结果烧到RK3568上连模块都加载不起来。模拟器只能帮你验证逻辑,不能帮你验证硬件。

2. 第一板斧:串口日志

2.1 串口是硬件调试的命根子,这句话不是客套

在OpenHarmony的嵌入式开发里,我敢说80%的硬件问题排查,第一步都是从串口日志入手的。原因很简单:系统从上电到运行,经历了U-Boot引导、内核启动、init进程、HDF驱动加载、服务拉起这几个阶段,任何一步出错,现象可能只是板子黑屏或者反复重启,但日志会告诉你具体卡在哪里。没有串口日志,你就像在黑屋子里摸开关,只能靠猜。

我看过太多新手拿着一块新板子,烧完固件上电,发现屏幕不亮,就开始怀疑屏参、怀疑背光、怀疑电容触摸,折腾半天。其实只要接上串口,一条“lcd_power_on: regulator enable failed”或“drm_panel_get_modes: panel not ready”就能让你少走三个小时弯路。

所以,OpenHarmony开发板上的调试串口,就是你与系统对话的窗口。在RK3568平台上,调试串口默认是UART2,四个引脚分别是GND、TX、RX、VCC。需要注意的是,有些板子的调试串口引脚和蓝牙串口、GPS串口是复用的,必须看原理图确认,别接错。

2.2 接线、波特率和日志等级,三件事一次讲清楚

接线这块,最保险的做法是USB转串口模块加杜邦线。GND必须共地,这是串口通信的前提,不共地你会收到一堆乱码。TX和RX交叉连接——板子的TX接模块的RX,板子的RX接模块的TX。VCC那个脚,不接也能通信,因为USB转串口模块一般是自供电的,板上串口也有自己的电平转换电路,VCC只是给外部设备供电用的,强行接上反而可能电平冲突。

波特率方面,OpenHarmony标准系统在RK3568上常用的串口波特率是115200,U-Boot阶段也是115200。连接好了以后,在宿主机上用minicom或者PuTTY打开串口,设置好波特率,重新给板子上电,就能看到完整的开机日志。

日志等级这块也值得说。系统跑起来之后,串口上默认打印的日志级别是有限的。内核日志可以通过/proc/sys/kernel/printk来控制,默认值往往是“4 4 1 7”,意思是控制台日志级别为4,也就是KERN_WARNING及以上才会打印。日常调试想看更多信息,可以用root权限执行:

echo "7 4 1 7" > /proc/sys/kernel/printk

把控制台级别提到7,就可以看到KERN_DEBUG级别的日志,内核崩溃前的最后几条信息,往往就在这个级别里。

OpenHarmony用户态的hilog日志,默认串口也是不打印的,需要单独开启。在Shell里执行:

hilog -w start

然后就可以用hilog命令过滤查看。实际调试中我习惯把hilog和串口日志分开用:串口看内核和硬件初始化,hilog看服务框架和驱动加载。两条线同时开着,一张桌子两台电脑或者一个电脑分屏,信息互补,排查效率最高。

2.3 实战现场:卡在启动流程的哪个环节

拿到一块新板子,接好串口,上电,你会看到一串输出。怎么快速判断系统跑到了哪一步,这是个经验活,我总结了一个简单的判断链条。

第一步,看有没有U-Boot输出。如果串口完全静默,大概率是上电时序或DDR初始化失败,这时候优先检查电源指示灯、核心板供电、DDR配置。如果是U-Boot有输出但卡在“Starting kernel ...”之后没有后续,可能是内核解压失败、设备树加载失败、或者内核启动早期挂死,需要在U-Boot环境里排查bootargs和bootcmd。

第二步,内核起来之后,日志会密集出现,以时间为前缀,比如“[0.000000] Booting Linux on physical CPU 0x100”开头的是一阶段,看到“Run /init as init process”就代表内核基本完成,进入用户态。

第三步,看init和服务拉起。OpenHarmony系统启动到桌面,中间有大量的服务启动日志。一个常见的坑是,内核跑得好好的,但桌面起不来,日志里反复报某个HDF驱动加载失败或者某个服务crash。这时候不一定是硬件坏,很可能是设备树里节点属性写错,导致驱动拿不到中断号或寄存器地址。这类问题,串口日志里通常能直接看到err字样,顺着查就行。

我自己排查时习惯把串口日志全程录下来,存成log文件,回头出问题了翻log比现场盯屏靠谱得多。用minicom的话,启动前执行:

minicom -D /dev/ttyUSB0 -b 115200 -C /path/to/board.log

就是挂上日志文件,上电,跑几轮复现,然后拿log文件慢慢分析。

3. 第二板斧:内核态崩溃分析

3.1 从panic和Oops信息里,怎么快速锁定问题点

串口日志里最让新手头疼的,是突然刷屏的内核Oops或者panic。满屏的寄存器值、栈回溯,看着吓人,其实信息量很大。内核崩溃在OpenHarmony设备上常见的几类:空指针解引用、非法内存访问、内核栈溢出、原子操作休眠、内存池越界。

处理这类问题的常规姿势分三步。

第一步,找到“Unable to handle kernel”或“Kernel panic - not syncing”这一行,它下面通常直接跟着崩溃地址和错误类型。比如:

Unable to handle kernel paging request at virtual address 00000000000000a8

这说明访问了地址0xa8,一个明显的内核结构体偏移访问,极可能是某个结构体指针为NULL,然后取了偏移量。顺着往上翻几条日志,找到最后一次调用这个地址的函数是哪个,问题范围就缩小了。

第二步,看调用栈回溯,也就是Call trace那一段。从下往上,最下面的函数是最早的调用者,最上面的是崩溃点。一般看崩溃点和它上两层的函数就能定位到是哪个驱动或者子系统。比如Call trace里有rk_gpio_setmtk_i2c_xfer,那基本可以确定是GPIO子系统在操作I2C引脚时出问题了。

第三步,确认是逻辑错误还是硬件问题。如果是逻辑错误,代码里判空、锁保护、异步访问的竞争条件都有可能;如果是硬件问题,最典型的表现是读寄存器的值始终是0xdeadbeef或全F,这种多半是时钟没开、电源域没上电、或者地址映射错误。

3.2 让内核崩溃信息更可读的三个技巧

内核默认的打印信息有时不够详细,遇到疑难杂症,就得靠几个开关把诊断信息拉满。

第一个技巧,打开更详细的内核调试参数。在内核命令行里追加ignore_loglevel,可以让内核把所有级别的日志都输出到串口,省去每次手动改printk的麻烦。bootargs里加上panic_on_oops,可以让内核在Oops后直接panic,方便抓取完整崩溃现场。

第二个技巧,用addr2line把地址翻译成代码行号。以内核编译时的System.map为参考,拿到崩溃PC值后,执行:

aarch64-linux-gnu-addr2line -e vmlinux -f -C 0xffffff8008123456

就能得到对应的函数名和源码行号。要注意的是,vmlinux必须和正在运行的固件是同一份编译产物,镜像烧录之后重新编译过都不行,否则地址对不上,分析出来全是错的。这块我踩过坑,所以现在每次给板子烧完新固件,都把对应的vmlinux和System.map归档保存,按日期和编译哈希命名,出问题直接翻对应归档,非常省事。

第三个技巧,内核配置里打开DEBUG_INFO和PROVE_LOCKING。在编译OpenHarmony内核时,kernel/linux/build目录下的配置文件里,把这些选项选中,内核体积会变大,但锁死、内存越界这类问题能给出更清晰的提示。这属于平时为调试成本买单,关键时刻真能救命。

3.3 出了panic别急着断电,先干这几件事

很多人在板子panic之后的第一反应是断电重启,这是最要不得的习惯。内核panic现场转瞬即逝,一旦断电,唯一能分析问题的线索就断了。

正确的做法是:如果串口还活着,先把崩溃前的完整日志保存下来,至少包含从崩溃前50行到Call trace结束的全部内容;如果日志太多,可以临时把串口终端行数拉高,或者用前面提到的-C参数落盘。然后,拍个照也行,把PC值、LR值、Call trace关键行记下来。最后再断电,重新上电复现问题,看是不是稳定复现。稳定复现的问题好办,砍掉一半外设、屏蔽一半驱动,二分法排查,总能找到元凶。不稳定复现的,多半是时序竞争、电源波动、电磁干扰,这类问题三板斧只能缩小范围,剩下的真得靠示波器和逻辑分析仪了。

4. 第三板斧:系统态观测与操作

4.1 用hdc shell直接操作设备:比你以为的强得多

串口日志和分析崩溃固然重要,但真正动手验证硬件通路时,OpenHarmony的hdc工具是我最常用的。

hdc相当于Android的adb,连接开发板后,执行:

hdc shell

就能进入板子的Shell环境。很多人只知道用hdc file send推文件、hdc install装应用,其实在硬件调试场景里,hdc shell里能干的事多得很。

比如要查某个I2C设备在不在总线上,可以用:

ls /dev/i2c-*

看到节点存在,再用i2cdetect之类的工具扫描设备地址:

i2cdetect -y -r 0

地址能扫出来,说明物理链路基本是通的。扫不出来,优先查硬件连接和上拉电阻,其次查设备树里i2c节点有没有被正确使能,最后看驱动有没有注册成功。

GPIO的操作更是家常便饭。想快速验证某个GPIO能不能输出高低电平,可以这样:

echo 25 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio25/direction echo 1 > /sys/class/gpio/gpio25/value

如果板子上接了LED或者示波器,一根线一根线地验证,比闷头看代码定位快得多。这里要提醒一下:GPIO编号和SoC的引脚号不是一回事,中间可能隔着GPIO bank基址换算,最好先在设备树里确认好gpio控制器基址和bank号,再算对应gpio编号,否则容易操作成别的引脚,导致外设误动作。

4.2 寄存器级别的验证:devmem是真的好用

当你想确认某个外设寄存器是不是按预期翻转时,devmem是最直接的工具。

OpenHarmony标准系统自带devmem命令,用法是:

devmem 0xff010000 32

这条命令会读取0xff010000地址处的32位值。写寄存器也很简单:

devmem 0xff010000 32 0x1

这相当于直接往该寄存器写0x1。调试时,我经常在驱动代码里临时加打印,打印值反复烧录,效率太低。有了devmem,就能一边跑系统一边操作寄存器,比如先读时钟控制寄存器确认时钟有没有开,再读电源寄存器确认电源域状态,最后直接往数据寄存器写值,看外设有没有反应。整个过程在Shell里就能完成,不用重新编译内核和驱动,调试周期缩短好几倍。

但devmem有风险。它绕过内核直接访问物理地址,如果操作到了关键系统寄存器,比如DDR控制器、中断控制器,分分钟panic。所以用之前先确认好寄存器的地址和位定义,该读的先读,别上来就写。修改前,把原始值记下来,万一出问题还能恢复现场。另外,OpenHarmony对devmem有权限要求,必须root用户,hdc shell进去默认就是root,问题不大。

4.3 HDF驱动加载失败,系统态怎么看

OpenHarmony的硬件驱动框架HDF,是硬件与系统之间的中坚层。外设不工作时,除了看串口日志,系统里也有几个地方能帮你定位。

HDF驱动的加载状态,可以查:

cat /sys/kernel/debug/hdf/device_manager

这个节点会列出所有已注册的HDF设备,以及它们的挂载状态。如果设备名出现了,但状态是failed或者pending,说明驱动注册有问题,顺着查代码或设备树;如果设备名压根不出现,说明设备树里的匹配节点没生效,大概率是compatible属性字符串对不上。

还有/dev/hdf/目录下的设备节点,可以用于用户态与内核态驱动的交互。调试驱动时,可以在用户态写一个小测试程序,用ioctl方式调用驱动的接口,验证数据通路是否正常。这种“最小复现”的方式,比在完整系统里层层排查要快得多。

5. 三板斧组合打法:一个真实问题的完整排查记录

5.1 案例:WiFi模组始终无法识别

之前在一款RK3568的板子上,WiFi模组始终无法识别,系统起来后WiFi开关是灰色的,怎么点都没反应。当时我就用三板斧走了一遍完整流程。

第一板斧,接上串口看日志。开机日志里看到hdf wifi device not found,然后hwifi服务起来后,反复报wifi_hal: failed to connect to driver。这说明硬件本身可能已经探测到,但驱动层没有正确绑定。

第二板斧,看内核态和HDF框架。从串口全量日志里搜wifisdio这些关键字,发现SDIO总线上根本没有扫描到设备:

mmc1: error -110 whilst initialising SDIO card

-110是ETIMEDOUT,说明SDIO通信超时了。这时候我再用hdc shell进系统,看sdio总线状态:

cat /sys/bus/sdio/devices/sdio:*

结果里面是空的,没有设备。这说明SDIO物理层都没通。

第三板斧,用devmem验证寄存器级别的链路。查了RK3568的数据手册和SDIO控制器基址,用devmem读SDIO的时钟控制寄存器和电源控制寄存器,发现时钟寄存器的值全是0xdeadbeef,电源寄存器也没读到有效值。结合串口里的超时错误,定位到是SDIO控制器的电源域没使能。最终查设备树,发现底板的电源管理节点里,WiFi的供电GPIO引脚的pinctrl配置和实际硬件不匹配,导致WiFi模组一直没上电。改掉设备树里的pinctrl配置,重新编译、烧录,WiFi一次识别成功。

这个案例非常典型:从串口日志发现错误,到系统态判断总线状态,再到寄存器级确认电源链路,最后反推到设备树配置。三板斧每一板都发挥了作用,缺一个都不好使。

5.2 常遇到的问题和排查方向速查表

我自己整理了一个速查表,每次遇到OpenHarmony硬件问题都先过一遍。

现象可能原因优先排查方向
串口完全无输出调试串口接错、波特率不对、DDR初始化失败查原理图确认调试口引脚,试波特率,检查电源时序
U-Boot卡住存储介质损坏、bootargs错误检查SD卡/eMMC,确认U-Boot环境变量
内核启动后屏幕黑LCD电源/背光未使能、屏参配置错误串口搜drm/lcd/pwm关键字,查设备树lcd节点
WiFi/蓝牙识别不到SDIO电源未上、compatible不匹配、固件缺失串口搜mmc/sdio报错,看HDF设备列表,查电源树
触摸屏无响应I2C链路不通、中断号配置错误i2cdetect扫描地址,查GPIO中断映射
反复重启内核panic、看门狗复位、DDR不稳定抓panic现场,查watchdog配置,检查供电质量
hilog日志为空hilog未启动、级别过滤执行hilog -w start,检查hilog配置

这张表不能解决所有问题,但能帮你在面对未知故障时,从乱糟糟的第一现场快速理出头绪,不至于一上来就乱翻代码。

5.3 三板斧之外的护身符:留好现场证据

调试硬件问题,过程和破案很像。现场证据越完整,破案速度越快。我强烈建议养成几个习惯。

每次烧录新固件后,把内核镜像、设备树、vmlinux、System.map、对应版本的代码commit号,以及本次启动的完整串口日志,按日期归档保存。目录结构可以这样组织:固件发布时间/内核镜像、设备树、内核符号表、启动日志、本次改动说明。别嫌麻烦,等到问题复现时,你会庆幸有这套档案。

编译内核时,如果空间允许,保留debug信息和不优化选项的构建副本。很多崩溃地址,只有在带调试信息的vmlinux里才能转换成有意义的源码行号。线上跑release版,本地留debug版,二者保持同一代码基线。

用到设备树时,每次改动都做对比和注释。设备树是所有外设的“户口本”,说错一句,设备就“黑户”了。习惯性把每次设备树改动的原因写成注释,哪怕只是改了一个pinmux配置,也写明“为什么这么改、改完解决什么问题”。过三个月再回头看,这些注释就是最宝贵的技术沉淀。

6. 写在最后的一点提醒

硬件调试是个耐心活,三板斧看着简单,真正用熟需要大量实战积累。我自己刚开始接触OpenHarmony时,也经历过对着串口日志一脸懵的日子,那时候不会看Call trace,不会用devmem,每次出问题只能靠猜,效率极低。后来慢慢把“串口日志、崩溃分析、系统态操作”这三套工具练成了本能反应,遇到问题不再慌,按顺序三板斧来,大多数问题都能在半小时内锁定范围。

给还在坑里的朋友一个建议:刚开始不要贪多,把一条串口线、一块开发板、一份数据手册研究透,比看十篇教程都有用。遇到问题先记录,再分析,最后动手,不要一上来就乱改代码、乱动硬件。每一板斧都有自己的位置,顺序也很重要——先串口定位方向,再崩溃信息缩小范围,最后用系统态操作验证猜测。三管齐下,硬骨头也能啃下来。

这个系列后续我还会继续写设备树配置的具体踩坑、HDF驱动开发的入门套路,以及如何把OpenHarmony跑在自己画的核心板上,有兴趣的朋友可以关注后续内容。也欢迎在评论区聊聊你调试时遇到的最头疼的问题,说不定下一个主题就能帮到你。

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

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

立即咨询