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)
Ensure that the
rtc-hym8563driver 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)Check and calibrate the system time and time zone.
date
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
2.1 Check RTC Time
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 dateUse 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.
To view the current RTC time, use the following command:
hwclock --showTo write the system time to the RTC:
hwclock --systohcTo write the RTC time to the system time:
hwclock --hctosys
3. RTC Testing (Python Program)
Read RTC time. The
get_rtc_time()function retrieves the RTC time by running thehwclock -rcommand.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 NoneSet RTC time. The
set_rtc_time()function sets the RTC time to the system's current time by running thehwclock -wcommand.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}")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()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.
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;
}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;
}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;
}Compile and run the program.
gcc rtc.c -o rtc
./rtc
5. Device Tree Overview
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>;
};
};
};