会刷之后,日常就三件事。今天把串口、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/ttyS79600(不少 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应打出一串十六进制,例如:
ab0122454d41325245135dbd94d6bc00Empty或空白: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 boothdc 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.exeDevEco 也带一份。两份同时跑,就是下一节的端口问题。
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 | headhdc 上:
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.imgcertutil -hashfile D:\ohos\boot_linux.img MD5 certutil -hashfile D:\ohos\resource.img MD5Windows 资源管理器能看见文件,不等于字节完整。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 8710PID 对到任务管理器,多余的 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/statusCONFIG_I2C_CHARDEV=y才能i2cdetect。debugfs 先挂:
# mount -t debugfs none /sys/kernel/debug9. 用户态是 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,Windowsfile 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