linux 实时时钟(RTC)驱动【转】

本文转载自:http://blog.csdn.net/yaozhenguo2006/article/details/6820218

这个是linux内核文档关于rtc实时时钟部分的说明,此文档主要描述了rtc实时时钟的作用和编程接口,分别介绍了老的rtc接口和新的rtc类架构。并给出了一个测试rtc驱动的程序。

    linux 实时时钟(RTC)驱动

                                                                        翻译:窗外云天[email protected]

                                    最后矫正时间:2011.9.25

当linux开发者提到“实时时钟”的时候,他们通常所指的就是墙钟时间,这个时间是电池供电的,所以在系统掉电的情况下还能正常工作。除非在MS-Windows启动的时候设置,否则这个时钟不会同步于本地时区和夏令时间。事实上,他被设置成格林威治时间。
        最新的非pc体系的硬件趋向于记秒数,比如time(2)系统调用的输出,但是实时时钟用公历和24小时表示日期与时间,比如gmtime(3)的输出。
        linux提供两类的rtc兼容性很高的用户空间系统调用接口,如下所示:
       (1) /dev/rtc ... 这个RTC适合pc体系的系统,而并不适合非x86体系的系统
       (2) /dev/rtc0,/dev/rtc1 ... 他们依赖一种架构,这种架构在所有的系统上被RTC芯片广泛的支持。
        程序员必须知道,PC/AT的功能不总是有效,其他的系统可能会有另外的实现。这种情况下,如果在相同的系统结构上使用同样的RTC API,那么硬件会有不同的反应。例如,不是每一个RTC都提供IRQ,所以这些不能处理报警中断;标准的PC系统RTC只能处理未来24小时以内的闹钟,而其他系统的RTC可能处理未来一个世纪的任何时间。
        老的PC/AT驱动:/dev/rtc
        所有基于PC的系统(甚至Alpha体系的机器)都有一个集成的实时时钟。通常他们集成在计算机的芯片组内,但是也有一些系统是在主板上焊接着摩托罗拉MC146818(或者类似的芯片),他们给系统提供时间和日期,这个时间和日期在系统掉电后仍然会保存。
        ACPT(高级配置与电源管理接口)对MC146818的功能进行了标准化,并且在一些方面进行了功能扩展(提供了更长的定时周期,睡眠唤醒功能)。然而这些扩展的功能不适合老的驱动程序。
        这个RTC还可以产生频率从 2HZ 到 8192HZ 的信号,以2的乘方增长。这些信号通过中断信号线8报告给系统。这个RTC还可以用作定时限制为24小时的闹钟,当定时时间到时产生8号中断。这个闹钟可以设置成任意一个可编程值的子集,这意味着可以设置成任意小时的任意分钟任意秒,例如,可以将这个时钟设置成在每个clk产生中断,从而产生1hz的信号。
        这些中断通过/dev/rtc报告给系统(主设备号10,次设备号135,只读字符设备),中断传回一个无符号整数类型的数据。最低的位包含中断的类型(更新,闹钟,或者期),其他的字节代表了最后一次读到现在中断发生的次数。状态信息由虚拟文件/proc/driver/rtc产生,前提条件是使能了/proc文件系统。驱动应该提供锁机制,保证在同一时刻只有一个进程访问/dev/rtc。
        用户进程通过系统调用read(2)或者select(2)读取/dev/rtc来获取这些中断。当调用这两个系统调用的时候,进程会阻塞或者退出直到下一个中断到来。这个功能用在需要不频繁的获取数据而又不希望通过轮询当前时间而占用CPU时间的情况下。
        在高频率中断或者高系统负载下,用户进程应该检查从上次读取到现在发生中断的次数以判断是否有未处理的中断。例如,一个典型的 486-33 对/dev/rtc以大于1024hz的频率进行循环读,偶尔会产生中断积累(从上次读取到现在发生大于一次的中断)。鉴于此你应该检查读取数据的高字节,特别是在频率高于普通定时器中断--100hz的情况下。
        中断频率是可编程的或可以让他超过64hz,但是只有root权限的用户可以这样做。这样做可能有点保守,但是我们不希望有恶意的用户在一个较慢的386sx-16机器上产生很多中断,这样会严重影响系统的性能。我们可以通过向/proc/sys/dev/rtc/max-user-freq写入值来修改这个64hz的限制。但是注意你一定要这样做,减少中断处理程序的代码才会亡羊补牢,使对系统性能的影响降到最小。
        如果内核时间是和外部时钟源同步的,那么内核将每隔11分钟就会将时间写回CMOS时钟。在这个过程中,内核会关闭rtc周期中断,如果你的程序在做一些关键的工作一定要注意到。如果你的内核不和外部时钟源同步,那么内核会一直处理rtc中断,处理方式根据你具体的应用。
        闹钟和中断频率可以通过系统调用ioctl(2)来设置,ioctl的命令定义在./include/linux/rtc.h。与其长篇大论的介绍怎么样使用这个系统调用,还不如写一个实例程序来的方便,这个程序用来演示驱动的功能,对很多人来说用驱动程序提供的功能来进行应用编程他们会更感兴趣。在这个文档的最后有这段程序。
    新接口 “RTC类” 驱动:/dev/rtcn
        因为linux支持许多非ACPI非PC平台,其中一些平台有不只一个RTC,所以需要更多可移植性的设计,而不是仅仅在每个系统都实现类似MC146818的接口。在这种情况下,新的“RTC类”构架产生了。他提供不同的用户空间接口:
       (1) /dev/rtcn 和老的接口一样
       (2)/dev/class/rtc/rtcn   sysfs 属性,一些属性是只读的
       (3) /dev/driver/rtc 第一个rtc会使用procfs接口。更多的信息会显示在这里而不是sysfs。
        RTC类构架支持很多类型的RTC,从集成在嵌入式SOC处理器内的RTC到通过I2C,SPI和其他总线连接到CPU的芯片。这个架构甚至还支持PC系统的RTC,包括使用ACPI,PC的一些新特性。
        新架构也打破了“每个系统只有一个RTC”的限制。例如,一个低功耗电池供电的RTC是一个分离的I2C接口的芯片,但是系统可能还集成了一个多功能的RTC。系统可能从分离的RTC读取系统时钟,但是对于其他任务用集成的RTC,因为这个RTC提供更多的功能。
