用户空间访问RTC

  • Post author:
  • Post category:其他




第1章用户空间访问


RTC






1.1


概述


RTC

是“

RealTimeClock”

的简称。

RTC

时钟一般由板载电池供电,系统掉电后仍可以照常运行,系统启动的时候从

RTC

读取时间作为系统时间的初始值,系统启动以后内核会根据系统中断不停的更新系统时间,并每过

11

分钟将内核维护的系统时间写入

RTC

一次。系统掉电之后,

RTC

的时间不会丢失,而且会根据输入的震荡时钟信号不停的更新自己的时间。



除了内核,用户空间也可以通过设备节点访问

RTC

。用户空间可以读取或者设置

RTC

的时间值,也可以在

RTC

的设备节点上睡眠,并设置

RTC

何时将自己唤醒,这对于需要定时或者周期性的唤醒的用户进程特别有用。



本章介绍用户空间通过设备节点使用

RTC

的方法,不涉及内核空间使用

RTC

的知识。用户需要注意的是,虽然

RTC

设备节点的接口是固定的(所有的

IOCTL

命令在所有

Linux

系统上也都是一致的),用户自己系统(或开发板)上的

RTC

设备节点也许并不支持某些功能,这依赖于实际使用的

RTC

芯片和

RTC

设备驱动。因此这里介绍的某些功能,用户或许无法在自己的系统(或开发板)上使用。





1.2


重要的数据结构



1.


rtc_time








RTC

的设备节点使用

ioctl()

接口的时候需要用到

rtc_time

结构体,它的定义如程序清单

1 .1

所示。






程序清单






1

.1 rtc_time







structrtc_time {








inttm_sec; /*





秒,





0











60











60





是闰秒的需要)





*/







inttm_min; /*





分钟,





0~59*/







inttm_hour; /*





小时,





0~23 */







inttm_mday; /*





本月中的第几天,





1~31 */







inttm_mon; /*





自一月以来的第几个月,





0~11*/







inttm_year; /*











1900





年以来的年数





*/







inttm_wday; /*





本周的第几天,





0~6





,星期天是





0 */







inttm_yday; /*





一年当中的第几天,





0~365*/







inttm_isdst; /*





夏令时标志





*/







};






对于最后一个成员变量





tm_isdst





,当它为正数的时候意味着正处在夏令时中,当是





0





的时候表示夏令时已经结束,负数表示不使用夏令时。





1992





年后中国大陆地区不再使用夏令时,因此这个成员应该总是负数。夏令时的意思是“





DaylightSaving Time”





,是在太阳升起较早的季节(通常是夏季)人为将时钟拨快一小时,让人们充分利用日光以节约能源。




2.


rtc_wkalrm






使用

ioctl()

设置

wakeupalarm

中断的时候需要用到

rtc_wkalrm

结构体做参数,它的定义如程序清单

1 .2

所示。






程序清单






1

.2 rtc_wkalrm







structrtc_wkalrm {








unsignedchar enabled; /* 0 =





禁止





alarm,1 =





使能





alarm */







unsignedchar pending; /* 0 = alarm





未挂起





,1 = alarm





挂起(已发生)





*/







structrtc_time time; /*





设置的





alarm





中断发生的时刻





*/







};







当设置


alarm


中断的时间的时候,第一个成员变量


enabled


影响设置完时间之后是否使能


alarm


中断。如果

enabled



0



alarm

中断的时间正常设置,但是中断功能将被禁止

(

即使设置之前是开启的

)

;而如果

enabled



1

的话,

RTC



alarm

功能将被开启。设置时间的时候第二个成员

pending

无效。



当使用

ioctl

读取

alarm

时间的时候,第一个成员

enabled

的值反应当前

alarm

中断的开启与否,第二个成员

pending

反应当前是否已有

alarm

中断挂起(发生)。



第三个成员是上面已经介绍过的

structrtc_time

结构体,用来传递要设置或者已设置的

alarm

中断时间。



对于

alarm



wakeupalarm

中断,将不会使用中文译名,因为无论定时中断、闹钟中断还是报警中断都不太合适。这两个中断在内核中其实是一回事,仅有的差别就是

alarm

中断只能设定为

24

小时内的某个时刻发生,而

