跳到主要内容

RTC

1.RTC

RTC(Real-Time Clock, 实时时钟)是一种能在微控制器或嵌入式系统掉电情况下仍能持续计时的硬件模块。它通常配备独立持久电源供应源,如常见的纽扣电池,以确保即使主电源断开,也能保持时间和日期的准确性。Omni3576 开发板采用 HYM8563 作为 RTC,HYM8563 是一款低功耗CMOS实时时钟/日历芯片,它提供一个可编程的时钟输出,一个中断输出和一个 掉电检测器,所有的地址和数据都通过 I2C 总线接口串行传递。最大总线速度为 400Kbits/s,每次读写数据后,内嵌的字地址寄存器会自动递增。

2. RTC 测试(shell)

  1. 确保rtc-hym8563驱动正常加载。

    luckfox@luckfox:~$ dmesg | grep 8563
    [ 2.716966] rtc-hym8563 2-0051: rtc information is valid
    [ 2.728824] rtc-hym8563 2-0051: registered as rtc0
    [ 2.730406] rtc-hym8563 2-0051: setting system clock to 2024-10-17T08:13:44 UTC (1729152824)
  2. 查看和校准系统时间、时区。

    date
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

2.1 查看 RTC 时间

  1. 使用 SYSFS 接口查看。

    cat /sys/class/rtc/rtc0/name   #查看RTC名字
    cat /sys/class/rtc/rtc0/time #查看当前RTC时间
    cat /sys/class/rtc/rtc0/date #查看当前RTC日期
  2. 使用 PROCFS 接口查看。

    cat /proc/driver/rtc

2.2 hwclock 校准 RTC 时间

Linux 系统中内置了 hwclock 工具,用于便捷地读取 RTC 硬件时钟提供的当前时间和日期。

  1. 要查看当前 RTC 的时间,使用以下命令:

    hwclock --show
  2. 将系统时间写入 RTC。

    hwclock --systohc
  3. 将 RTC 时间写入系统时间

    hwclock --hctosys

3. 测试 RTC(Python 程序)

  1. 读取 RTC时间。get_rtc_time() 函数通过运行 hwclock -r 命令获取 RTC 的时间。

    def get_rtc_time():
    try:
    result = subprocess.run(['hwclock', '-r'], capture_output=True, text=True, check=True)
    return result.stdout.strip()
    except subprocess.CalledProcessError as e:
    print(f"Error in get_rtc_time: {e}")
    return None
  2. 设置 RTC 时间。set_rtc_time()函数通过运行 hwclock -w 命令设置 RTC 的时间为系统当前时间.

    def set_rtc_time():
    try:
    subprocess.run(['hwclock', '-w'], check=True)
    print("RTC time set successfully.")
    except subprocess.CalledProcessError as e:
    print(f"Error in set_rtc_time: {e}")
  3. 完整代码。

    #!/usr/bin/python3

    import subprocess

    def get_rtc_time():
    try:
    result = subprocess.run(['hwclock', '-r'], capture_output=True, text=True, check=True)
    return result.stdout.strip()
    except subprocess.CalledProcessError as e:
    print(f"Error in get_rtc_time: {e}")
    return None

    def set_rtc_time():
    try:
    subprocess.run(['hwclock', '-w'], check=True)
    print("RTC time set successfully.")
    except subprocess.CalledProcessError as e:
    print(f"Error in set_rtc_time: {e}")

    rtc_time = get_rtc_time()
    if rtc_time:
    print(f"RTC time: {rtc_time}")

    set_rtc_time()
  4. 运行程序。

    python3 rtc.py

4. 测试RTC(C 程序)