SYSFS 接口
--------------------
        在/sys/class/rtc/rtcn下面的sysfs接口提供了操作rtc属性的方法,而不用通过Ioclt系统调用。所有的日期时间都是墙钟时间,而不是系统时间。
date:                         RTC提供的日期
hctosys:                   如果在内核配置选项中配置了CONFIG_RTC_HCTOSYS,RTC会在系统启动的时候提供系统时间,这种情况下这个位就是1,否则为0
max_user_freq:     非特权用户可以从RTC得到的最大中断频率
name:                       RTC的名字,与sysfs目录相关
since_epoch:         从纪元开始所经历的秒数
time:                          RTC提供的时间
wakealarm:             唤醒时间的时间事件。 这是一种单次的唤醒事件,所以如果还需要唤醒,在唤醒发生后必须复位。这个域的数据结构或者是从纪元开始经历的妙数,或者是相对的秒数
IOCTL 接口
----------------------
        /dev/rtc支持的Ioctl系统调用,RTC类构架也支持。然而,因为芯片和系统没有一个统一的标准,一些PC/AT功能可能没有提供。以相同方式工作的一些新特性,--包括ACPI提供的,--在RTC类构架中表现出的,在老的驱动上不会工作。
     (1) RTC_RD_TIME,RTC_SET_TIME .. 每一个RTC都至少支持读时间这个命令,时间格式为公历和24小时制墙钟时间。最有用的特性是,这个时间可以更新。
     (2) RTC_ATE_ON,RTC_ATE_OFF,RTC_ALM_SET,RTC_ALM_READ ... 当RTC连接了一条IRQ线,他还能处理在未来24小时的报警中断。
     (3) RTC_WKALM_SET,RTC_WKALM_RD 。。。 RTCs 使用一个功能更强大的api,他可以处理超过24小时的报警时间。这个API支持设置更长的报警时间,支持单次请求的IRQ中断。
     (4) RTC_UIE_ON,RTC_UIE_OFF ... 如果RTC提供IRQ,他可能也提供每秒更新的IRQ中断。如果需要,RTC结构可以模仿这个机制。

