串口节点会找了。今天第一颗外设是 RS232。发送脚曾被公版相机电源抢走,探测失败时设备节点都不会出现。

/dev/ttyS0 在 ls 里看得到,open 也能返回一个 fd,tcgetattr 却报 Not a tty。或者更干净:/proc/tty/driver/serial 根本没有 0: 这一行。uart2、uart3、uart7 都好好的,只有 uart0 缺席。

这不是 RS232 电平转换芯片坏了。转换芯片还没轮到上场。SoC 这边 uart0 的 TX 脚已经被别人抢走,probe 返回 -22,驱动没有绑定,tty 层没有第 0 路。有人急了,echo test > /dev/ttyS0,当时节点还不存在,tmpfs 的 /dev 建了一个普通文件。从此 ls 永远看得到 ttyS0,它只是个假的。

UART 的打开方式、poll、termios,UART 那篇已经写过。这里不重复那份 uart_tool.c,只补:假文件、probe -22、抢脚的 vcc_camera、设备树怎么删 gpio、DB9 回环。

产品 rk3568_evb。uart0 基址 0xFDD50000,节点 /dev/ttyS0,主次设备号 4, 64。


1. RS232 在这套硬件上是什么

SoC 的 uart0 吐的是 3.3 V TTL。PC、工控屏、不少仪表要的是 RS232:逻辑 1 是负电压,逻辑 0 是正电压,摆幅大约 ±5 V 到 ±15 V。中间必须有一颗电平转换芯片。模块侧是 DB9,板侧是 TTL 接到 SoC 的 UART0_TX / UART0_RX。

PC / 仪表  DB9  ←±12V→  电平转换  ←3.3V TTL→  SoC uart0  →  /dev/ttyS0

TX、RX 交叉:模块的 TX 进 SoC 的 RX,模块的 RX 接 SoC 的 TX。地要共。DB9 常见是针 2 RX、针 3 TX、针 5 GND,以模块丝印为准,不要死记「公头一定是 2 收 3 发」。

应用层看起来和 ttyS7 没区别:8N1、原始模式、poll 读。波特率常见 9600 或 115200,跟对端商量。不要用 1500000——那是 ttyS2 调试口的速率。

uart0 的 pinctrl 组叫 uart0_xfer:

信号脚Linux gpio 编号
UART0_RXGPIO0_C016
UART0_TXGPIO0_C117

GPIO0_C1 = bank0 的 pin 17。记住 17,后面 dmesg 里出现的是 gpio0-17。


2. 先分清三种「没有 ttyS0」

假文件。 ls -l /dev/ttyS0 第一位是 -,不是 c。stat 看到普通文件。tcgetattr:Not a tty。

hdc shell "ls -l /dev/ttyS0"
hdc shell "cat /proc/tty/driver/serial | grep '^0:'"

没有 0: 这一行,这个 /dev/ttyS0 就是垃圾。删掉:

hdc shell "rm /dev/ttyS0"

删完不会自动长出真节点。真节点只有驱动 probe 成功才会 device_create。假文件只是挡住你看清「驱动根本没起来」。

节点不存在。 ls 报 No such file。驱动没绑。往下查 probe。

节点是 crw,但应用打不开。 权限 600。UART 那篇的开机 chmod。uart0 若给 HAP 用,同样要 chmod 0666 /dev/ttyS0。调试阶段 hdc shell 是 root,先别被 shell 能打开骗过去。

这三种里,本产品上最常见的是第一种叠第二种:probe 失败 → 没有真节点 → 有人 echo 出假文件 → 看起来「有 ttyS0 但不是串口」。


3. probe -22 的现场

设备树里 &uart0 { status = "okay"; } 已经写了。运行中的 dtb 也 okay:

hdc shell "cat /proc/device-tree/serial@fdd50000/status"
# okay

驱动却没绑:

hdc shell "ls -l /sys/bus/platform/devices/fdd50000.serial/driver"
# 没有这一项,或 ls 失败

