音频口认准了。今天收口板载基础接口:断电后还在走的实时时钟,以及没有触摸时的 USB 键鼠。

拔电池上电,date 回到 1970 或编译时间。插着 Type-C 线的那一端插上键盘,hdc list targets 没了,键盘灯也不亮。两件事经常同一天出现,根因却完全不同。

RTC 是 DS1307,挂在 I2C0 地址 0x68。没有后备电池时,它只是上电时钟,断电必丢。USB 键鼠是 HID,必须插 Host 口(USB-A)。Type-C / OTG 那路给电脑做 hdc 和烧录。HID 插进 OTG,控制器切到 device,hdc 掉线,键盘也不会按 Host 去枚举。

产品 rk3568_evb

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

1. DS1307:先扫地址,再 hwclock

hdc shell "i2cdetect -y -r 0"
hdc shell "ls -l /dev/rtc*"
hdc shell "cat /sys/class/rtc/rtc0/name"
hdc shell "cat /sys/class/rtc/rtc1/name" 2>/dev/null

I2C0 上 0x68 应为 UU(驱动占着)或 68(扫到但没绑)。rtc0 的 name 期望 ds1307。RK809 内部也有 RTC,可能登记成 rtc1。对电池保持,认 DS1307 那颗,不要对 PMIC 那颗做断电实验——PMIC RTC 靠主电池,行为不同。

设备树:

&i2c0 {
    status = "okay";
    clock-frequency = <100000>;

    ds1307: rtc@68 {
        compatible = "dallas,ds1307";
        reg = <0x68>;
    };
};

同总线还有 CPU 供电芯片在 0x40。不要把 i2c0 关掉。内核:

CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_DS1307=y
CONFIG_I2C=y

读、写:

hdc shell "date"
hdc shell "hwclock -r -f /dev/rtc0"
hdc shell "hwclock -w -f /dev/rtc0"
hdc shell "hwclock -r -f /dev/rtc0"

-w 把系统时间写入芯片。系统时间若是 1970,写进去也是 1970,断电回来还是错的。先 date -s 或 NTP 把系统时间拨对,再 -w

toybox 的 hwclock 参数因版本而异。没有 -f 时,确认 /dev/rtc 软链指向 DS1307:

hdc shell "ls -l /dev/rtc /dev/rtc0"

sysfs 也能读:

hdc shell "cat /sys/class/rtc/rtc0/time"
hdc shell "cat /sys/class/rtc/rtc0/since_epoch"

断电保持:写好时间,拔主电源(USB 供电也要断),等半分钟,再上电。hwclock -r 应还在刚才附近,误差秒级。立刻跳回 1970:电池没装、极性反、空的 CR2032、或你读的是 rtc1。电池座空着测「保持」,永远失败。

32.768 kHz 晶振没起振:地址能扫到,时间不走或乱走。示波器看晶振脚。这是硬件,改 dts 没用。


2. 完整 C:读 RTC

/* rtc_get.c
 * armv7-unknown-linux-ohos-clang --target=arm-linux-ohos \
 *   --sysroot=${OHOS_NATIVE}/sysroot \
 *   -o rtc_get rtc_get.c \
 *   -Wl,--dynamic-linker=/system/lib/ld-musl-arm.so.1
 */
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/rtc.h>
#include <dirent.h>

static int name_is_ds1307(const char *rtc)
{
    char p[64], buf[64];
    snprintf(p, sizeof(p), "/sys/class/rtc/%s/name", rtc);
    int fd = open(p, O_RDONLY);
    if (fd < 0) {
        return 0;
    }
    int n = (int)read(fd, buf, sizeof(buf) - 1);
    close(fd);
    if (n <= 0) {
        return 0;
    }
    buf[n] = 0;
    return strstr(buf, "ds1307") != NULL;
}

