Skip to main content

RTC

1.RTC

The RTC (Real-Time Clock) is a hardware module that can continue to keep track of time even when the microcontroller or embedded system loses power. It is typically equipped with a separate power supply, such as a common coin cell battery, to ensure that the time and date remain accurate even when the main power is disconnected. The Omni3576 development board uses the HYM8563 as the RTC. The HYM8563 is a low-power CMOS real-time clock/calendar chip that provides a programmable clock output, an interrupt output, and a power-down detector. All address and data transfers occur through an I2C bus interface, with a maximum bus speed of 400 Kbits/s. After each read or write operation, the internal address register automatically increments.

2. RTC Testing (Shell)

  1. Ensure that the rtc-hym8563 driver is loaded properly.

    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. Check and calibrate the system time and time zone.

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

2.1 Check RTC Time

  1. Use the SYSFS interface to check:

    cat /sys/class/rtc/rtc0/name   # Check RTC name
    cat /sys/class/rtc/rtc0/time # Check current RTC time
    cat /sys/class/rtc/rtc0/date # Check current RTC date
  2. Use the PROCFS interface to check:

    cat /proc/driver/rtc

2.2 Calibrate RTC Time with hwclock

The Linux system includes the hwclock utility for conveniently reading the current time and date from the RTC hardware clock.

  1. To view the current RTC time, use the following command:

    hwclock --show
  2. To write the system time to the RTC:

    hwclock --systohc
  3. To write the RTC time to the system time:

    hwclock --hctosys

3. RTC Testing (Python Program)

  1. Read RTC time. The get_rtc_time() function retrieves the RTC time by running the hwclock -r command.

    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. Set RTC time. The set_rtc_time() function sets the RTC time to the system's current time by running the hwclock -w command.

    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. Complete code:

    #!/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. Run the program:

    python3 rtc.py

4. RTC Testing (C Program)

Earlier, we discussed how to interact with the RTC using shell tools and Python programs. In practical programming, you can directly read and set the RTC by calling C library functions or system calls. For embedded Linux environments, accessing the RTC typically involves opening and manipulating the /dev/rtc or /dev/rtc0 device files. Please note that to run the program on a specific embedded system, you will usually need to cross-compile the code to create an executable file for the target development board. Let's go through the implementation steps.

  1. Read RTC time. The rtc_get_time() function retrieves the RTC time and formats the output.

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

    // Open RTC device file (/dev/rtc) in read-only mode
    fd = open("/dev/rtc", O_RDONLY);
    if (fd == -1) {
    perror("error open /dev/rtc");
    return -1;
    }

    // Use ioctl function to read RTC time from the opened device file
    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;
    }

    // Assign the read RTC time values to the provided 'time' parameter
    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. Set RTC time. The rtc_set_time() function sets the RTC time and formats the output for the set time.

    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
    };

    // Use ioctl function to set RTC time
    if (ioctl(fd, RTC_SET_TIME, &rtc_tm) == -1) {
    perror("RTC_SET_TIME ioctl failed");
    close(fd);
    return -1;
    }

    close(fd);
    return 0;
    }
  3. Complete code:

    #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. Compile and run the program.

    gcc rtc.c -o rtc
    ./rtc

5. Device Tree Overview

  1. The RTC DTS source file has been defined in kernel-6.10/arch/arm64/boot/dts/rockchip/luckfox-core3576.dtsi, and we can call it directly.

    &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>;
    };
    };
    };