(5) RTC_PIE_ON,RTC_PIE_OFF,RTC_IRQP_SET,RTC_IRQP_READ ... 如果一个IRQ是周期中断,那么这个IRQ还有可设置频率的特性(频率通常是2的n次方)

很多情况下,RTC报警时钟通常是一个系统唤醒事件,用于将Linux从低功耗睡眠模式唤醒到正常的工作模式。例如,系统会处于低功耗的模式下,直到时间到了去执行一些任务。注意这些ioctl的一些功能不必在你的驱动程序中实现。如果一个ioctl调用,你的驱动返回ENOIOCTLCMD,那么这个Ioctl就由通用RTC设备接口处理。下面是一些通用的例子:

(6) RTC_RD_TIME, RTC_SET_TIME: read_time/set_time 函数会被调用。
      (7) RTC_ALM_SET, RTC_ALM_READ, RTC_WKALM_SET, RTC_WKALM_RD: set_alarm/read_alarm 函数将会被调用.
      (8) RTC_IRQP_SET, RTC_IRQP_READ: irq_set_freq 函数将会调用,用来设置频率,RTC类构架会处理读请求,而频率保存在RTC设备结构中的irq_freq域。你的驱动需要在模块初始化的时候初始化irq_freq,你必须在irq_set_freq函数里检查设置的频率是否在硬件允许的范围。如果不是那么驱动应该返回-EINVAL。如果你不需要改变这个频率,那么不要定义irq_set_freq这个函数。
      (7) RTC_PIE_ON, RTC_PIE_OFF: irq_set_state 函数会被调用。

如果所有的ioctl都失败了,用下面的rtc-test.c检查一下你的驱动吧!

