第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
所示。
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
所示。
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
所示。
#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
版权没有,但是转载请保留此段声明
============================================