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 功能电路设计

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