[cpp] view plain copy

    1. /*
    2. *      Real Time Clock Driver Test/Example Program
    3. *
    4. *      Compile with:
    5. *           gcc -s -Wall -Wstrict-prototypes rtctest.c -o rtctest
    6. *
    7. *      Copyright (C) 1996, Paul Gortmaker.
    8. *
    9. *      Released under the GNU General Public License, version 2,
    10. *      included herein by reference.
    11. *
    12. */
    13. #include <stdio.h>
    14. #include <linux/rtc.h>
    15. #include <sys/ioctl.h>
    16. #include <sys/time.h>
    17. #include <sys/types.h>
    18. #include <fcntl.h>
    19. #include <unistd.h>
    20. #include <stdlib.h>
    21. #include <errno.h>
    22. /*
    23. * This expects the new RTC class driver framework, working with
    24. * clocks that will often not be clones of what the PC-AT had.
    25. * Use the command line to specify another RTC if you need one.
    26. */
    27. static const char default_rtc[] = "/dev/rtc0";
    28. int main(int argc, char **argv)
    29. {
    30. int i, fd, retval, irqcount = 0;
    31. unsigned long tmp, data;
    32. struct rtc_time rtc_tm;
    33. const char *rtc = default_rtc;
    34. switch (argc) {
    35. case 2:
    36. rtc = argv[1];
    37. /* FALLTHROUGH */
    38. case 1:
    39. break;
    40. default:
    41. fprintf(stderr, "usage:  rtctest [rtcdev]\n");
    42. return 1;
    43. }
    44. fd = open(rtc, O_RDONLY);
    45. if (fd ==  -1) {
    46. perror(rtc);
    47. exit(errno);
    48. }
    49. fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n");
    50. /* Turn on update interrupts (one per second) */
    51. retval = ioctl(fd, RTC_UIE_ON, 0);
    52. if (retval == -1) {
    53. if (errno == ENOTTY) {
    54. fprintf(stderr,
    55. "\n...Update IRQs not supported.\n");
    56. goto test_READ;
    57. }
    58. perror("RTC_UIE_ON ioctl");
    59. exit(errno);
    60. }
    61. fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading %s:",
    62. rtc);
    63. fflush(stderr);
    64. for (i=1; i<6; i++) {
    65. /* This read will block */
    66. retval = read(fd, &data, sizeof(unsigned long));
    67. if (retval == -1) {
    68. perror("read");
    69. exit(errno);
    70. }
    71. fprintf(stderr, " %d",i);
    72. fflush(stderr);
    73. irqcount++;
    74. }
    75. fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:");
    76. fflush(stderr);
    77. for (i=1; i<6; i++) {
    78. struct timeval tv = {5, 0};     /* 5 second timeout on select */
    79. fd_set readfds;
    80. FD_ZERO(&readfds);
    81. FD_SET(fd, &readfds);
    82. /* The select will wait until an RTC interrupt happens. */
    83. retval = select(fd+1, &readfds, NULL, NULL, &tv);
    84. if (retval == -1) {
    85. perror("select");
    86. exit(errno);
    87. }
    88. /* This read won‘t block unlike the select-less case above. */
    89. retval = read(fd, &data, sizeof(unsigned long));
    90. if (retval == -1) {
    91. perror("read");
    92. exit(errno);
    93. }
    94. fprintf(stderr, " %d",i);
    95. fflush(stderr);
    96. irqcount++;
    97. }
    98. /* Turn off update interrupts */
    99. retval = ioctl(fd, RTC_UIE_OFF, 0);
    100. if (retval == -1) {
    101. perror("RTC_UIE_OFF ioctl");
    102. exit(errno);
    103. }
    104. test_READ:
    105. /* Read the RTC time/date */
    106. retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
    107. if (retval == -1) {
    108. perror("RTC_RD_TIME ioctl");
    109. exit(errno);
    110. }
    111. fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
    112. rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
    113. rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
    114. /* Set the alarm to 5 sec in the future, and check for rollover */
    115. rtc_tm.tm_sec += 5;
    116. if (rtc_tm.tm_sec >= 60) {
    117. rtc_tm.tm_sec %= 60;
    118. rtc_tm.tm_min++;
    119. }
    120. if (rtc_tm.tm_min == 60) {
    121. rtc_tm.tm_min = 0;
    122. rtc_tm.tm_hour++;
    123. }
    124. if (rtc_tm.tm_hour == 24)
    125. rtc_tm.tm_hour = 0;
    126. retval = ioctl(fd, RTC_ALM_SET, &rtc_tm);
    127. if (retval == -1) {
    128. if (errno == ENOTTY) {
    129. fprintf(stderr,
    130. "\n...Alarm IRQs not supported.\n");
    131. goto test_PIE;
    132. }
    133. perror("RTC_ALM_SET ioctl");
    134. exit(errno);
    135. }
    136. /* Read the current alarm settings */
    137. retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);
    138. if (retval == -1) {
    139. perror("RTC_ALM_READ ioctl");
    140. exit(errno);
    141. }
    142. fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",
    143. rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
    144. /* Enable alarm interrupts */
    145. retval = ioctl(fd, RTC_AIE_ON, 0);
    146. if (retval == -1) {
    147. perror("RTC_AIE_ON ioctl");
    148. exit(errno);
    149. }
    150. fprintf(stderr, "Waiting 5 seconds for alarm...");
    151. fflush(stderr);
    152. /* This blocks until the alarm ring causes an interrupt */
    153. retval = read(fd, &data, sizeof(unsigned long));
    154. if (retval == -1) {
    155. perror("read");
    156. exit(errno);
    157. }
    158. irqcount++;
    159. fprintf(stderr, " okay. Alarm rang.\n");
    160. /* Disable alarm interrupts */
    161. retval = ioctl(fd, RTC_AIE_OFF, 0);
    162. if (retval == -1) {
    163. perror("RTC_AIE_OFF ioctl");
    164. exit(errno);
    165. }
    166. test_PIE:
    167. /* Read periodic IRQ rate */
    168. retval = ioctl(fd, RTC_IRQP_READ, &tmp);
    169. if (retval == -1) {
    170. /* not all RTCs support periodic IRQs */
    171. if (errno == ENOTTY) {
    172. fprintf(stderr, "\nNo periodic IRQ support\n");
    173. goto done;
    174. }
    175. perror("RTC_IRQP_READ ioctl");
    176. exit(errno);
    177. }
    178. fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp);
    179. fprintf(stderr, "Counting 20 interrupts at:");
    180. fflush(stderr);
    181. /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
    182. for (tmp=2; tmp<=64; tmp*=2) {
    183. retval = ioctl(fd, RTC_IRQP_SET, tmp);
    184. if (retval == -1) {
    185. /* not all RTCs can change their periodic IRQ rate */
    186. if (errno == ENOTTY) {
    187. fprintf(stderr,
    188. "\n...Periodic IRQ rate is fixed\n");
    189. goto done;
    190. }
    191. perror("RTC_IRQP_SET ioctl");
    192. exit(errno);
    193. }
    194. fprintf(stderr, "\n%ldHz:\t", tmp);
    195. fflush(stderr);
    196. /* Enable periodic interrupts */
    197. retval = ioctl(fd, RTC_PIE_ON, 0);
    198. if (retval == -1) {
    199. perror("RTC_PIE_ON ioctl");
    200. exit(errno);
    201. }
    202. for (i=1; i<21; i++) {
    203. /* This blocks */
    204. retval = read(fd, &data, sizeof(unsigned long));
    205. if (retval == -1) {
    206. perror("read");
    207. exit(errno);
    208. }
    209. fprintf(stderr, " %d",i);
    210. fflush(stderr);
    211. irqcount++;
    212. }
    213. /* Disable periodic interrupts */
    214. retval = ioctl(fd, RTC_PIE_OFF, 0);
    215. if (retval == -1) {
    216. perror("RTC_PIE_OFF ioctl");
    217. exit(errno);
    218. }
    219. }
    220. done:
    221. fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
    222. close(fd);
    223. return 0;
    224. }
