硬件调试三板斧—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
会刷之后,日常就三件事。今天把串口、hdc、编译机的职责分开——系统还没起来时,hdc 不会有设备,只能靠串口。
板子黑着,USB 线插着,你不知道该骂哪一头。Windows 上 DevEco 说 hdc list targets 空,Linux 上 ./build.sh 刚编完,串口工具里一片乱码。三件事其实是三条独立的路,任何一条都能单独活着:没有桌面,串口照样能出 sh;没有串口,hdc 照样能推文件;没有 hdc,整包照样能刷。把它们缠成「插上 USB 就全通」,排障会从第一分钟开始猜。
这块 RK3568 板上,调试控制台是 UART2,节点 ttyS2,1500000 8N1。hdc 走 OTG 口上的 hdcd。编译在 Ubuntu,应用和多数 hdc 操作在 Windows。两台电脑一台板,是这套系统最省事的拓扑,也是 hdc 端口被抢、文件不知道在哪台机器上的根源。
产品名 rk3568_evb。官方 hdc 说明:使用指导。



1. 三条线,三种职责
flowchart LR
L[Ubuntu 编译机] -->|共享盘镜像 / .ko / hap| W[Windows]
W -->|OTG ROCKUSB / hdc| B[RK3568 板]
W -->|USB 转串口 1500000| B
L -.->|可选:同网 nc| B
| 线 | 插哪 | 干什么 | 系统没起来时还在吗 |
|---|---|---|---|
| USB 转串口 | 底板 UART / DEBUG,UART2 | U-Boot、printk、`#` 提示符 | 在。BootROM、DDR 训练也走这里 |
| USB 数据线 | 底板 **OTG / DEVICE**,不是 Host | Maskrom 刷 eMMC;起来之后 hdc | Maskrom 阶段是 ROCKUSB,不是 hdc |
| 读卡器 | 电脑,不插板 | 给 SD 写整包 | 与板是否开机无关 |
Host 口给 U 盘、USB 相机、USB 网卡。OTG 口反过来,板当从机。插错的表现:电脑毫无反应,设备管理器不刷新,你却去重装驱动。充电线不能传数据,同一症状。换线、换口、认丝印,十分钟;怀疑芯片,十天。
串口是 3.3 V TTL。5 V 模块有可能打坏 SoC 脚。GND 对 GND,TX 对板 RX,RX 对板 TX。接反的表现是完全没字,和波特率错、没供电很难一眼分开。先确认转接芯片在设备管理器里出现,再对调 TX/RX 试一次。
2. 串口:1500000 8N1,回车出 sh
1500000 不是笔误。芯片 UART 时钟分频在这块板上的常用值,U-Boot 和内核约定好了。填 115200,电平和连线全对,窗口里只是乱码或空白。很多人在这里耗一个晚上。
| 项 | 值 | 填错会怎样 |
|---|---|---|
| 设备 | UART2 / ttyS2,Windows 上是 COMx | 打开失败或打开了没数据 |
| 波特率 | **1500000** | 乱码,像板子没起 |
| 数据位 | 8 | 偶发错字 |
| 校验 | None | 大量错字 |
| 停止位 | 1 | 错字 |
| 流控 | **None** | 默认 RTS/CTS 时,能收几个字然后卡住,回车发不出去 |
板子 UART2 没接 RTS/CTS。工具若默认硬件流控,TX 等一个永远不来的 CTS,看起来像「串口只能看不能敲」。关掉流控。
Windows 上 PuTTY、MobaXterm、安全 CRT 都可以。Linux 上:
# 查看转接芯片
ls -l /dev/ttyUSB* /dev/ttyACM*
# picocom
sudo picocom -b 1500000 /dev/ttyUSB0
# 退出:Ctrl-A Ctrl-X
# 或 screen(有的发行版 screen 对非标准波特率不友好,优先 picocom)
sudo picocom -b 1500000 --omap crcrlf /dev/ttyUSB0
上电后正常顺序:
DDR ... V1.16 ...
U-Boot ...
Hit any key to stop autoboot
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0000000000 ...
...
#
内核起来之后,init 拉 console 服务。公版 console.cfg 常是 disabled:1 加 ondemand:true,默认不起。板级必须在 init.rk30board.cfg 的 boot 段写:
start console
写在 init.rk3568.cfg 里等于没写。ohos.boot.hardware=rk30board,init 只 import 这个后缀。写对了,敲回车出现 # 或 / #。
# uname -a
Linux localhost 5.10.xxx #1 SMP PREEMPT ... aarch64
# param get ohos.boot.hardware
rk30board
# cat /proc/cmdline
console=ttyS2,1500000n8 androidboot.hardware=rk30board ...
噪声多、刷屏影响看字:
# echo 1 > /proc/sys/kernel/printk
排完再改回去,或重启恢复。dmesg 的 ring 仍在,只是不再往控制台喷。
串口能看、敲回车没人理,按这个顺序排,不要先换线:
- 流控是否关掉
- TX/RX 是否接反(能看不能敲,经常是 TX 没接到板 RX,板的 TX 倒是接到了你的 RX)
ps | grep sh有没有 console 拉起的 shellgrep start /vendor/etc/init.rk30board.cfg
console 服务对应 /system/bin/sh。SELinux enforcing 时也可能把控制台卡住,这块板 bring-up 常用 enforcing=0。
3. 测其它 tty:microcom,别占用调试口
UART2 是调试控制台。外设模组挂在 uart0 / uart3 / uart7 / uart8 上,对应 /dev/ttyS0、ttyS3、ttyS7、ttyS8。在已经有 # 的那条调试口里,用 microcom 打开另一条。
板上 toybox 带 microcom:
# ls -l /dev/ttyS0 /dev/ttyS3 /dev/ttyS7 /dev/ttyS8
# cat /proc/tty/driver/serial
/proc/tty/driver/serial 里没有 0: 这一行,说明 uart0 没 probe,节点即便 ls 看得到也可能是假文件。先把设备树和抢脚处理完,再谈收发。
原始 8N1、无流控,115200:
# microcom -s 115200 /dev/ttyS7
9600(不少 GPS / LoRa 模组):
# microcom -s 9600 /dev/ttyS7
退出:Ctrl-X。有的版本是 Ctrl-A Ctrl-X,先试前者。
不要 cat /dev/ttyS7。cat 不设 termios,波特率、raw 模式都是默认,二进制帧会被 canonical 处理切碎。也不要 echo AT > /dev/ttyS7 当调试手段——echo 不设波特率,还可能在节点不存在时建假文件。
自己写一个最小收发,比 microcom 好留日志。用户态必须 32 位 musl:
/* uart_raw.c 板上: /data/uart_raw /dev/ttyS7 115200 */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
static speed_t to_speed(int b)
{
switch (b) {
case 9600: return B9600;
case 115200: return B115200;
case 1500000: return B1500000;
default: return B115200;
}
}
int main(int argc, char **argv)
{
if (argc < 3) {
fprintf(stderr, "usage: %s /dev/ttySN baud\n", argv[0]);
return 1;
}
int fd = open(argv[1], O_RDWR | O_NOCTTY);
if (fd < 0) {
perror("open");
return 1;
}
struct termios t;
if (tcgetattr(fd, &t) < 0) {
perror("tcgetattr"); /* Not a tty = 假文件或没 probe */
return 1;
}
cfmakeraw(&t);
speed_t sp = to_speed(atoi(argv[2]));
cfsetispeed(&t, sp);
cfsetospeed(&t, sp);
t.c_cflag |= CLOCAL | CREAD;
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = 10; /* 1s */
if (tcsetattr(fd, TCSANOW, &t) < 0) {
perror("tcsetattr");
return 1;
}
char buf[256];
for (;;) {
int n = read(fd, buf, sizeof buf);
if (n > 0)
write(STDOUT_FILENO, buf, n);
}
}
tcgetattr 报 Not a tty:ls -l 看是不是 -rw 普通文件。是就删掉,回头查为什么驱动没建字符设备。
调试口自己不要用 microcom 打开 /dev/ttyS2。你正在这条口上敲命令,再抢它,shell 和 microcom 互相吃字符,看起来像板子死了。
4. hdc:设备号、shell、传文件
系统进到用户态,hdcd 起来,OTG 插着 Windows(或 Linux),电脑侧:
hdc kill
hdc start
hdc list targets
应打出一串十六进制,例如:
ab0122454d41325245135dbd94d6bc00
Empty 或空白:hdcd 没起、线插在 Host、驱动认成了别的设备、两个 hdc server 在抢。板上:
# ps | grep hdcd
# netstat -lptn | grep 8710
常用命令(官方文档更全,下面是每天用的那几个):
hdc shell
hdc shell "uname -a"
hdc shell "param get ohos.boot.hardware"
hdc file send D:\ohos\boot_linux.img /data/local/tmp/boot_linux.img
hdc file recv /data/local/tmp/p4.read D:\ohos\p4.read
hdc install D:\hap\entry-signed.hap
hdc uninstall com.example.foo
hdc hilog
hdc hilog -x
hdc fport ls
hdc tmode port
hdc target boot
hdc shell 进的是板上的 sh,和串口那个 console 不是同一个会话,但看到的文件系统是同一份。串口被 printk 刷屏时,用 hdc shell 更干净。
file send 远端用 /data/local/tmp/。只读分区 send 过去可能报成功、复位后消失。大文件看大小:
hdc shell "ls -l /data/local/tmp/*.img"
和主机 dir 的字节数比,不够就重发,不要 dd 半截 system.img。
Windows 上 hdc.exe 常见路径:
C:\Users\<you>\AppData\Local\OpenHarmony\Sdk\11\toolchains\hdc.exe
DevEco 也带一份。两份同时跑,就是下一节的端口问题。
5. hilog 和 dmesg 不是一个桶
内核 printk 进 dmesg,也进串口(级别够的话)。用户态 SA、HDF host、ArkTS、相机 HAL,走 hilog。你在 dmesg 里找 HiLog 标签、在 hilog 里找 gpio-wdt probe,都会觉得「驱动没打日志」。
# dmesg | tail -50
# dmesg | grep -iE 'hdmi|vop|gt911|i2c|uart'
# hilog | grep -i hdf | head
hdc 上:
hdc hilog > D:\ohos\hilog.txt
hdc shell "hilog -x"
hdc shell "hilog -b D"
dmesg 的 ring 大约几百 KB,开机 audit 噪声能把它冲掉。关心 probe 失败时,起来之后立刻抓,不要先玩十分钟再翻。
# dmesg -c > /data/local/tmp/boot.dmesg
# hdc file recv 之前先在 hdc 里把文件拉走
区分口诀:
| 你在查 | 用谁 |
|---|---|
| 驱动 probe、-22、pin already requested | dmesg |
| `/sys` `/dev` 节点有没有 | 串口或 hdc shell |
| HDF Bind/Init、HDI、Ability、相机 pipeline | hilog |
| init 挂载、fstab wait | 串口(init 往 console 打)+ `hilog |
相机预览花屏那类问题,内核 uvcvideo 枚举成功只说明 USB 层活着,花屏在用户态节点里,hilog 才看得到。LED 不亮、uart0 没 tty,先 dmesg。
6. 两台电脑一台板
日常拓扑:
Ubuntu
源码、./build.sh、pack、增量 make M=、sign-file
产物放到共享盘 /mnt/hgfs/share/ 并写 md5
Windows
DevEco 编 hap、签名、hdc、串口工具、SDDiskTool、RKDevTool
从共享盘取 sd_image.img / .ko / .hap
RK3568
OTG 插 Windows
串口插 Windows
以太网可选,给 nc 拉文件
为什么不把 hdc 放 Linux、把编译放 Windows:标准系统现阶段不在 Windows 上编内核;hap 的 IDE、瑞芯微刷机工具、DriverAssistant,Windows 更省事。OTG 同时插两台电脑做不到。认这一条:板的 USB 调试只属于 Windows。
Linux 编出来的 .ko 要上板:
Ubuntu --共享盘--> Windows --hdc file send--> /data/xxx.ko --insmod-->
板和 Ubuntu 同网段、板上有 nc 时,可以跳过 Windows:
# Ubuntu
python3 -m http.server 8000 --bind 0.0.0.0
# 板上没有 wget,用 toybox nc 拼 HTTP 很脆,ftpget 若在:
# ftpget -u user -p pass 192.168.3.10 driver.ko driver.ko
多数时候共享盘更稳。源码不要放共享盘上编,只丢产物。拷完两端对哈希,不对就不要 file send:
# Ubuntu
md5sum /mnt/hgfs/share/boot_linux.img /mnt/hgfs/share/resource.img \
/mnt/hgfs/share/vendor.img /mnt/hgfs/share/system.img
certutil -hashfile D:\ohos\boot_linux.img MD5
certutil -hashfile D:\ohos\resource.img MD5
Windows 资源管理器能看见文件,不等于字节完整。hgfs 大文件偶发拷残,大小对、哈希不对时再拷一遍。
Windows 侧 hdc shell 里管道要引号包死,否则 cmd 自己吃掉 |:
hdc shell "dmesg | grep uart"
hdc shell "cat /proc/tty/driver/serial"
少了引号,Windows 会本地 grep,然后说找不到文件。
7. HDC_SERVER_PORT:两个 server 抢一台板
DevEco 启动时会拉起自己的 hdc server。你在 cmd 里再 hdc start,默认都听 8710。后启动的那个把前一个挤掉,或者两个都在、设备挂在你没在看的那一个上。表现:
- DevEco 能装 hap,cmd 里
hdc list targets空 - cmd 能 shell,DevEco 一直 Connecting
list targets闪一下又 Empty
处理:同一时刻只留一个 server。命令行要用时,先关掉 DevEco 的调试服务,或给命令行换端口。
REM 只给这份 cmd 用 18710,DevEco 继续 8710
set HDC_SERVER_PORT=18710
hdc kill
hdc start
hdc list targets
设备只能连一个 server。板插着的时候,先 hdc list targets 看它在哪边,再在那边操作。不要两个窗口同时 hdc kill。
Linux 编译机若也装了 hdc、也插过这根 OTG(虚拟机直通 USB),会再出现第三个 server。虚拟机 USB 直通一旦勾上这根线,Windows 侧设备管理器里板子会消失。调试期不要把 OTG 直通给 Ubuntu,除非你决定 hdc 全部改去 Linux。
看电脑侧谁在听:
netstat -ano | findstr 8710
PID 对到任务管理器,多余的 hdc 进程结束掉。
8. 没有 shell 的时候:late_initcall 诊断
hdc 没起来、console 服务没开、你又不能在板上敲命令——早期 bring-up 或某次 init 崩在一阶段,会落到这个状态。诊断不能做成「我发你一个用户态程序」,没有进程能跑它。做成内核模块,late_initcall 在大部分驱动 probe 之后跑,pr_err 打到串口。串口只收不发时,这是唯一的读数手段。
骨架(板级 device/board/rk/rk3568_evb/kernel/diag_i2c.c,构建时拷进 drivers/misc/):
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#define TAG "BOARD_DIAG: "
static void diag_work_fn(struct work_struct *w)
{
struct i2c_adapter *adap;
int addr, ret;
u8 b;
msleep(3000); /* 等 HDF / i2c 控制器起来 */
adap = i2c_get_adapter(2); /* i2c2,按你要扫的总线改 */
if (!adap) {
pr_err(TAG "no i2c-2\n");
return;
}
for (addr = 0x08; addr <= 0x77; addr++) {
struct i2c_msg msg = {
.addr = addr,
.flags = I2C_M_RD,
.len = 1,
.buf = &b,
};
ret = i2c_transfer(adap, &msg, 1);
if (ret == 1)
pr_err(TAG "ack 0x%02x val=0x%02x\n", addr, b);
}
pr_err(TAG "scan done\n");
i2c_put_adapter(adap);
}
static DECLARE_DELAYED_WORK(diag_work, diag_work_fn);
static int __init diag_init(void)
{
pr_err(TAG "scheduled\n");
schedule_delayed_work(&diag_work, msecs_to_jiffies(4000));
return 0;
}
late_initcall(diag_init);
MODULE_LICENSE("GPL");
Kconfig / Makefile 由 build_kernel.sh 注入 CONFIG_BOARD_DIAG=y。前缀用固定字符串 BOARD_DIAG:,串口再乱也能 grep。
# 主机侧从串口日志里滤
grep BOARD_DIAG uart.log
交付镜像不要留这种模块。它会在每趟开机扫总线,拖慢启动,也干扰真驱动。调通就删节点、关 CONFIG。
gpio / 寄存器同理,pr_err 打 gpiod_get_raw_value 或 readl。用户态 /dev/mem 在有 shell 之后更方便,没 shell 时不要指望它。
有 shell 之后,同一件事用现成节点:
# i2cdetect -y 2
# cat /sys/kernel/debug/gpio
# cat /proc/device-tree/serial@fdd50000/status
CONFIG_I2C_CHARDEV=y 才能 i2cdetect。debugfs 先挂:
# mount -t debugfs none /sys/kernel/debug
9. 用户态是 32 位 musl,内核是 aarch64
uname -a 打出 aarch64。那是内核。.ko、设备树、vmlinux 按 64 位 ARM 走。用户态不是。
板上没有 aarch64 的 musl 加载器。有的是:
/system/lib/ld-musl-arm.so.1
没有 /system/lib64/ld-musl-aarch64.so.1。/system/bin/sh、toybox、你丢上去的小工具,都应当是 ELF 32-bit ARM,动态链接,解释器指到上面那个路径。
编错的两种死法,报错都不提 arch:
| 你编出来的 | 板上表现 |
|---|---|
| aarch64 动态 | `No such file or directory`。shell 找不到 64 位解释器,像路径写错 |
| aarch64 静态 | `Signal 11`,退出码 139 |
| armv7 静态 musl | 也可能段错误,或链接期缺符号。这套 musl 静态不作为日常选项 |
| armv7 动态,没指定解释器 | 主机默认解释器不是 `/system/lib/ld-musl-arm.so.1`,上板仍是 No such file |
正确的编译器在源码树预编译工具链里,不是系统自带的 aarch64-linux-gnu-gcc,也不是 glibc 的 arm-linux-gnueabihf-gcc:
CC=prebuilts/clang/ohos/linux-x86_64/llvm/bin/armv7-unknown-linux-ohos-clang
SYSROOT=prebuilts/ohos-sdk/linux/11/native/sysroot
$CC --sysroot=$SYSROOT \
-o hello hello.c \
-Wl,--dynamic-linker=/system/lib/ld-musl-arm.so.1
主机上确认:
file hello
readelf -h hello | grep -E "Class|Machine"
readelf -l hello | grep interpreter
期望:ELF 32-bit、ARM、interpreter /system/lib/ld-musl-arm.so.1。然后拷到共享目录,对 md5,Windows file send 到 /data/local/tmp/hello:
hdc shell "ls -l /system/lib/ld-musl-arm.so.1"
hdc shell "chmod 0755 /data/local/tmp/hello"
hdc shell "/data/local/tmp/hello"
hdc shell 有时会吞掉秒退进程的 stdout。程序自己把结果写 /data/local/tmp/hello.log 再 cat,比只靠 printf 稳。
.ko 仍然按内核 arch 编,ARCH=arm64。用户态工具和内核模块不能共用一条 clang 命令。同一块板,两种 ABI,混了就按上面那张表死。
板上不一定有 file。把二进制 file recv 回主机再看,或在主机编完立刻 file,不要等上板。上文 uart_raw.c 也按这条命令编。这块板的 dw-apb-uart 对 VTIME 超时不可靠,正式收发用非阻塞加 poll,UART 总线那篇写。
10. 一次最小验收,十分钟内能做完
板子上电,串口开着,OTG 插着 Windows。
串口:回车出现 #
串口:uname -a 是 5.10 aarch64
串口:param get ohos.boot.hardware = rk30board
Windows:hdc list targets 有设备号
hdc shell "echo hdc_ok"
hdc file send 一个小文本 /data/local/tmp/a.txt
hdc shell "cat /data/local/tmp/a.txt"
hdc hilog 能刷出几行
dmesg | head 能看见 Booting Linux
全部绿灯,三板斧就在你手上。缺串口,Maskrom 和 panic 你会瞎;缺 hdc,单分区和 hap 你会回到整包;缺编译机,改一行 dts 你只能等别人打镜像。
11. 失败判断
乱码。 波特率。先改 1500000,再怀疑线。
有字,回车没反应。 流控;TX 没接上;console 服务没 start;改错了 init.rk3568.cfg。
完全没字。 供电、TX/RX 反、插错口、DDR 训练就停了(loader 版本,启动链那篇)、USB 转串口驱动没装。
hdc list targets 空,串口正常。 OTG 口、hdcd、HDC_SERVER_PORT、充电线、虚拟机 USB 直通把设备抢走。
DevEco 和 cmd 抢设备。 只留一个 server,或给 cmd 换 HDC_SERVER_PORT。
hdc shell "dmesg | grep uart" 在 Windows 上报错。 引号没包住,cmd 本地执行了 grep。
microcom 打开 ttyS2。 你把调试口抢了。开 ttyS7。
tcgetattr: Not a tty。 假文件。ls -l,删,查 probe。
dmesg 没有你加的 printk,hilog 也没有。 分桶。内核用 dmesg,用户态用 hilog。还没有,多半没编进这次镜像。
late_initcall 里 i2c_get_adapter 返回 NULL。 延迟不够,控制器还没 probe。msleep 加大,或对具体 platform_device 做 defer。不要改成 pure_initcall,那时总线更不存在。
自编工具 No such file or directory,路径明明在。 ls /system/lib/ld-musl-arm.so.1;主机 file。编成了 aarch64 动态,找不到 64 位加载器。
自编工具立刻 139。 多半是 aarch64 静态。.ko 能 insmod 不代表同目录的用户态 ELF 能跑——模块是 64 位,用户态要 32 位。
三板斧的验收不是「工具都装了」。验收是:串口能敲命令、hdc 能推文件、两台电脑不会抢同一根 OTG。少一条,后面改 dts、改驱动、装 hap 都会把时间浪费在通道上,而不是浪费在问题上。
系列第 10 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10
更多推荐




所有评论(0)