不在 deferred 列表里:

hdc shell "cat /sys/kernel/debug/devices_deferred"

空,或者没有 fdd50000.serial。意思是:probe 已经跑过,返回了非 defer 的错误,内核不会再试。defer 是「资源还没好,晚点再来」;-22(-EINVAL)是「我试了,不行」。

dmesg 可能已经被 audit 噪声冲掉。手动绑一次,错误会重新打出来。先看一路正常的 uart 绑的是哪个驱动:

hdc shell "readlink /sys/bus/platform/devices/fe660000.serial/driver"
# .../dw-apb-uart

然后:

hdc shell "echo fdd50000.serial > /sys/bus/platform/drivers/dw-apb-uart/bind"
hdc shell "dmesg | tail -20"

关键两行(原文大意,你板上的标点可能略有出入):

pin gpio0-17 already requested by vcc-camera-regulator; cannot claim for fdd50000.serial
could not request pin 17 (gpio0-17) from group uart0-xfer
fdd50000.serial: probe of fdd50000.serial failed with error -22

gpio0-17 就是 GPIO0_C1,uart0 的 TX。占用者叫 vcc-camera-regulator。不是相机驱动,是一个 always-on 的 regulator 节点,把这根脚当成使能脚用了。


4. 谁在抢脚

公版 EVB 的 dtsi 里有一段类似:

vcc_camera: vcc-camera-regulator {
    compatible = "regulator-fixed";
    regulator-name = "vcc_camera";
    regulator-always-on;
    regulator-boot-on;
    enable-active-high;
    gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&camera_pwr>;
};

regulator-always-on + regulator-boot-on:开机立刻申请那根 gpio。uart0 后 probe,pinctrl 要这根脚做 TX,申请失败,-22。

本产品的相机供电是硬连线的 vcc_avdd / vcc_dovdd / vcc_dvdd,相机节点的 *-supply 引用这三路,根本不引用 vcc_camera。这个 regulator 是 EVB 底板留下来的死遗留。删掉它的 gpio / pinctrl,相机照样亮。保留 regulator 节点本身无害,always-on 一个没有消费者的固定电源,只是名字吓人。

同类抢脚在这块板上出现过不止一次:某个 always-on 的电源节点、某个 disabled 得不彻底的 PWM,都会在 probe 阶段把 GPIO 先占上。排障口诀:already requested by X 里的 X,去设备树全局搜,看它是不是「开机就抢、实际没人用」。


5. 设备树怎么改

板级 dts,不要去改 SoC 的 dtsi。覆盖节点、删属性:

/* device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts */

&uart0 {
    status = "okay";
};

/*
 * uart0 TX = GPIO0_C1 = gpio0-17。
 * 公版 vcc_camera 把这根脚当使能,regulator-always-on,开机即抢,
 * uart0 probe -22,/dev/ttyS0 不出。
 * 本板相机走硬连线 avdd/dovdd/dvdd,不引用 vcc_camera 的 gpio。
 */
&vcc_camera {
    /delete-property/ gpio;
    /delete-property/ pinctrl-names;
    /delete-property/ pinctrl-0;
    /delete-property/ enable-active-high;
};

/delete-property/ 是 dtc 的覆盖语法,把基 dtsi 里已经写上的属性拿掉。只写 gpio; 空属性是不行的,那是声明,不是删除。

改完重打 dtb,刷进 resource 和带 dtb 的 boot_linux(生效的 dtb 在 resource 分区的 rk-kernel.dtb)。只刷 Image、不刷 resource,抢脚还在。

解包确认属性真的没了:

debugfs -R 'dump /extlinux/toybrick.dtb /tmp/run.dtb' boot_linux.img
dtc -I dtb -O dts /tmp/run.dtb 2>/dev/null | grep -A20 'vcc-camera-regulator'

gpio = <...> 不该再出现。uart0 那段应有 pinctrl-0 指向 uart0-xfer,status = "okay"。

板上运行时:

