RTC(x86)

Posted terrytian88

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RTC(x86)相关的知识,希望对你有一定的参考价值。

RTC

一直以来想写一篇关于RTC的总结,可是人太懒,在读完John Z. Sonmez大伽的《软技能代码之外的生存技能》后,终于下定决心,完成这项早已计划中的任务。首先声明,本文是以PC为例来阐述RTC工作的基本原理。

 

RTC的基本概念

RTC(Real Time Clock),实时时钟,是存在于PC(x86)及类PC架构的电路中,其主要的作用是记录设备关机时的时间及在设备开机时提供时间基准,也就是说在设备机器关电的时候,记录下当时的时间,在设备启动时为设备内部的时间提供基准值,从而使得设备内部的时间值不是从初值开始,而是从RTC记录并运行的时间开始。从这个意义上RTC有被成为墙上时间(walltimer)。

 

RTC电路

聪明的你,先来看下RTC电路

 

其中RTCX1,RTCX2, RTCRST#, SRTCRST#连接到PCH上, 所以上RTC的input和output都是受PCH的控制的,也就是说RTC提供的时间是给PCH的。

 

RTCX1为晶振的input,RTCX2为晶振的output,也就是晶振反馈值。

 

RTCRST#的主要作用是用来清楚CMOS的,当然它也可以用来检测电池的电压是否低于2V。

 

SRTCRST#用来当电池更换时清除Intel ME相关的寄存器。

 

Vbatt时由安装的一颗3V左右(一般为2.8V~3.3V)的纽扣电池(一般都为CR2302,提供最大10mA的电流)提供,这颗电池的作用时在机器移除AC电源后, RTC电路仍然可以正常工作。当这颗电池的电压不足的时候,就会出现RTC时间不准确。

 

 Xtal是由一颗频率为32 .768KHz的石英晶振提供的, 此晶振的功能是提供给计时电路用的基准clock。

 

肖特基二极管D1的作用:当AC电源连接时,也就是VCCRTC(3.3V)有电时,D1不导通,电池不提供电流给RTC电路;当AC电源移除后,D1导通,电池工作。

 

RTC工作原理

提起RTC,不得不提起PC系统的另外一个时间,系统时间。在Linux和Windows中,这个时间都是内核在维护的,也就是说在Linux等操作系统中,有两套时间在运行。

 

系统时间的精度可以做到微妙级,而相对应的RTC的时间精度只能做到秒级。一般情况下系统时间的精度为24小时最大漂移1.2秒,RTC的时间的漂移就要大的多。这就造成了系统时间的精确的要远远高于RTC。所以当系统运行了一段时间后,会出现系统时间和RTC不一致的情况,这是就需要我们定期将两个时间进行同步。

 

RTC和CMOS

RTC的时间和设置都保存在CMOS RAM中,详细的如下表,其中前10个字节(offset 00 ~ 09h)存储着RTC确切的时间,也就是你在Bios setup utility中看到的时间,当然这个时间是变动的,剩下的都是RTC的配置字节。可以通过70/71h端口去操作,也可以通过ioctl命令去配置。

 

Offset Hex

Offset Dec

Field Size

Function

00h

0

1 byte

RTC seconds.  Contains the seconds value of current time

01h

1

1 byte

RTC seconds alarm.  Contains the seconds value for the RTC alarm

02h

2

1 byte

RTC minutes.  Contains the minutes value of the current time

03h

3

1 byte

RTC minutes alarm.  Contains the minutes value for the RTC alarm

04h

4

1 byte

RTC hours.  Contains the hours value of the current time

05h

5

1 byte

RTC hours alarm.  Contains the hours value for the RTC alarm

06h

6

1 byte

RTC day of week.  Contains the current day of the week

07h

7

1 byte

RTC date day.  Contains day value of current date

08h

8

1 byte

RTC date month.  Contains the month value of current date

09h

9

1 byte