在前文中,我们介绍了如何使用 shell 工具、Python 程序与 RTC 进行交互。而在实际的编程实践中,我们可以通过调用 C 库函数或系统调用来直接读取和设置 RTC。对于嵌入式 Linux 环境,访问 RTC 通常涉及打开并操作 /dev/rtc/dev/rtc0 设备文件。请注意,为了在特定的嵌入式系统上运行程序,通常需要使用交叉编译工具来编译代码,以生成可在目标开发板上执行的可执行文件。接下来,让我们一起探讨具体的实施步骤。

  1. 读取RTC时间。通过调用 rtc_get_time() 函数实现读取 RTC 时间,并格式化输出读取到的时间。

    int rtc_get_time(struct tm *time) {
    int ret,fd;
    struct rtc_time rtc_tm;

    //以只读模式打开 RTC 设备文件(/dev/rtc)
    fd=open("/dev/rtc", O_RDONLY);
    if(fd == -1) {
    perror("error open /dev/rtc");
    return -1;
    }

    //调用 ioctl 函数从打开的设备文件中读取 RTC 时间
    ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
    if(ret == -1) {
    perror("error RTC_RD_TIME ioctl");
    close(fd);
    return -1;
    }
    if (!time) {
    printf("time is NULL\n");
    close(fd);
    return -1;
    }

    //将读取到的 RTC 时间的值赋给传入的参数 time
    time->tm_sec = rtc_tm.tm_sec;
    time->tm_min = rtc_tm.tm_min;
    time->tm_hour = rtc_tm.tm_hour;
    time->tm_mday = rtc_tm.tm_mday;
    time->tm_mon = rtc_tm.tm_mon;
    time->tm_year = rtc_tm.tm_year;

    close(fd);
    return 0;
    }
  2. 设置RTC时间,通过调用 rtc_set_time() 函数实现设置 RTC 时间,并格式化输出设置时间。

    int rtc_set_time(const struct tm *time) {
    int fd = open("/dev/rtc", O_RDWR);
    if (fd == -1) {
    perror("open /dev/rtc failed");
    return -1;
    }

    struct rtc_time rtc_tm = {
    .tm_sec = time->tm_sec,
    .tm_min = time->tm_min,
    .tm_hour = time->tm_hour,
    .tm_mday = time->tm_mday,
    .tm_mon = time->tm_mon,
    .tm_year = time->tm_year
    };

    //调用 ioctl 函数设置RTC时间
    if (ioctl(fd, RTC_SET_TIME, &rtc_tm) == -1) {
    perror("RTC_SET_TIME ioctl failed");
    close(fd);
    return -1;
    }

    close(fd);
    return 0;
    }
  3. 完整代码。

    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    #include <unistd.h>
    #include <linux/rtc.h>
    #include <sys/ioctl.h>

    int rtc_get_time(struct tm *time);
    int rtc_set_time(const struct tm *time);

    int main() {
    struct tm input_time = { .tm_year = 2024 - 1900, .tm_mon = 1 - 1, .tm_mday = 13,
    .tm_hour = 12, .tm_min = 0, .tm_sec = 0 };
    struct tm output_time;

    if (rtc_get_time(&output_time) == 0) {
    printf("GET RTC TIME: %04d-%02d-%02d, %02d:%02d:%02d\n",
    output_time.tm_year + 1900, output_time.tm_mon + 1, output_time.tm_mday,
    output_time.tm_hour, output_time.tm_min, output_time.tm_sec);
    } else {
    printf("rtc_get_time failed!\n");
    }

    if (rtc_set_time(&input_time) == 0) {
    printf("SET RTC TIME: %04d-%02d-%02d, %02d:%02d:%02d\n",
    input_time.tm_year + 1900, input_time.tm_mon + 1, input_time.tm_mday,
    input_time.tm_hour, input_time.tm_min, input_time.tm_sec);
    } else {
    printf("rtc_set_time failed!\n");
    }

    return 0;
    }

    int rtc_get_time(struct tm *time) {
    int fd = open("/dev/rtc", O_RDONLY);
    if (fd == -1) {
    perror("open /dev/rtc failed");
    return -1;
    }

    struct rtc_time rtc_tm;
    if (ioctl(fd, RTC_RD_TIME, &rtc_tm) == -1) {
    perror("RTC_RD_TIME ioctl failed");
    close(fd);
    return -1;
    }

    time->tm_sec = rtc_tm.tm_sec;
    time->tm_min = rtc_tm.tm_min;
    time->tm_hour = rtc_tm.tm_hour;
    time->tm_mday = rtc_tm.tm_mday;
    time->tm_mon = rtc_tm.tm_mon;
    time->tm_year = rtc_tm.tm_year;

    close(fd);
    return 0;
    }

    int rtc_set_time(const struct tm *time) {
    int fd = open("/dev/rtc", O_RDWR);
    if (fd == -1) {
    perror("open /dev/rtc failed");
    return -1;
    }

    struct rtc_time rtc_tm = {
    .tm_sec = time->tm_sec,
    .tm_min = time->tm_min,
    .tm_hour = time->tm_hour,
    .tm_mday = time->tm_mday,
    .tm_mon = time->tm_mon,
    .tm_year = time->tm_year
    };

    if (ioctl(fd, RTC_SET_TIME, &rtc_tm) == -1) {
    perror("RTC_SET_TIME ioctl failed");
    close(fd);
    return -1;
    }

    close(fd);
    return 0;
    }
  4. 编译和运行程序。

    gcc rtc.c -o rtc
    ./rtc

5. 设备树简介

  1. RTC DTS 源文件在kernel-6.10/arch/arm64/boot/dts/rockchip/luckfox-core3576.dtsi已经定义,我们可以直接调用。

    &i2c2 {
    status = "okay";
    pinctrl-0 = <&i2c2m0_xfer>;
    hym8563: hym8563@51 {
    compatible = "haoyu,hym8563";
    reg = <0x51>;
    #clock-cells = <0>;
    clock-frequency = <32768>;
    clock-output-names = "hym8563";
    pinctrl-names = "default";
    pinctrl-0 = <&hym8563_int>;
    interrupt-parent = <&gpio0>;
    interrupts = <RK_PA5 IRQ_TYPE_LEVEL_LOW>;
    wakeup-source;
    };
    };

    &pinctrl {
    hym8563 {
    hym8563_int: hym8563-int {
    rockchip,pins = <0 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>;
    };
    };
    };