hdc shell "cat /proc/device-tree/serial@fdd50000/status"
hdc shell "ls /sys/bus/platform/devices/fdd50000.serial/driver"
hdc shell "cat /proc/tty/driver/serial | grep '^0:'"
hdc shell "ls -l /dev/ttyS0"

期望:

okay
/sys/bus/platform/devices/fdd50000.serial/driver -> .../dw-apb-uart
0: uart:16550A mmio:0xFDD50000 irq:... tx:0 rx:0
crw-rw-rw- 1 root root 4, 64 ... /dev/ttyS0

主设备号 4、次设备号 64 是 ttyS0 的标准编号。不是 4, 64 就不是这路串口。


6. 回环

电平转换芯片、DB9、线,到这一步才值得测。TX 和 RX 在 DB9 侧短接:针 2 和针 3 用杜邦线或金属夹短在一起。短的是 RS232 侧,信号会走转换芯片打一个来回,再进 SoC 的 RX。只在 TTL 侧短 TX-RX,测的是 SoC 脚,绕过了转换芯片,模块坏了你测不出来。

两边都测也行:先 TTL 回环确认控制器,再 DB9 回环确认转换芯片。出问题的时候按这个分层,不要一上来换模块。

波特率 115200 8N1,和UART 那篇同一套 termios:O_NONBLOCK、poll、关流控。回环时顺序:先 flush,再 write,再 poll 读。不要在 write 之后立刻 flush 接收缓冲,把刚绕回来的字节冲掉。

完整工具 uart0_loop.c:

/* uart0_loop.c — /dev/ttyS0 回环。短接 DB9 2/3。
 * 32 位 musl。用法:uart0_loop /dev/ttyS0 115200
 */
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

static speed_t baud_const(int b)
{
    switch (b) {
    case 9600:   return B9600;
    case 19200:  return B19200;
    case 38400:  return B38400;
    case 57600:  return B57600;
    case 115200: return B115200;
    default:     return B115200;
    }
}

static int open_raw(const char *path, int baud)
{
    int fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    struct termios t;
    memset(&t, 0, sizeof(t));
    if (tcgetattr(fd, &t) != 0) {
        perror("tcgetattr");
        close(fd);
        return -1;
    }
    cfsetispeed(&t, baud_const(baud));
    cfsetospeed(&t, baud_const(baud));
    t.c_cflag = (t.c_cflag & ~CSIZE) | CS8;
    t.c_cflag |= (CLOCAL | CREAD);
    t.c_cflag &= ~(PARENB | CSTOPB | CRTSCTS);
    t.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    t.c_iflag &= ~(IXON | IXOFF | IXANY | INLCR | ICRNL);
    t.c_oflag &= ~OPOST;
    t.c_cc[VMIN] = 0;
    t.c_cc[VTIME] = 0;
    tcflush(fd, TCIOFLUSH);
    if (tcsetattr(fd, TCSANOW, &t) != 0) {
        perror("tcsetattr");
        close(fd);
        return -1;
    }
    return fd;
}

static int read_poll(int fd, char *buf, int bufsz, int timeout_ms)
{
    int used = 0;
    int idle = 0;
    while (idle < timeout_ms && used < bufsz - 1) {
        struct pollfd p;
        p.fd = fd;
        p.events = POLLIN;
        p.revents = 0;
        int pr = poll(&p, 1, 50);
        int r = 0;
        if (pr > 0 && (p.revents & POLLIN))
            r = (int)read(fd, buf + used, (size_t)(bufsz - 1 - used));
        if (r > 0) {
            used += r;
            idle = 0;
        } else {
            idle += 50;
        }
    }
    buf[used] = '\0';
    return used;
}