wakeupalarm

中断可是设置为将来任意时刻发生。后面讲解的时候将会把它们两个做为一类中断来讲解。



3.读取的数据格式






RTC

设备节点上使用

read()

函数将会使进程进入睡眠,直到有一个中断发生。中断发生后从

RTC

设备节点只能读取一个

unsignedlong

类型的变量,无论发生了几次中断,一次只能读取一个

unsignedlong

。读取到的这个

unsignedlong

变量的最低一字节反应已经发生的中断类型,其余的字节反应自上次读取以来发生中断的次数。注意,如果自上次读取以来已发生了多钟类型的中断,并且每种类型的中断都发生了多次,根本没有办法判断每种中断类型各发生了几次。


RTC

共支持三种类型的中断:更新中断、周期中断和

alarm/wakeupalarm

中断。更新中断是在

RTC

的时间变化的时候发生的中断,由于

RTC

的最小单位是秒,所以更新中断的频率是

1HZ

;周期中断是频率高于

1HZ

的中断(必须高于

1HZ

),中断频率必须是

2^N



N>=1

,整数)

HZ

,并且只有

Root

用户能使用

64HZ

以上的频率;

alarm/wakeupalarm

中断是在未来某个时刻发生的中断,它只能发生一次,发生过后需要重新设置才能再次发生,其中

alarm

中断只能将中断发生时刻设定在

24

小时之内,

wakeupalarm

中断可以设定为未来的任意时刻。





RTC

设备节点读出的最低字节使用掩码表示发生的中断类型。掩码的定义如程序清单

1 .3

所示。






程序清单






1

.3





中断类型掩码






#defineRTC_IRQF 0x80 /*





任意中断发生它都会置位





*/







#defineRTC_PF 0x40 /*





周期中断发生后该位置位





*/







#defineRTC_AF 0x20 /* alarm/wakeup alarm





中断





*/







#defineRTC_UF 0x10 /*





更新中断





*/







当有多种类型的中断发生后而用户空间有没来得及读取的时候,会有多个位被置


1


。高字节中的中断次数是所有中断次数之和。





现在需要澄清一下


RTC


中断的含义。这里的中断是说在


RTC


设备节点上使用


read





select


休眠的进程被唤醒,每次中断都发生一次唤醒操作。即使没有进程在设备节点上休眠,中断状态也不会丢失,而是在下一次读取的时候被读出。这里中断的概念和内核中的中断不是一回事。





1.3


用户和


RTC


的接口



1.


/dev


接口






如果系统中有


udev


模块的话,系统启动以后会在


/dev


下生成


RTC


的设备节点。一般


RTC


设备节点分为两类:


/dev/rtc





/dev/rtcN





N>=0


)。其中


/dev/rtc





PC


机上使用的接口,因为最早的时候系统中只有一个


RTC


设备,设备节点的名字就是


rtc


,现在为了兼容以前的程序,仍然会建立这个设备节点,但是现在的


/dev/rtc


往往是一个指向其它设备节点的链接。随着嵌入式平台的兴起,


Linux


开始支持多个


RTC


设备了,因此必须用新的命名规则对不同的设备进行区分,所以产生了


/dev/rtcN





N>=0


)设备节点,


/dev/rtc


往往就是指向它们中某一个的链接。





上面说


Linux


已经支持多个


RTC


了,那么系统启动的时候从哪个


RTC


读取时间呢?答案取决于编译内核之前配置内核的选项。





假设现在某个开发板上有两个


RTC


设备,那么在


/dev


下的设备节点如下:





[root@zlg/]# ls /dev/rtc*







/dev/rtc /dev/rtc0 /dev/rtc1







[root@zlg/]# ls -l /dev/rtc*







lrwxrwxrwx 1 root root 4 Nov 25 17:32 /dev/

rtc -> rtc0








crw-r–r– 1 root root 254, 0 Nov 25 17:32 /dev/rtc0







crw-rw—- 1 root root 254, 1 Nov 25 17:32 /dev/rtc1







[root@zlg/]#







可以看到,


rtc


是指向


rtc0


的一个链接。据此可以推断系统使用


rtc0


来读取系统时间。





在这三个节点上可以使用


open





close





read





ioctl





select