int main(void)
{
    const char *dev = "/dev/rtc0";
    char path[32];
    int i;
    for (i = 0; i < 3; i++) {
        snprintf(path, sizeof(path), "rtc%d", i);
        if (name_is_ds1307(path)) {
            snprintf(path, sizeof(path), "/dev/rtc%d", i);
            dev = path;
            break;
        }
    }
    printf("using %s\n", dev);
    int fd = open(dev, O_RDONLY);
    if (fd < 0) {
        perror(dev);
        printf("i2cdetect -y 0 看 0x68;name 看是不是 ds1307\n");
        return 1;
    }
    struct rtc_time tm;
    if (ioctl(fd, RTC_RD_TIME, &tm) < 0) {
        perror("RTC_RD_TIME");
        return 2;
    }
    printf("%04d-%02d-%02d %02d:%02d:%02d\n",
           tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
           tm.tm_hour, tm.tm_min, tm.tm_sec);
    close(fd);
    return 0;
}

RTC_SET_TIME 需要权限。应用侧用 NAPI 调这个 ioctl,或读 sysfs time。不要自己对 0x68 发 I2C 去改寄存器,驱动已经占着总线,用户态会和 rtc 驱动打架。


3. USB:OTG 给 hdc,Host 给键鼠

设备树里两套控制器都 okay:

&usb2phy0 { status = "okay"; };
&u2phy0_host { status = "okay"; };
&u2phy0_otg { status = "okay"; };
&usb2phy1 { status = "okay"; };
&u2phy1_host { status = "okay"; };
&u2phy1_otg { status = "okay"; };
&usb_host0_ehci { status = "okay"; };
&usb_host0_ohci { status = "okay"; };
&usb_host1_ehci { status = "okay"; };
&usb_host1_ohci { status = "okay"; };
&usbdrd_dwc3 { status = "okay"; };
&usbdrd30 { status = "okay"; };
&usbhost_dwc3 { status = "okay"; };
&usbhost30 { status = "okay"; };

usbdrd* 是 OTG / Type-C 这一路,插电脑:烧录、hdc、装 HAP。usbhost* 和 ehci/ohci 是 USB-A Host,插键盘、鼠标、U 盘、UVC。

内核:

CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_HID=y
CONFIG_HID_GENERIC=y
CONFIG_USB_STORAGE=y

键鼠不需要再写驱动。HID generic 会出 /dev/input/event*

hdc shell "lsusb"
hdc shell "cat /proc/bus/input/devices"

键盘插在 A 口 上,devices 里出现 Name="... Keyboard"HID,Handlers 带 eventN。od 那个 event,按键有 EV_KEY。鼠标是 EV_REL。

插错口的现场:

  1. 键盘插 Type-C 转接头,电脑 hdc 掉了
  2. lsusb 在板子上看不见键盘
  3. 拔掉键盘,hdc 又回来

那不是 HID 没编,是 OTG 口被占成 device。键鼠改插 USB-A。hdc 线留在 Type-C。

Hub:A 口接 Hub 再接键鼠可以。供电不足时 Hub 要自带电源,否则枚举到一半掉。


4. 完整 C:找 HID 键盘

/* hid_keys.c  同 keypad_probe 的 find_event_by_name,want="keyboard" 或 "HID"
 */
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

static int find_event(const char *want, char *out, int len)
{
    FILE *f = fopen("/proc/bus/input/devices", "r");
    char line[256], name[128] = {0}, handler[128] = {0};
    if (!f) {
        return -1;
    }
    while (fgets(line, sizeof(line), f)) {
        if (strncmp(line, "N: Name=", 8) == 0) {
            snprintf(name, sizeof(name), "%s", line + 8);
        } else if (strncmp(line, "H: Handlers=", 12) == 0) {
            snprintf(handler, sizeof(handler), "%s", line + 12);
            if (strcasestr(name, want)) {
                char *p = strstr(handler, "event");
                int n = 0;
                if (p) {
                    sscanf(p, "event%d", &n);
                    snprintf(out, len, "/dev/input/event%d", n);
                    fclose(f);
                    return 0;
                }
            }
        }
    }
    fclose(f);
    return -1;
}