int main(int argc, char **argv)
{
    const char *dev = "/dev/ttyS0";
    int baud = 115200;
    if (argc >= 2)
        dev = argv[1];
    if (argc >= 3)
        baud = atoi(argv[2]);

    int fd = open_raw(dev, baud);
    if (fd < 0)
        return 1;

    const char *msg = "RS232-LOOP-0123456789\n";
    size_t want = strlen(msg);

    tcflush(fd, TCIOFLUSH);
    ssize_t wn = write(fd, msg, want);
    if (wn < 0 || (size_t)wn != want) {
        perror("write");
        close(fd);
        return 1;
    }
    tcdrain(fd);

    char buf[256];
    int n = read_poll(fd, buf, sizeof(buf), 400);
    close(fd);

    printf("wrote %zu bytes: %s", want, msg);
    printf("read  %d bytes: ", n);
    fwrite(buf, 1, (size_t)n, stdout);
    if (n == 0 || buf[n - 1] != '\n')
        putchar('\n');

    if (n == (int)want && memcmp(buf, msg, want) == 0) {
        printf("PASS\n");
        return 0;
    }
    printf("FAIL (need loopback on DB9 pins 2-3, check shifter)\n");
    return 2;
}

编译、丢板:

OHOS=/path/to/OpenHarmony
CLANG=$OHOS/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang
SYSROOT=$OHOS/prebuilts/ohos-sdk/linux/11/native/sysroot

$CLANG --target=arm-linux-ohos --sysroot=$SYSROOT -O2 -Wall \
  -Wl,--dynamic-linker=/system/lib/ld-musl-arm.so.1 \
  -o uart0_loop uart0_loop.c

hdc file send uart0_loop /data/local/tmp/uart0_loop
hdc shell "chmod 0755 /data/local/tmp/uart0_loop"
hdc shell "/data/local/tmp/uart0_loop /dev/ttyS0 115200"

PASS:控制器 + 转换芯片 + 短接线都通。FAIL 且 read 0 bytes:没短接,或转换芯片没上电,或你短错了针。open 阶段 Not a tty:回到第 2 节假文件。open 失败 No such file:probe 还没好,dtb 没刷进去。

用UART 那篇的 uart_tool 手测不稳:两次独立进程,第一次 write 的回环数据在第二次 open 之前可能已经被冲掉。回环必须单进程,write 和 read 之间不要 close。


7. NAPI 和 ArkTS

UART 那篇的 uartOpen / uartWrite / uartRead / uartClose 直接用。设备填 /dev/ttyS0,波特率 115200。页面上加一个「回环」按钮:写固定字符串,立刻读,比对。

import board from 'libboard.so';

@Entry
@Component
struct Rs232Page {
  @State msg: string = 'ttyS0 @115200。DB9 的 2、3 针短接再点回环。';
  @State opened: boolean = false;
  @State out: string = '';

  aboutToDisappear() {
    if (this.opened) {
      board.uartClose();
      this.opened = false;
    }
  }

  build() {
    Column() {
      Text('RS232 / ttyS0').fontSize(20).fontWeight(FontWeight.Bold).margin({ bottom: 8 })
      Text('3.3V TTL 经电平转换到 DB9。TX 是 GPIO0_C1,曾被 vcc_camera 抢走。')
        .fontSize(13).fontColor('#C62828').margin({ bottom: 8 })
      Row() {
        Button(this.opened ? '关闭' : '打开 ttyS0').onClick(() => { this.toggle(); })
        Button('回环').margin({ left: 8 }).onClick(() => { this.loopback(); })
        Button('读一次').margin({ left: 8 }).onClick(() => { this.readOnce(); })
      }
      Text(this.msg).fontSize(14).margin({ top: 10 }).fontColor('#1565C0')
      Text(this.out).fontSize(13).margin({ top: 8 })
    }.padding(16).width('100%')
  }

  toggle() {
    if (this.opened) {
      board.uartClose();
      this.opened = false;
      this.msg = '已关闭';
      return;
    }
    let rc: number = board.uartOpen('/dev/ttyS0', 115200);
    if (rc === 0) {
      this.opened = true;
      this.msg = '已打开 /dev/ttyS0 @115200';
    } else {
      this.msg = '打开失败。ls -l 必须是 crw 4,64;serial 必须有 0: 16550A。';
    }
  }