等函数。有关这些函数的使用将会在后面介绍。



2.



proc


fs



接口






如果系统中使用了


procfs


,那么第一个


RTC


设备(


rtc0


)可以在


/proc/driver/rtc


中暴露自己的信息。





读取这个文件会得到类似下面的信息:





[root@zlg/]# cat /proc/driver/rtc







rtc_time : 18:45:50







rtc_date : 2011-01-03







24hr : yes





3.


sysfs


接口












/sys/class/






下会生成一个名为






rtc






的目录,这个目录中会为每一个注册的






rtc






设备生成一个子目录,名为






rtcN













N>=0






),每个子目录下的内容都是一致的,如下:






[root@zlg/]# ls /sys/class/rtc/







rtc0 rtc1







[root@zlg/]# ls /sys/class/rtc/rtc0







date device name since_epoch time







dev max_user_freq power subsystem uevent







[root@zlg/]# ls /sys/class/rtc/rtc1







date device name since_epoch time







dev max_user_freq power subsystem uevent







[root@zlg/]#






现在以






rtc0






为例访问用户比较关心的几个节点,它们的含义如下:






[root@zlg/]# cat /sys/class/rtc/rtc0/date /*





日期





*/







2040-01-03







[root@zlg/]# cat /sys/class/rtc/rtc0/name /*





设备名





*/







rtc-pcf8563







[root@zlg/]# cat /sys/class/rtc/rtc0/since_epoch /*











1970











1











1











00:00:00





以来的秒数





*/







2209231116







[root@zlg/]# cat /sys/class/rtc/rtc0/time /*





时间





*/







19:18:50







[root@zlg/]# cat /sys/class/rtc/rtc0/dev /*





设备节点号





*/







254:0







[root@zlg/]# cat /sys/class/rtc/rtc0/max_user_freq /*











Root





用户可使用的最大周期中断频率





*/







64







[root@zlg/]#







1.4IOCTL


命令





对于


RTC


设备在


/dev


下的设备节点可以使用


ioctl()


函数进行访问。这一小节介绍一下需要用到的


IOCTL


命令。



1.


alarm/wakeupalarm


中断操作






  • RTC_ALM_SET






    设置






    alarm






    中断的触发时刻,使用的方式如下:






ioctl(fd,RTC_ALM_SET, &rtc_tm);






  • 其中第三个参数是一个






    structrtc_time






    结构体,这个结构体的成员请参考程序清单

    1 .1

    。注意,






    alarm






    中断的触发时间只能是






    24






    小时内的一个时刻,所以只有时、分、秒的部分是有效的,





    structrtc_time





    的年、月、日部分会被忽略。为了保证正确性,





    structrtc_time











    tm_isdst





    成员应该设为





    -1











    如当前时间是





    13:00:00





    ,而传入的时刻是





    14:00:00





    ,则意味着






    alarm






    中断

    将在一小时后发生。而如果传入的是







    12:00:00







    ,则意味着明天中午发生中断。总之,







    alarm







    中断不能超过







    24







    小时。







  • RTC_ALM_READ






    读取已经设置的





    alarm





    中断时刻,使用的方式如下:






ioctl(fd,RTC_ALM_READ, &rtc_tm);







  • RTC_WKALM_SET






    设置





    wakeupalarm





    中断的触发时刻,它和





    alarm





    中断的唯一不同之处就是





    wakeupalarm





    中断的触发时刻可以在未来的任意时刻。它的使用方式如下:






ioctl(fd,RTC_WKALM_SET, &alarm);






  • 第三个参数是一个





    structrtc_wkalrm





    结构体,请参考程序清单

    1 .2







    structrtc_wkalrm





    的第一个成员





    enabled











    1





    的话可以使





    ioctl()





    返回的时候中断功能就已经开启了。如果这里里没有开启中断功能,可以使用





    RTC_AIE_ON





    命令来开启,这个命令的用法在下面。





    由于






    alarm













    wakeupalarm






    在内核中表现为一种中断,因此同一时刻只有一个是有效的。也就是说不可以使用






    alarm






    中断的同时使用






    wakeupalarm






    中断。只有最后一次设置的






    alarm













    wakeupalarm






    中断触发时刻是有效的。






  • RTC_WKALM_RD






    读取






    wakeupalarm






    中断的触发时刻。它的使用方式如下:






