RTC
RTC 全称为 Real Time Clock(实时时钟),是一个专门用来记录时间的硬件设备,一般可以集成在soc内部,或者选择外挂,通过i2c与主控通信。实时时钟芯片通过引脚对外提供时间读写接口,通常使用独立电池供电,以保证在外部系统关电时,芯片电路正常工作,时间正常运行。
不同的时钟芯片内部机制不一样,但在Linux系统中驱动封装了不同时钟芯片的操作细节,为应用程序提供了统一的时间操作接口。
注解
XNIUPI-R系列开发板均配备有外置RTC,可用于记录和提供精确时间,用户如需开启RTC功能,需要先接入纽扣电池。
1. 查看 RTC 资源
通过ls命令,可查看RTC芯片是否有被系统识别
ls /dev/rtc*
通过查看系统信息,可以确认被加载的rtc驱动,与硬件所用的芯片是否匹配。
dmesg | grep -i rtc-
确认驱动成功加载后,可通过下方命令访问驱动,读出RTC芯片的所有信息。
cat /proc/driver/rtc
2. RTC 时间设置
2.1 RTC 设置常用命令
date //修改系统时钟,具体命令使用可以man下
hwclock -h //查看hwclock帮助
hwclock -s //将硬件时间同步到系统时间
hwclock -w //将系统时间同步到硬件时间
timedatectl //显示系统时间等
2.2 RTC 时间设置方法
sudo date -s "2023-08-14 08:00:00" //手动设置时间
sudo hwclock -w //系统时间同步到硬件rtc
sudo hwclock -s //硬件rtc同步到系统
小技巧
以上手动设置时间或者网络同步时间后,-w将系统时间写入到硬件rtc,-s再将rtc时间写回系统,这样每次重启板卡都会进行rtc时间同步到系统时间。
2.3 RTC 时间同步网络时间
1、安装NTP服务
sudo apt update
sudo apt install ntp
2、启动NTP服务
sudo systemctl start ntp
3、使NTP服务开机自启
sudo systemctl enable ntp
4、检查时间同步状态
ntpq -p
3. RTC 功能测试
步骤一:装好RTC纽扣电池,将XNIUPI开发板/开发主机完全断电防止一段时间,然后不连接互联网的情况下开机
步骤二:通过以下指令查看RTC时间同步情况
dmesg | grep "rtc"
注解
如果没有输出rtc相关信息,说明RTC时间未同步。
步骤三:开机以后查看系统时间是否正常,如果正常则说明RTC时间同步成功。
4. 如何使用RTC
Linux 提供了三种用户空间调用接口。在板卡中对应的路径为:
SYSFS接口:/sys/class/rtc/rtc0/
PROCFS接口: /proc/driver/rtc
IOCTL接口: /dev/rtc0
SYSFS接口:
root@xniupi:~# cat /sys/class/rtc/rtc0/date
2023-06-19
root@xniupi:~# cat /sys/class/rtc/rtc0/time
03:15:22
PROCFS接口:
root@xniupi:~# cat /proc/driver/rtc
rtc_time : 07:06:56
rtc_date : 2023-06-19
alrm_time : 03:18:50
alrm_date : 2023-06-19
alarm_IRQ : no
alrm_pending : no
update IRQ enabled : no
periodic IRQ enabled : no
periodic IRQ frequency : 1
max user IRQ frequency : 64
24hr : yes
root@xniupi:~#
IOCTL接口:
可以参考内核文档作为例子
tools/testing/selftests/timers/rtctest.c
5. RTC 电路设计及内核配置
5.1 RTC 功能电路设计
5.2 RTC 功能Kernel配置
1、内核添加对hym8563的支持
直接修改SDK下面 /kernel/arch/arm/configs/rockchip_linux_deconfig文件 查看是否有如下配置: CONFIG_RTC_DRV_HYM8563=y 如果没有增加该配置,并查看是否有其他RTC功能配置(如RK808/RK809内置RTC配置),如有,则删除。
2、DTS新增RTC设备节点
可以参考如下DTS设备节点:
&i2c5 {
status = "okay";
hym8563: hym8563@51 {
compatible = "haoyu,hym8563";
irq-gpios = <&gpio0 27 GPIO_ACTIVE_HIGH>;
reg = <0x51>;
#clock-cells = <0>;
clock-frequency = <32768>;
clock-output-names = "xin32k";
/* rtc_int is not connected */
status = "okay";
};
};
3、编译内核
参考编译章节
4、烧录镜像,测试功能
1、查看挂载RTC的I2C总线
查看RTC是否已挂载在i2c总线上
2、查看RTC驱动加载日志
dmesg | grep "rtc"
6. RTC C语言编程实例
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;
}