  loopback() {
    if (!this.opened) {
      this.msg = '先打开';
      return;
    }
    let payload: string = 'RS232-LOOP-0123456789';
    let n: number = board.uartWrite(payload, 0);
    let s: string = board.uartRead(400, 0);
    if (s.indexOf('RS232-LOOP') >= 0) {
      this.out = 'PASS  write=' + n.toString() + '  read=' + s;
    } else {
      this.out = 'FAIL write=' + n.toString() + '  read=[' + s +
        ']  短接 DB9 2/3,查转换芯片供电';
    }
  }

  readOnce() {
    if (!this.opened) {
      this.msg = '先打开';
      return;
    }
    this.out = board.uartRead(600, 0);
  }
}

NAPI 的 UartWrite 里有 tcflush(TCIFLUSH)。那是为了「发命令前丢掉模块主动上报的半帧」。回环不是那种场景:write 把数据送进 TX,几乎同时出现在 RX。如果 write 函数先 flush 接收缓冲,再 write,回环数据随后到达,read 还能接到。如果内核把回环字节在 write() 返回前就放进接收缓冲,flush 会把它们丢掉。现场若稳定 FAIL 且 read 为空、示波器上 TX 有波形,把回环留在 uart0_loop.c 里验硬件,ArkTS 只验「应用能打开 ttyS0、能写、对端 PC 能收到」。


8. 和对端 PC 对测

回环只证明自己能听见自己。真用还要对面一台会 RS232 的机器。

  1. 板子 uart_tool write /dev/ttyS0 115200 text hello
  2. PC 串口工具 115200 8N1 无流控,应出现 hello
  3. PC 发 world,板子 uart_tool read /dev/ttyS0 115200 text 1000 应出现 world

PC 侧常见坑:

  • 线是交叉还是直通。模块已经交叉过一次,再插交叉线等于直通,两边都在听自己。
  • 波特率 9600 对 115200,两边都有「收到了」的错觉,内容是垃圾。hex 读一眼。
  • PC 开了 RTS/CTS,板子没开。关掉 PC 的硬件流控。
  • USB 转 RS232 芯片的驱动没装,设备管理器里是未知设备。
  • Windows 串口工具勾「新行发送 CR+LF」。板子原始模式不会把 \n 翻译成 \r\n。PC 只发 LF、仪表要 CRLF,表现为「命令写了没反应」。

板上 tx: 计数在 /proc/tty/driver/serial 的 0: 那行,write 之后应该涨。rx: 在对端发送之后涨。两个计数都不动,问题在 pinctrl 或假节点,不在应用。

短线 115200 稳。十几米的 RS232 电缆、劣质转换芯片,115200 会偶发丢字节,降到 9600 再对一次。丢字节时 /proc 的 rx: 仍在涨,应用看到的是缺了几个字符的行——那是电气,不是 termios。


9. 权限和开机

HAP 要用 ttyS0,开机脚本加上:

# device/board/rk/rk3568_evb/board_perm/board-perm.sh
chmod 0666 /dev/ttyS0 2>/dev/null

不要碰 ttyS2。/dev 是 tmpfs,重启重建。改脚本刷 vendor。

init.rk30board.cfg 里 once 服务,跟UART 那篇同一份脚本。ohos.boot.hardware 必须是 rk30board,写错后缀这份 cfg 不会加载,chmod 永远不跑。

SELinux enforcing 时还要 te 规则。bring-up 阶段 enforcing=0。


10. 改 dtb 之后仍然 -22

刷错分区。/dev/block/by-name/resource 指向 eMMC,你从 SD 启动,dd 到 by-name 等于写了另一块介质。cat /proc/partitions,认 mmcblk0 / mmcblk1,dd 用 /dev/block/mmcblkXpY。

dtb 没进 resource。只替换了 Image。