ioctl(fd,RTC_WKALM_RD, &alarm);







  • RTC_AIE_ON






    开启






    alarm/wakeupalarm






    中断。使用上面的命令设置了中断的时刻还不能启用中断,必须使用这个命令来开启。






  • RTC_AIE_OFF






    关闭





    alarm/wakeupalarm





    中断。但是不会改变已经设置的中断触发时刻。




2.更新中断(



updateint



)操作









  • RTC_UIE_ON







    开启






    RTC






    更新中断。更新中断每秒钟触发一次,因此频率为






    1HZ






    。有些






    RTC






    芯片或者






    RTC






    驱动不支持更新中断,可以使用软件来模拟,但前提是编译内核前配置内核时选择了软件模拟的功能,选项的路径如下:






DeviceDriver->RealTime Clock->RTC UIE emulation on dev interface






  • 开启和关闭更新中断都不需要传入参数。






  • RTC_UIE_OFF






    关闭





    RTC





    更新中断。




3.周期中断(



Periodicint



)操作






  • RTC_IRQP_SET






    设置周期中断的频率。使用的方法如下:






ioctl(fd,RTC_IRQP_SET, tmp);








  • tmp






    是一个






    unsignedlong






    类型的变量,它的值必须是






    2






    的幂,也就是






    2^N













    N>=1






    )。非






    Root






    用户无法使用






    64HZ






    以上的周期中断。









  • RTC_IRQP_READ







    读取周期中断的频率。使用方式如下:






ioctl(fd,RTC_IRQP_READ, &tmp);








  • tmp






    是一个






    unsignedlong






    类型的变量,它保存返回的周期中断频率。






  • RTC_PIE_ON






    开启周期中断功能。周期中断的频率设置好之后还不能立即使用,必须使用此命令来开启。






  • RTC_PIE_OFF






    关闭周期中断功能。之前设置的周期中断频率依然有效。




4.


RTC


时间操作








  • RTC_SET_TIME






    设置






    RTC






    的时间。这个命令和中断无关,用于更新






    RTC






    芯片的当前时间。使用的方式如下:






ioctl(fd,RTC_SET_TIME, &rtc_tm)






  • 第三个参数以一个






    structrtc_time






    变量,各个成员的含义请参考程序清单

    1 .1








  • RTC_RD_TIME












    读取





    RTC





    硬件中的当前时刻。使用的方式如下:






ioctl(fd,RTC_RD_TIME, &rtc_tm);







1.5


用户空间使用访问


RTC


的例程




用户空间使用





RTC





设备的方法就是设置好中断的参数然后使用





read





或者





select





在设备节点上睡眠,等待中断将自己唤醒后读取设备节点的数据来判断中断类型和次数。





本文撰写的依据是






2.6.27.8






版本的内核。在内核的






/driver/documentation/rtc.txt






中有一个用户空间访问






RTC






设备节点的例程





1.6  3250


开发板不支持的


RTC


功能






3250


开发板上有两个


RTC


设备,一个片外的


pcf8563


和一个片内的


RTC


。其中片


pcf8563


仅仅支持读取和设置时间,不支持硬件的更新中断和


alarm/wakeupalarm


中断。片内的


RTC


不支持更新中断,但是支持


alarm/wakeupalarm


中断。在


sys/class/rtc





/dev


下,


pcf8563


对应


rtc0


,片内


RTC


对应


rtc1


。内核的系统时间是从


rtc0


读取的,并不使用


rtc1






虽然


3250


开发板的两个


RTC


硬件上并不支持更新中断,但是可以开启软件模拟更新中断的功能,开启的方法是在编译内核前使用“


makemenuconfig”


命令来配置内核,使内核支持软件模拟更新中断的功能。选择这个功能的路径如下:





DeviceDriver->Real Time Clock->RTC UIE emulation on dev interface







选择之后重新编译内核并下载到开发板就可以支持软件模拟的更新中断了,但是对用户空间来说,和硬件更新中断是没有区别的。

============================================

作者:yuanlulu

http://blog.csdn.net/yuanlulu

版权没有,但是转载请保留此段声明

============================================



版权声明:本文为yuanlulu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。