时间: 2024-10-05 21:10:33

linux 实时时钟(RTC)驱动【转】的相关文章

STM32学习笔记10(实时时钟RTC)

对于单片机转ARM的同学来说,RTC可能比较少接触.提到实时时钟,更经常想到的是DS1302.当然,在STM32里,自己一个CPU已经足够,不需要DS1302. 实际上,RTC就只一个定时器而已,掉电之后所有信息都会丢失,因此我们需要找一个地方来存储这些信息,于是就找到了备份寄存器.因为它掉电后仍然可以通过纽扣电池供电,所以能时刻保存这些数据.我们在本期教程中将详细讲述RTC原理及例程,以引导大家顺利进入RTC的世界. 1.STM32的RTC模块 RTC模块之所以具有实时时钟功能,是因为它内部维

RTC实时时钟驱动

RTC(Real-Time Clock)实时时钟为操作系统提供了一个可靠的时间,并且在断电的情况下,RTC实时时钟也可以通过电池供电,一直运行下去. RTC通过STRB/LDRB这两个ARM指令向CPU传送8位数据(BCD码).数据包括秒,分,小时,日期,天,月和年.RTC实时时钟依靠一个外部的32.768Khz的石英晶体,产生周期性的脉冲信号.每一个信号到来时,计数器就加1,通过这种方式,完成计时功能. RTC实时时钟有如下一些特性: 1,BCD数据:这些数据包括秒.分.小时.日期..星期几.

STM32F4学习笔记10——RTC实时时钟