vcc_camera 的 gpio 写在别的 dtsi,板级 /delete-property/ 没覆盖到那个节点名。dtc -I dtb -O dts 全局搜 RK_PC1 和 camera_pwr,看还有谁引用 GPIO0_C1。

手动 bind 的错误变成别的 pin。那就不是这根脚,按新的 already requested by 再搜。

相机「修完 uart0 之后不亮」:先确认你删的只是 gpio / pinctrl,没有把 vcc_avdd 那些硬连线 regulator 关掉。uart0 抢的是 GPIO0_C1,相机供电不是这根脚。

compatible 保持 SoC dtsi 里的 snps,dw-apb-uart 一类,不要改成 8250 的别名碰运气。时钟、dma 也别在板级乱动。这章只删 vcc_camera 的 gpio,不动 uart0 节点的 clocks。


11. pinctrl 和 gpio 编号

Linux gpio 编号 = bank × 32 + 组内偏移。RK 的组:A=0、B=8、C=16、D=24。GPIO0_C1 = 0×32 + 16 + 1 = 17。dmesg 写 gpio0-17,sysfs 导出是 17。混用「C1」和「17」的时候,先换算再搜。

占用情况:

hdc shell "cat /sys/kernel/debug/pinctrl/pinctrl-rockchip/pinmux-pins | grep -i 'gpio0-17\|pc1\|uart0'"
hdc shell "cat /sys/kernel/debug/gpio | grep gpio-17"

修好之前,gpio-17 的 owner 是 vcc-camera-regulator 或 sysfs。修好之后应是 uart0 的 pinctrl 组。还停在 camera regulator 上,dtb 没生效。

示波器:GPIO0_C1 上电之后应能看到 uart0 的 TX 空闲高电平(TTL 3.3 V)。write 的时候有比特波形。一直 0 V:脚被当成 gpio 拉低,或没接到转换芯片。一直 3.3 V 且 write 后 tx: 计数涨、脚上没跳变:pinctrl 还是 gpio,不是 uart 功能。

转换芯片的 VCC 常见 3.3 V 或 5 V,以模块丝印为准。没供电的转换芯片,TTL 侧回环可能仍 PASS(你短在 SoC 脚上),DB9 侧一定 FAIL。分层测,不要两边一起怀疑。

uart0 和相机同时亮,才算这章收工。只亮串口、相机没了,是误删了 avdd 那组 regulator,把 /delete-property/ 用过了头。


12. 验收

  1. ls -l /dev/ttyS0 → crw,4, 64。不是普通文件。
  2. /proc/tty/driver/serial 有 0: uart:16550A mmio:0xFDD50000。
  3. /sys/bus/platform/devices/fdd50000.serial/driver 链到 dw-apb-uart。
  4. dmesg 不再出现 gpio0-17 already requested by vcc-camera-regulator。
  5. DB9 2/3 短接,uart0_loop 打印 PASS。
  6. 对端 PC 115200 8N1,板子 write 的文本能出现在 PC 窗口,反过来也行。

1 到 4 过了、5 失败:转换芯片、供电、短接针。1 到 4 不过:还在抢脚或假文件,不要买新模块。


13. 现场记录(一次真实的下午)

dtb 反编译确认 uart0 okay、pinctrl 指向 uart0-xfer。/proc/device-tree 同样 okay。/dev/ttyS0 存在。应用报 Not a tty。

ls -l 第一位是 -。假文件。删掉。节点不再出现。

ls driver 对 fdd50000.serial 失败。devices_deferred 没有它。手动 bind,dmesg 打出 gpio0-17 被 vcc-camera-regulator 占用。

搜板级 dts,没有 vcc_camera。搜基 dtsi,有,always-on,gpio 就是 RK_PC1。板级加 /delete-property/ 四条。重打 dtb,刷 resource + boot_linux。

重启。0: uart:16550A。crw 4,64。TTL 侧回环 PASS。DB9 侧第一次 FAIL:短接了针 2 和针 5(地)。改短 2 和 3,PASS。

相机预览还在。删 gpio 没有动 avdd。

