RTC实时时钟和USB键鼠设置—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
音频口认准了。今天收口板载基础接口:断电后还在走的实时时钟,以及没有触摸时的 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。
插错口的现场:
- 键盘插 Type-C 转接头,电脑 hdc 掉了
lsusb在板子上看不见键盘- 拔掉键盘,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 无 HID | USB_HID / HID_GENERIC | defconfig |
| 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,用 hwclock 和 RTC_RD_TIME,断电靠电池。USB 键鼠走 Host 口上的 HID,OTG 留给 hdc。两件事不要插在同一个口上验。
系列第 32 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10
更多推荐



所有评论(0)