RTC实时时钟 实时时钟 (RTC) 是一个独立的 BCD 定时器/计数器.RTC 提供一个日历时钟.两个可编程 闹钟中断,以及一个具有中断功能的周期性可编程唤醒标志.RTC 还包含用于管理低功耗模 式的自动唤醒单元. 两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒.分钟.小时(12 或 24 小时制).星 期几.日期.月份和年份.此外,还可提供二进制格式的亚秒值. 系统可以自动将月份的天数补偿为 28.29(闰年).30 和 31 天.并且还可以进行夏令时 补偿. 其它 32 位寄

RTC实时时钟

作者:宋老师,华清远见嵌入式学院讲师. 1.1 RTC介绍 在 一个嵌入式系统中,通常采用RTC 来提供可靠的系统时间,包括时分秒和年月日等,而且要求在系统处于关机状态下它也能够正常工作(通常采用后备电池供电).它的外围也不需要太多的辅助电 路,典型的就是只需要一个高精度的32.768kHz晶体和电阻电容等,如图10-8所示. 图10-8 RTC外接电路 1.2 RTC控制器 实时时钟(RTC)单元可以通过备用电池供电,因此,即使系统电源关闭,它也可以继续工作.RTC 可以通过STRB/LDRB

飞凌干货丨OK-xx18 Android实时时钟框架介绍

RTC(Real-TimeClock)实时时钟为操作系统提供了一个可靠的时间,并且在断电的情况下,RTC实时时钟也可以通过电池供电,一直运行下去.本文以OK-4418-C为例,介绍在Android实时时钟框架. 整体流程介绍由于实时时钟子系统,硬件抽象层.框架层.应用层的android开发包都已经做好,因此此接口的主要设计工作在于rx8010芯片的驱动的实现,包括芯片的初始化及配置时间,读取时间及接入实时时钟子系统等功能. 板子上电以后,内核驱动会初始化rtc芯片rx8010,并生成/dev/r

Linux RTC驱动模型分析

RTC简介 RTC(real-time clock)简称实时时钟,主要作用是用来记时,产生闹钟等.RTC因为有备份电池,所以即使计算机关机掉电,也不会影响RTC记时.而RTC和系统时间(主要靠软件模拟)的区别在于,RTC会在掉电后数据不丢失,在下次启动依旧可以重新设置当前时间给计算机.而系统时间主要靠软件模拟产生,在掉电之后会丢失,需要在下次计算机重新启动之后重新模拟产生.RTC时间在每次系统启动的时候会使用,在以后需要的时候会将设置的时间写入到RTC中,别的时候获取时间都通过软件可以获得. R

linux RTC 驱动模型分析【转】

转自:http://blog.csdn.net/yaozhenguo2006/article/details/6824970 RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间.RTC因为是电池供电的,所以掉电后时间不丢失.Linux内核把RTC用作“离线”的时间与日期维护器.当Linux内核启动时,它从RTC中读取时间与日期,作为基准值.在运行期间内核完全抛开RTC,以软件的形式维护系统的当前时间与日期,并在需要时将时间回写RTC芯片.另外如果RTC提供了IR

第43章 RTC—实时时钟

第43章     RTC-实时时钟 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 43.1 RTC简介 RTC-real time clock,实时时钟,主要包含日历.闹钟和自动唤醒这三部分的功能,其中的日历功能我们使用的最多.日历包含两个32bit的时间寄存器,可直接输出时分秒,星期.月.日.年.比起F103系列的RTC只能输出秒中断,剩下的其他时间需要软件来实现,429的

cortex_m3_stm32嵌入式学习笔记(十四):RTC实时时钟(秒中断)

STM32 的实时时钟( RTC)是一个独立的定时器. STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能.修改计数器的值可以重新设置系统当前的时间和日期. 由于时钟只需要配置一次,下次开机不需要重新配置(开发板有电池的情况下),所以需要用到备份区域(BKP)来标记是否配置过时钟 简单介绍BKP:备份寄存器是 42 个 16 位的寄存器( Mini 开发板就是大容量的),可用来存储 84 个字节的用户应用程序数据.他们处在备份域里, 当 VDD 电源被切