这套步骤可以原样用在下一次「某路 UART 就是不出节点」:先看是不是假文件,再看 deferred 还是硬错误,再手动 bind 抓 already requested by。


路径

路径用途
`device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts``&uart0`、`&vcc_camera` 删 gpio
公版 dtsi 里的 `vcc_camera`抢脚来源,不要改 dtsi,用板级覆盖
`/dev/ttyS0`用户态节点,必须是 `c 4,64`
`/proc/tty/driver/serial``0: uart:16550A mmio:0xFDD50000`
`/sys/bus/platform/devices/fdd50000.serial/driver`是否绑上 dw-apb-uart
`/sys/kernel/debug/devices_deferred`区分 defer 和硬错误
`/sys/bus/platform/drivers/dw-apb-uart/bind`手动 bind 抓 dmesg
`uart0_loop.c`DB9 回环
`entry/src/main/cpp/napi_init.cpp`复用 uartOpen 那套 termios
`device/board/rk/rk3568_evb/board_perm/board-perm.sh``chmod 0666 /dev/ttyS0`

GPIO0_C1 还给 uart0 之后,这路 RS232 就只剩电平转换和线序。室内没经纬度是 GPS 的事,不要拿到这里来改 probe。


DB9 针脚和三种线

DE-9 公头正视、缺口向上,常用针:

针信号回环时
2RXD与 3 短接
3TXD与 2 短接
5GND不要和 2 短,第一次 FAIL 经常短到这里

模块是 DCE 还是 DTE,会把 2/3 对调。TTL 侧回环(转换芯片靠 SoC 一侧把 TX 焊到 RX)能过、DB9 不过:转换芯片或它的供电。DB9 能过、对端 PC 不过:交叉线还是直通、PC 口不是 115200 8N1、流控勾了 RTS/CTS。板子 termios 已关 CRTSCTS,PC 端也要关。

波特率这路默认 115200,不是调试口的 1500000,也不是 GPS 的 9600。三个数字混用,窗口里全是 0x80 以上的脏字节。文本模式若不过滤非 ASCII,ArkTS 一 split 就崩,看起来像「RS232 把应用打挂了」。过滤在UART 那篇的 uartRead 里,这路同样要走。

假文件再强调一次:ls -l /dev/ttyS0 第一位必须是 c,主次设备号 4, 64。第一位是 -,那是有人 echo > /dev/ttyS0 在 tmpfs 上建的普通文件,删掉重开,不要在它上面调 termios。

手动 bind 的命令不要记错控制器名。uart2 能用时先抄它的驱动名:

readlink /sys/bus/platform/devices/fe660000.serial/driver
# .../dw-apb-uart
echo fdd50000.serial > /sys/bus/platform/drivers/dw-apb-uart/bind
dmesg | tail -20

already requested by vcc-camera-regulator 出现,就不要再怀疑电平转换芯片。脚还在别人手里,转换芯片连信号都收不到。板级 dts 覆盖之后,这两条命令应当 bind 成功,/proc/tty/driver/serial 出现 0: uart:16550A mmio:0xFDD50000。

ArkTS 复用UART 那篇串口页,设备填 /dev/ttyS0,波特率 115200,文本模式。打开失败先 ls -l,不要在页面里重试一百次。回环 PASS 之后再接对端。对端乱码先对波特率,再对 2/3 交叉。

ttyS0 也要进 board-perm.sh 的 chmod 0666。HAP 不是 root,节点默认 0600 时应用 open 直接 -1,看起来又像「驱动没绑上」。root 的 hdc 工具能通、应用不通,先看权限这一层。

hdc shell "cat /proc/partitions"
hdc shell "ls -l /dev/i2c-1 /dev/ttyS0"
hdc shell "dmesg | grep -E 'i2c|mlx|hdc|uart0|vcc-camera' | tail -30"

上面三条在换模块、改 dts、刷完分区之后各跑一次,比在应用里加 log 便宜。输出贴进记录,下一轮对照。


系列第 42 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