int main(void)
{
    char path[64];
    if (find_event("keyboard", path, sizeof(path)) < 0 &&
        find_event("HID", path, sizeof(path)) < 0) {
        fprintf(stderr, "没有键盘。插 USB-A Host,不要插 OTG。lsusb 应能看见设备\n");
        return 1;
    }
    printf("open %s\n", path);
    int fd = open(path, O_RDONLY);
    if (fd < 0) {
        perror(path);
        return 2;
    }
    struct input_event ev;
    while (read(fd, &ev, sizeof(ev)) == (ssize_t)sizeof(ev)) {
        if (ev.type == EV_KEY) {
            printf("code=%d value=%d\n", ev.code, ev.value);
        }
    }
    close(fd);
    return 0;
}

strcasestr 在 musl 可用。没有的话改 strstr 并对 want 用小写。

ArkTS 侧和矩阵键盘同一套 pollKey。桌面也会收 HID 的 KEY_A,测试页和输入法可能同时跳。这是预期。

权限:chmod 0666 /dev/input/event*,开机脚本里对后插入的设备用 uevent 再放权,否则热插键盘 HAP 读不到。


5. ArkTS:时间 + HID 提示

import evbio from 'libevbio.so';

@Entry
@Component
struct RtcHid {
  @State rtc: string = '';
  @State hid: string = '键盘插 USB-A';
  @State key: string = '';
  private timer: number = -1;

  build() {
    Column() {
      Text('RTC DS1307 / USB HID').fontSize(22).fontWeight(FontWeight.Bold)
      Text('芯片时间 ' + this.rtc).margin({ top: 12 }).fontSize(18)
      Text(this.hid).margin({ top: 8 }).fontSize(14)
      Text('最近按键 ' + this.key).margin({ top: 8 })
      Button('读 RTC').onClick(() => { this.readRtc(); }).margin(8)
      Button('系统时间写入芯片').onClick(() => {
        this.hid = evbio.rtcHwclockWrite();
        this.readRtc();
      }).margin(8)
    }.padding(20)
  }

  readRtc() {
    this.rtc = evbio.rtcRead(); // ioctl RTC_RD_TIME,按 name=ds1307 选设备
  }

  aboutToAppear() {
    this.readRtc();
    let p: string = evbio.findInput('keyboard');
    if (p.length === 0) {
      this.hid = '没有键盘 event。插 A 口;OTG 留给 hdc';
      return;
    }
    this.hid = 'HID ' + p;
    evbio.openKeys(p);
    this.timer = setInterval(() => {
      let arr = evbio.pollKey() as Array<Record<string, number>>;
      if (arr && arr.length) {
        this.key = 'code=' + arr[0]['code'].toString() + ' v=' + arr[0]['value'].toString();
      }
    }, 50);
  }

  aboutToDisappear() {
    if (this.timer >= 0) {
      clearInterval(this.timer);
    }
  }
}

写芯片时间的 NAPI 里用 RTC_SET_TIME,不要 shell 出 hwclock 再解析中文。


6. 现象对照

现象原因处理
0x68 扫不到i2c0 没 okay / 没芯片查 dts、焊、上拉
0x68 是 68 不是 UU驱动没绑CONFIG_RTC_DRV_DS1307、compatible
断电时间丢没电池或读错 rtc 号CR2032;name=ds1307
写入 1970系统时间没拨先 date 再 hwclock -w
键盘插上 hdc 掉插进 OTG改 USB-A Host
lsusb 无设备口错或供电A 口;Hub 要外供电
devices 无 HIDUSB_HID / HID_GENERICdefconfig
HAP 热插读不到新 event 权限uevent 里 chmod
flowchart TD
  A[断电时间不保持] --> B{rtc name=ds1307?}
  B -->|否| C[别对 PMIC rtc 做断电实验]
  B -->|是| D{有电池?}
  D -->|无| E[装 CR2032]
  D -->|有仍 1970| F[先把系统时间拨对再 hwclock -w]
  G[键盘不工作] --> H{插的是 USB-A?}
  H -->|Type-C/OTG| I[hdc 会掉, 改 A 口]
  H -->|是| J{lsusb 有设备?}
  J -->|无| K[线 / Hub 供电 / Host 控制器]
  J -->|有| L[devices 找 HID event]

DS1307 在 I2C0@0x68,用 hwclockRTC_RD_TIME,断电靠电池。USB 键鼠走 Host 口上的 HID,OTG 留给 hdc。两件事不要插在同一个口上验。


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

Logo

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

更多推荐