RTC date year.  Contains the year value of current date

0Ah

10

1 byte

Status Register A 

 

 

 

Bit 7 = Update in progress (0 = Date and time can be read, 1 = Time update in progress)

 

 

 

Bits 6-4 = Time frequency divider (010 = 32.768KHz

 

 

 

Bits 3-0 = Rate selection frequency (0110 = 1.024KHz square wave frequency)

0Bh

11

1 byte

Status Register B

 

 

 

Bit 7 = Clock update cycle (0 = Update normally, 1 = Abort update in progress)

 

 

 

Bit 6 = Periodic interrupt (0 = Disable interrupt (default), 1 = Enable interrupt)

 

 

 

Bit 5 = Alarm interrupt (0 = Disable interrupt (default), 1 = Enable interrupt)

 

 

 

Bit 4 = Update ended interrupt (0 = Disable interrupt (default), 1 = Enable interrupt)

 

 

 

Bit 3 = Status register A square wave frequency (0 = Disable square wave (default), 1 = Enable square wave)

 

 

 

Bit 2 = 24 hour clock (0 = 24 hour mode (default), 1 = 12 hour mode)

 

 

 

Bit 1 = Daylight savings time (0 = Disable daylight savings (default), 1 = Enable daylight savings)

0Ch

12

1 byte

Status Register C - Read only flags indicating system status conditions

 

 

 

Bit 7 = IRQF flag

 

 

 

Bit 6 = PF flag

 

 

 

Bit 5 = AF flag

 

 

 

Bit 4 UF flag

 

 

 

Bits 3-0 = Reserved

0Dh

13

1 byte

Status Register D - Valid CMOS RAM flag on bit 7 (battery condition flag)

 

 

 

Bit 7 = Valid CMOS RAM flag (0 = CMOS battery dead, 1 = CMOS battery power good)

 

 

 

Bit 6-0 = Reserved

0Eh

14

1 byte

Diagnostic Status

 

 

 

Bit 7 = Real time clock power status (0 = CMOS has not lost power, 1 = CMOS has lost power)

 

 

 

Bit 6 = CMOS checksum status (0 = Checksum is good, 1 = Checksum is bad)

 

 

 

Bit 5 = POST configuration information status (0 = Configuration information is valid, 1 = Configuration information in invalid)

 

 

 

Bit 4 = Memory size compare during POST (0 = POST memory equals configuration, 1 = POST memory not equal to configuration)

 

 

 

Bit 3 = Fixed disk/adapter initialization (0 = Initialization good, 1 = Initialization bad)

 

 

 

Bit 2 = CMOS time status indicator (0 = Time is valid, 1 = Time is invalid)

 

 

 

Bit 1-0 = Reserved

 

Linux下RTC相关函数

如果你通过70/71h端口去访问RTC的数据,在linux下有两种方式,第一种是通过函数inb级outb去操作,读取的

#include <sys/io.h>

iopl(3);

unsigned char index, data;

outb(index, 0x70);

data = inb(0x71);

 

第二种方式是通过访问/dev/port。

   unsigned char index, data;

 

       int fp = open("/dev/port",O_RDWR);

   if(fp >0)

{

        lseek(fp, 0x70, SEEK_SET);

        write(fp, &index, 1);

        lseek(fp, 0x71, SEEK_SET);

        read(fp, data, 1);

}

close(fp);

以上都是以读取为例的,你可以将每一中方式最后一行的inb改为outb, read改为write就将读取改为设置了。

 

以上两种方式是常见的方式,实际上linux提供以一系列的ioctl命令去操作RTC的读取及配置。强烈推荐这种方式。

X86 架构的Linux下存在/dev/rtc这个字符型设备文件,有些有多个rtc设备的系统,可能会有/dev/rtc0…. /dev/rtc1等等。

 

首先需要打开rtc设备文件

#include <linux/rtc.h>

struct rtc_time rtc;

int fd = open("/dev/rtc", O_RDONLY);

if(fd <= 0)

{

       fd = open("/dev/rtc0", O_RDONLY);

  }

然后运行ioctl命令,

ret = ioctl(fd,RTC_RD_TIME, &rtc);

详细的rtc的ioctl命令可以参考linux/rtc.h文件

/*

 * Generic RTC interface.

 * This version contains the part of the user interface to the Real Time Clock

 * service. It is used with both the legacy mc146818 and also  EFI

 * Struct rtc_time and first 12 ioctl by Paul Gortmaker, 1996 - separated out

 * from <linux/mc146818rtc.h> to this file for 2.4 kernels.

 *

 * Copyright (C) 1999 Hewlett-Packard Co.

 * Copyright (C) 1999 Stephane Eranian <[email protected]>

 */

#ifndef _LINUX_RTC_H_

#define _LINUX_RTC_H_

 

/*

 * The struct used to pass data via the following ioctl. Similar to the

 * struct tm in <time.h>, but it needs to be here so that the kernel

 * source is self contained, allowing cross-compiles, etc. etc.

 */

 

struct rtc_time {

        int tm_sec;

        int tm_min;

        int tm_hour;

        int tm_mday;

        int tm_mon;

        int tm_year;

        int tm_wday;

        int tm_yday;

        int tm_isdst;

};

 

/*

 * This data structure is inspired by the EFI (v0.92) wakeup

 * alarm API.

 */

struct rtc_wkalrm {

        unsigned char enabled;  /* 0 = alarm disabled, 1 = alarm enabled */

        unsigned char pending;  /* 0 = alarm not pending, 1 = alarm pending */

        struct rtc_time time;   /* time the alarm is set to */

};

 

/*

 * Data structure to control PLL correction some better RTC feature

 * pll_value is used to get or set current value of correction,

 * the rest of the struct is used to query HW capabilities.

 * This is modeled after the RTC used in Q40/Q60 computers but

 * should be sufficiently flexible for other devices

 *

 * +ve pll_value means clock will run faster by

 *   pll_value*pll_posmult/pll_clock

 * -ve pll_value means clock will run slower by

 *   pll_value*pll_negmult/pll_clock

 */

 

struct rtc_pll_info {

        int pll_ctrl;       /* placeholder for fancier control */

        int pll_value;      /* get/set correction value */

        int pll_max;        /* max +ve (faster) adjustment value */

        int pll_min;        /* max -ve (slower) adjustment value */

        int pll_posmult;    /* factor for +ve correction */

        int pll_negmult;    /* factor for -ve correction */

        long pll_clock;     /* base PLL frequency */

};

 

/*

 * ioctl calls that are permitted to the /dev/rtc interface, if

 * any of the RTC drivers are enabled.

 */

 

 * Data structure to control PLL correction some better RTC feature

 * pll_value is used to get or set current value of correction,

 * the rest of the struct is used to query HW capabilities.

 * This is modeled after the RTC used in Q40/Q60 computers but

 * should be sufficiently flexible for other devices

 *

 * +ve pll_value means clock will run faster by

 *   pll_value*pll_posmult/pll_clock

 * -ve pll_value means clock will run slower by

 *   pll_value*pll_negmult/pll_clock

 */

 

struct rtc_pll_info {

        int pll_ctrl;       /* placeholder for fancier control */

        int pll_value;      /* get/set correction value */

        int pll_max;        /* max +ve (faster) adjustment value */

        int pll_min;        /* max -ve (slower) adjustment value */

        int pll_posmult;    /* factor for +ve correction */

        int pll_negmult;    /* factor for -ve correction */

        long pll_clock;     /* base PLL frequency */

};

 

/*

 * ioctl calls that are permitted to the /dev/rtc interface, if

 * any of the RTC drivers are enabled.

 */

 

#define RTC_AIE_ON      _IO(‘p‘, 0x01)  /* Alarm int. enable on         */

#define RTC_AIE_OFF     _IO(‘p‘, 0x02)  /* ... off                      */

#define RTC_UIE_ON      _IO(‘p‘, 0x03)  /* Update int. enable on        */

#define RTC_UIE_OFF     _IO(‘p‘, 0x04)  /* ... off                      */

#define RTC_PIE_ON      _IO(‘p‘, 0x05)  /* Periodic int. enable on      */

#define RTC_PIE_OFF     _IO(‘p‘, 0x06)  /* ... off                      */

#define RTC_WIE_ON      _IO(‘p‘, 0x0f)  /* Watchdog int. enable on      */

#define RTC_WIE_OFF     _IO(‘p‘, 0x10)  /* ... off                      */

 

#define RTC_ALM_SET     _IOW(‘p‘, 0x07, struct rtc_time) /* Set alarm time  */

#define RTC_ALM_READ    _IOR(‘p‘, 0x08, struct rtc_time) /* Read alarm time */

#define RTC_RD_TIME     _IOR(‘p‘, 0x09, struct rtc_time) /* Read RTC time   */

#define RTC_SET_TIME    _IOW(‘p‘, 0x0a, struct rtc_time) /* Set RTC time    */

#define RTC_IRQP_READ   _IOR(‘p‘, 0x0b, unsigned long)   /* Read IRQ rate   */

#define RTC_IRQP_SET    _IOW(‘p‘, 0x0c, unsigned long)   /* Set IRQ rate    */

#define RTC_EPOCH_READ  _IOR(‘p‘, 0x0d, unsigned long)   /* Read epoch      */

#define RTC_EPOCH_SET   _IOW(‘p‘, 0x0e, unsigned long)   /* Set epoch       */

 

#define RTC_WKALM_SET   _IOW(‘p‘, 0x0f, struct rtc_wkalrm)/* Set wakeup alarm*/

#define RTC_WKALM_RD    _IOR(‘p‘, 0x10, struct rtc_wkalrm)/* Get wakeup alarm*/

 

#define RTC_PLL_GET     _IOR(‘p‘, 0x11, struct rtc_pll_info)  /* Get PLL correction */

#define RTC_PLL_SET     _IOW(‘p‘, 0x12, struct rtc_pll_info)  /* Set PLL correction */

 

/* interrupt flags */

#define RTC_IRQF 0x80 /* any of the following is active */

常用的是RTC_RD_TIME和RTC_SET_TIME, RTC_AIE_OFF, RTC_UIE_OFF,RTC_PIE_OFF尽量少用,或者说在没有搞懂原理之前,不要使用,因为此3项配置可能会带来机器一些异常动作。

                                                           

Linux RTC驱动

RTC的驱动代码在kernel代码的drivers/rtc目录下。在此目录下会看到一大堆文件,这是因为linux支持众多rtc设备,本文是以x86架构的rtc为例的。

 

所有的Linux驱动无非都有几个关于设备的函数, rtc的在class.c文件中。

struct rtc_device *rtc_device_register(const char *name, struct device *dev,

                                   const struct rtc_class_ops *ops,

                                   struct module *owner)

用来rtc 设备的注册。

 

void rtc_device_unregister(struct rtc_device *rtc)

移除注册的rtc设备类

static void rtc_device_release(struct device *dev)

 

hwclock工具

 

RTC的测试

 

总结

 

 

 未完待续。。。。。。

 

以上是关于RTC(x86)的主要内容,如果未能解决你的问题,请参考以下文章

STM32之RTC配置与初始化-rtc.h rtc.c

HAL库 STM32CubeMX实现RTC时钟

如何找到内核加载的RTC模块?

Linux RTC驱动模型分析

RT-Thread&ART-PI硬件RTC+软件RTC外设

RT-Thread&ART-PI硬件RTC+软件RTC外设