RTC
在本章中,我们将学习如何读写 RTC 的日期和时间,以及如何实现系统校时。注意:只有 RV1106 内置 RTC,RV1103 无内置 RTC。
示例程序:Code.zip
1.RTC
RTC(Real-Time Clock, 实时时钟)是一种能在微控制器或嵌入式系统掉电情况下仍能持续计时的硬件模块。它通常配备独立持久电源供应源,如常见的纽扣电池,以确保即使主电源断开,也能保持时间和日期的准确性。这一特性使得RTC在众多依赖于实时可靠时间戳的应用场景中扮演了关键角色,诸如数据记录、设备唤醒定时以及保持系统时间同步等。
2.RTC读写应用程序
2.1 读写RTC(Shell)
在 Linux 操作系统环境中,与 RTC 进行交互通常依赖于特定的系统命令和接口。以下详细介绍了如何运用 Shell 命令来实现 RTC 时间的读取与设置:
2.1.1 读取RTC时间
Linux 系统中内置了 hwclock
工具,用于便捷地读取 RTC 硬件时钟提供的当前时间和日期。只需执行如下命令,即可展示 RTC 上的精确时间:
hwclock --show
该命令将输出类似于以下格式的时间信息:
Sat Jan 13 12:00:00 2024 0.000000 seconds
2.1.2 设置RTC时间
若要将当前系统时间同步到RTC,可使用 hwclock
命令的 --systohc
选项完成这一操作:
hwclock --systohc
反之,如果需要从 RTC 获取时间并同步到系统时间,可以使用 --hctosys
选项:
hwclock --hctosys
2.1.3 校准RTC时间
对于需要对 RTC 时间进行校准的情况,首要步骤是确保系统时间的准确性,然后将已校准的系统时间同步到 RTC。具体操作流程请参阅"系统校时"部分说明。
2.2 读写RTC(Python程序)
Python 程序通过调用外部 hwclock
命令与系统交互,提供了获取和设置 RTC 时间的功能。
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()
下面对代码进行解析:
2.2.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.2.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}")
2.2.3 运行程序
使用 nano 工具在终端创建 py 文件,粘贴并保存 python 程序
# nano rtc.py
运行程序
# python3 rtc.py
实验现象
输出获取到的 RTC 时间,同步系统时间到 RTC:
2.3 读写RTC(C程序)
在前文中,我们介绍了如何使用 shell 工具、Python 程序与 RTC 进行交互。而在实际的编程实践中,我们可以通过调用 C 库函数或系统调用来直接读取和设置 RTC。对于嵌入式 Linux 环境,访问 RTC 通常涉及打开并操作 /dev/rtc
或 /dev/rtc0
设备文件。请注意,为了在特定的嵌入式系统上运行程序,通常需要使用交叉编译工具来编译代码,以生成可在目标开发板上执行的可执行文件。接下来,让我们一起探讨具体的实施步骤。
2.3.1 读取RTC时间
通过调用该函数,实现读取 RTC 时间。
int rtc_get_time(struct tm *time) {
int ret,fd;
struct rtc_time rtc_tm;
fd=open("/dev/rtc", O_RDONLY);
if(fd == -1) {
perror("error open /dev/rtc");
return -1;
}
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;
}
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;
}
下面对函数进行解析:
打开RTC设备
以只读模式打开 RTC 设备文件(/dev/rtc
)
fd=open("/dev/rtc", O_RDONLY);
if(fd == -1) {
perror("error open /dev/rtc");
return -1;
}
读取RTC时间
调用 ioctl
函数从打开的设备文件中读取 RTC 时间
ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
if(ret == -1) {
perror("error RTC_RD_TIME ioctl");
close(fd);
return -1;
}
存储RTC时间
将读取到的 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;
2.3.2 设置RTC时间
通过调用该函数,实现设置 RTC 时间。
int rtc_set_time(struct tm *time) {
int fd, ret;
struct rtc_time rtc_tm;
if (!time) {
printf("time is NULL\n");
return -1;
}
fd = open("/dev/rtc",O_RDWR);
if(fd == -1) {
perror("error open /dev/rtc");
return -1;
}
rtc_tm.tm_sec = time->tm_sec ;
rtc_tm.tm_min = time->tm_min ;
rtc_tm.tm_hour = time->tm_hour ;
rtc_tm.tm_mday = time->tm_mday ;
rtc_tm.tm_mon = time->tm_mon - 1;
rtc_tm.tm_year = time->tm_year - 1900;
ret = ioctl(fd, RTC_SET_TIME, &rtc_tm);
if(ret == -1) {
perror("error RTC_SET_TIME ioctl");
close(fd);
return -1;
}
close(fd);
return 0;
}
下面对函数进行解析:
打开RTC设备
以读写模式打开 RTC 设备文件(/dev/rtc
)
fd = open("/dev/rtc",O_RDWR);
if(fd == -1) {
perror("error open /dev/rtc");
return -1;
}
设置RTC时间
将传入的参数 time 的值赋给rtc_tm
,准备进行 RTC 时间的设置
rtc_tm.tm_sec = time->tm_sec ;
rtc_tm.tm_min = time->tm_min ;
rtc_tm.tm_hour = time->tm_hour ;
rtc_tm.tm_mday = time->tm_mday ;
rtc_tm.tm_mon = time->tm_mon - 1;
rtc_tm.tm_year = time->tm_year - 1900;
调用 ioctl
函数设置RTC时间
ret = ioctl(fd, RTC_SET_TIME, &rtc_tm);
if(ret == -1) {
perror("error RTC_SET_TIME ioctl");
close(fd);
return -1;
}
2.3.3 主函数
主函数首先获取当前 RTC 时间,然后设置指定的 RTC 时间
int main() {
int ret;
struct tm *input_time = NULL;
struct tm *output_time = NULL;
input_time = (struct tm *)malloc(sizeof(struct tm));
if (!input_time) {
printf("Malloc input struct tm fail!\n");
return -1;
}
output_time = (struct tm *)malloc(sizeof(struct tm));
if (!output_time) {
printf("Malloc output struct tm fail!\n");
return -1;
}
memset(input_time, 0, sizeof(struct tm));
memset(output_time, 0, sizeof(struct tm));
ret = rtc_get_time(output_time);
if (ret)
printf("rtc_get_time fail!\n");
else
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);
input_time->tm_year = 2024;
input_time->tm_mon = 1;
input_time->tm_mday = 13;
input_time->tm_hour = 12;
input_time->tm_min = 0;
input_time->tm_sec = 0;
ret = rtc_set_time(input_time);
if (ret)
printf("rtc_set_time fail!\n");
else
printf("SET RTC TIME : %04d-%02d-%02d,%02d:%02d:%02d\n",input_time->tm_year, input_time->tm_mon,
input_time->tm_mday, input_time->tm_hour, input_time->tm_min, input_time->tm_sec);
free(input_time);
free(output_time);
return 0;
}
下面对函数进行解析:
读取RTC时间
通过调用 rtc_get_time() 函数实现读取 RTC 时间,并格式化输出读取到的时间
ret = rtc_get_time(output_time);
if (ret)
printf("rtc_get_time fail!\n");
else
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);
设置RTC时间
通过调用 rtc_set_time() 函数实现设置 RTC 时间,并格式化输出设置时间
ret = rtc_set_time(input_time);
if (ret)
printf("rtc_set_time fail!\n");
else
printf("SET RTC TIME : %04d-%02d-%02d,%02d:%02d:%02d\n",input_time->tm_year, input_time->tm_mon,
input_time->tm_mday, input_time->tm_hour, input_time->tm_min, input_time->tm_sec);
2.3.4 交叉编译
指定交叉编译工具
首先,我们要将交叉编译工具的路径添加到系统的
PATH
环境变量中,以便可以在任何地方使用交叉编译工具,您可以在shell配置文件中添加以下行(通常是~/.bashrc
或~/.bash_profile
或~/.zshrc
,具体取决于您使用的shell),注意PATH=
后的路径为交叉编译工具所在的目录。gcc路径
<SDK Directory>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
打开shell配置文件
vi ~/.bashrc
将交叉编译工具的路径添加到系统的PATH环境变量中,将 <SDK Directory> 修改为自己的 SDK 路径,如
/home/luckfox/luckfox-pico/
export PATH=<SDK Directory>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin:$PATH
重新加载shell配置文件,使更改生效
source ~/.bashrc
使用交叉编译工具编译程序
arm-rockchip830-linux-uclibcgnueabihf-gcc rtc.c -o rtc
交叉编译成功后,将在当前目录下生成可在开发板运行的可执行文件
# ls
rtc rtc.c
2.3.5 运行程序
文件传输
先将
rtc
从虚拟机传输到 Windows,再通过 TFTP 或 ADB 传输到开发板,将文件从 Windows 通过 ADB 将文件传输到开发板的步骤如下:adb push 文件所在路径 开发板存储路径
eg:(将当前目录下rtc文件传输到开发板的根目录)
adb push rtc /运行程序
修改
rtc
文件的操作权限后运行程序# chmod +x rtc
# ./rtc实验现象
获取RTC时间,设置RTC时间:
3.系统校时
下面我们介绍如何在Buildroot中添加ntpd软件包,并将ntpd配置为客户端模式以进行网络时间同步。
3.1 添加ntpd软件包
按 / 搜索 "ntpd",找到 "ntp" 下的 "ntpd",按 2 进行跳转(详细步骤请参考SDK环境部署的内核配置部分)
按 Y 分别 "ntp" 和 "ntpd"(使能 "ntp" 后会显示 "ntpd")
3.2 重新烧录固件
编译选择分支,指定开发板型号
luckfox@luckfox:~/luckfox-pico$ ./build.sh lunch
编译
luckfox@luckfox:~/luckfox-pico$ ./build.sh
重新烧录固件
编译完成后重新烧录固件
3.3 网络校时
修改时区
打开文件
vi /etc/profile
添加内容
export TZ=CST-8
重新加载配置文件
source /etc/profile
查看当前时区
date -R
终止ntpd进程
# ps | grep ntpd
204 root /usr/sbin/ntpd -g -p /var/run/ntpd.pid
423 root grep ntpd
# kill -9 204网络校时
# ntpd -p cn.ntp.org.cn -qn
8 Dec 14:27:51 ntpd[423]: ntpd 4.2.8p15@1.3728-o Fri Dec 8 06:12:46 UTC 2023 (1): Starting
8 Dec 14:27:51 ntpd[423]: Command line: ntpd -p cn.ntp.org.cn -qn
8 Dec 14:27:51 ntpd[423]: ----------------------------------------------------
8 Dec 14:27:51 ntpd[423]: ntp-4 is maintained by Network Time Foundation,
8 Dec 14:27:51 ntpd[423]: Inc. (NTF), a non-profit 501(c)(3) public-benefit
8 Dec 14:27:51 ntpd[423]: corporation. Support and training for ntp-4 are
8 Dec 14:27:51 ntpd[423]: available at https://www.nwtime.org/support
8 Dec 14:27:51 ntpd[423]: ----------------------------------------------------
8 Dec 14:27:51 ntpd[423]: proto: precision = 1.166 usec (-20)
8 Dec 14:27:51 ntpd[423]: basedate set to 2023-11-26
8 Dec 14:27:51 ntpd[423]: gps base set to 2023-11-26 (week 2290)
8 Dec 14:27:53 ntpd[423]: restrict: ignoring line 11, address/host '[::1]' unusable.
8 Dec 14:27:53 ntpd[423]: Listen and drop on 0 v4wildcard 0.0.0.0:123
8 Dec 14:27:53 ntpd[423]: Listen normally on 1 lo 127.0.0.1:123
8 Dec 14:27:53 ntpd[423]: Listen normally on 2 eth0 192.168.10.148:123
8 Dec 14:27:53 ntpd[423]: Listen normally on 3 usb0 172.32.0.93:123
8 Dec 14:27:53 ntpd[423]: Listening on routing socket on fd #20 for interface updates
8 Dec 14:28:00 ntpd[423]: ntpd: time slew -0.019767 s
ntpd: time slew -0.019767s将系统时间同步到hwclock
hwclock --systohc
查看hwclock
hwclock
开机启动时,从hwclock同步到系统时间
hwclock -u -s