1. RTC介绍
RTC的英文全称是Real-Time Clock,翻译过来是实时时钟芯片。实时时钟芯片通过引脚对外提供时间读写接口,通常使用独立电池供电,以保证在外部系统关电时,芯片电路正常工作,时间正常运行。
不同的时钟芯片内部机制不一样,但在Linux系统中驱动封装了不同时钟芯片的操作细节,为应用程序提供了统一的时间操作接口。
1.1 Orin-Nano的RTC资源
EASY EAI Orin-Nano 【默认不带】RTC电路。若想底板支持RTC功能,可通过使用我司的RTC模块进行【扩展】。
扩展RTC的具体操作:【首先】要把底板【断电】,然后再把模块【正面朝上】地插入到底板的40PIN接口上,如下图所示。
插稳后,再进行【上电】操作。
通过ls命令,可查看RTC芯片是否有被系统识别
ls /dev/rtc*
确认驱动成功加载后,可通过下方命令访问驱动,读出RTC芯片的所有信息。
cat /proc/driver/rtc
1.2 RTC时间的读写
这里涉及2个时钟,RTC芯片时钟以及系统时钟。手动管理RTC时钟操作的本质就是:同步时钟(要么把系统时钟同步到RTC芯片时钟上,要么把RTC芯片时钟同步到系统时钟)。
系统时钟:系统时钟本质是一个64位的整数,这个整数代表当前与Epoch Time的时间差(以秒为单位),我们称之为时间戳。这个时钟由CPU主芯片定时器维护,CPU掉电后,时钟信息就会丢失。操作系统时钟的命令为date。
date #查询系统时间
date -s "2023-09-20 11:18:00" #修改系统时间
注:
* Epoch Time:是指一个特定的时间。1970年1月1日0时0分0秒。假设现在距离1970年1月1日0时0分0秒走了N秒,在Linux系统里,时间数值就是N。
RTC芯片时钟:RTC芯片内部所维护的时间。在系统掉电后由电池进行供电。因此系统电源掉电后RTC时间仍然能够正常运行,RTC芯片时钟的作用是在Linux不运行时,依然可以保持时间信息。
芯片时钟同步到系统时钟。
sudo hwclock --hctosys
系统时钟同步到芯片时钟(或者是sudo hwclock -w)。
sudo hwclock --systohc
如果只想查询RTC芯片时钟,但不同步到系统时钟,可以采用以下命令。
sudo hwclock -r
1.3 时区和校时服务
RTC时钟和系统时钟用的都是UTC时间,不同地区所使用的时间,还需要考虑上时区的影响。另外:RTC时钟除了可以被手动操作,校时服务也会影响RTC时钟。EASY-EAI-Orin-Nano采用ntpd作为校时服务,若用户对其机制不熟悉,可查阅文档《系统时间管理介绍》。
2. 快速上手
2.1 例程源码下载
到【百度网盘】上下载相关的单例程序:
链接:https://pan.baidu.com/s/1RXHMGpmGSEfFy0rb1VkXSg?pwd=1234
提取码: 1234
比如在windows环境中,就把单例程序下载到:此电脑\D:\BaiduNetdisk (无规定,用户可自主选择),如下图所示。
然后把例程【复制粘贴】到nfs挂载目录中。(不清楚目录如何构建的,可以参考《入门指南/开发环境准备/nfs服务搭建与挂载》)
2.2 例程编译&运行
通过adb shell进入开发板环境(不清楚如何通过adb进行调试,可以参考《入门指南/开发板调试方式介绍/adb调试》),执行下方命令定位到demo目录,并且执行编译操作。
cd /home/orin-nano/Desktop/nfs/12_IIC/
./build.sh
编译成功后,会生成一个test-rtc的可执行程序在Release目录中。
执行下方命令则可运行测试demo,如下所示。
sudo ./Release/test-rtc
执行效果如下所示。
3. C语言使用案例
RTC的C语言使用案例,代码地址为12_RTC/test-rtc/main.c,供用户编码参考。以下代码展示了对RTC时钟的读写操作流程:
int main(int argc, char const *argv[])
{
const char *strDateTime = "2023-09-21 15:22:37";
// 将字符串转换为tm结构体类型的时间信息
struct tm tm = {0};
strptime(strDateTime, "%Y-%m-%d %H:%M:%S", &tm);
// 打开RTC设备
int rtc_fd = open("/dev/rtc0", O_RDWR);
if (rtc_fd < 0) {
perror("open RTC device /dev/rtc0 faild.");
close(rtc_fd);
return -1;
}
printf("---设置参数前日期时间---\n");
system("date");
/*** 1.关闭网络校时服务 ***/
system("systemctl stop ntp.service");
/*** 2.将预设好的时间写入【RTC时钟】 ***/
struct rtc_time rtc_tm;
rtc_tm.tm_sec = tm.tm_sec;
rtc_tm.tm_min = tm.tm_min;
rtc_tm.tm_hour = tm.tm_hour;
rtc_tm.tm_mday = tm.tm_mday;
rtc_tm.tm_mon = tm.tm_mon;
rtc_tm.tm_year = tm.tm_year;
if (ioctl(rtc_fd, RTC_SET_TIME, &rtc_tm) < 0) {
perror("set data time to rtc0");
perror("RTC时间设置失败");
close(rtc_fd);
return -1;
}
/*** 3.将【RTC时钟】同步回【系统时钟】 ***/
// 读出刚才写入的RTC时钟参数
if (ioctl(rtc_fd, RTC_RD_TIME, &rtc_tm) < 0) {
perror("RTC时间读取失败");
close(rtc_fd);
return -1;
}
close(rtc_fd);
tm.tm_sec = rtc_tm.tm_sec;
tm.tm_min = rtc_tm.tm_min;
tm.tm_hour = rtc_tm.tm_hour;
tm.tm_mday = rtc_tm.tm_mday;
tm.tm_mon = rtc_tm.tm_mon;
tm.tm_year = rtc_tm.tm_year;
struct timeval tv;
tv.tv_sec = mktime(&tm);
tv.tv_usec = 0;
// 同步时间到系统时钟
if(0 != settimeofday(&tv, (struct timezone *)0)){
perror("系统时间设置失败");
}
printf("---设置参数后日期时间---\n");
system("date");
return 0;
}