Linux操作系统内核对RTC的编程详解(4)

文章作者 100test 发表时间 2007:03:14 16:39:46
来源 100Test.Com百考试题网


7.2.3 内核对RTC的操作

如前所述,Linux内核与RTC进行互操作的时机只有两个:(1)内核在启动时从RTC中读取启动时的时间与日期;(2)内核在需要时将时间与日期回写到RTC中。为此,Linux内核在arch/i386/kernel/time.c文件中实现了函数get_cmos_time()来进行对RTC的第一种操作。显然,get_cmos_time()函数仅仅在内核启动时被调用一次。而对于第二种操作,Linux则同样在arch/i386/kernel/time.c文件中实现了函数set_rtc_mmss(),以支持向RTC中回写当前时间与日期。下面我们将来分析这二个函数的实现。 在分析get_cmos_time()函数之前,我们先来看看RTC芯片对其时间与日期寄存器组的更新原理。

(1)Update In Progress

当控制寄存器B中的SET标志位为0时,MC146818芯片每秒都会在芯片内部执行一个“更新周期”(Update Cycle),其作用是增加秒寄存器的值,并检查秒寄存器是否溢出。如果溢出,则增加分钟寄存器的值,如此一致下去直到年寄存器。在“更新周期”期间,时间与日期寄存器组(0x00~0x09)是不可用的,此时如果读取它们的值将得到未定义的值,因为MC146818在整个更新周期期间会把时间与日期寄存器组从CPU总线上脱离,从而防止软件程序读到一个渐变的数据。

在MC146818的输入时钟频率(也即晶体增荡器的频率)为4.194304MHZ或1.048576MHZ的情况下,“更新周期”需要花费248us,而对于输入时钟频率为32.768KHZ的情况,“更新周期”需要花费1984us=1.984ms。控制寄存器A中的UIP标志位用来表示MC146818是否正处于更新周期中,当UIP从0变为1的那个时刻,就表示MC146818将在稍后马上就开更新周期。在UIP从0变到1的那个时刻与MC146818真正开始Update Cycle的那个时刻之间时有一段时间间隔的,通常是244us。也就是说,在UIP从0变到1的244us之后,时间与日期寄存器组中的值才会真正开始改变,而在这之间的244us间隔内,它们的值并不会真正改变。如下图所示:

(2)get_cmos_time()函数

该函数只被内核的初始化例程time_init()和内核的APM模块所调用。其源码如下:

/* not static: needed by APM */ 

unsigned long get_cmos_time(void) 

{ 

unsigned int year, mon, day, hour, min, sec. 

int i. 



/* The Linux interpretation of the CMOS clock register contents: 

* When the Update-In-Progress (UIP) flag goes from 1 to 0, the 

* RTC registers show the second which has precisely just started. 

* Lets hope other operating systems interpret the RTC the same way. 

*/ 

/* read RTC exactly on falling edge of 0update flag */ 

for (i = 0 . i < 1000000 . i  ) /* may take up to 1 second... */ 

if (CMOS_READ(RTC_FREQ_SELECT) &. RTC_UIP) 

break. 

for (i = 0 . i < 1000000 . i  ) /* must try at least 2.228 ms */ 

if (!(CMOS_READ(RTC_FREQ_SELECT) &. RTC_UIP)) 

break. 

do { /* Isnt this overkill ? UIP above should guarantee consistency */ 

sec = CMOS_READ(RTC_SECONDS). 

min = CMOS_READ(RTC_MINUTES). 

hour = CMOS_READ(RTC_HOURS). 

day = CMOS_READ(RTC_DAY_OF_MONTH). 

mon = CMOS_READ(RTC_MONTH). 

year = CMOS_READ(RTC_YEAR). 

} while (sec != CMOS_READ(RTC_SECONDS)). 

if (!(CMOS_READ(RTC_CONTROL) &. RTC_DM_BINARY) || RTC_ALWAYS_BCD) 

{ 

BCD_TO_BIN(sec). 

BCD_TO_BIN(min). 

BCD_TO_BIN(hour). 

BCD_TO_BIN(day). 

BCD_TO_BIN(mon). 

BCD_TO_BIN(year). 

} 

if ((year  = 1900) < 1970) 

year  = 100. 

return mktime(year, mon, day, hour, min, sec). 

}

对该函数的注释如下:

(1)在从RTC中读取时间时,由于RTC存在Update Cycle,因此软件发出读操作的时机是很重要的。对此,get_cmos_time()函数通过UIP标志位来解决这个问题:第一个for循环不停地读取RTC频率选择寄存器中的UIP标志位,并且只要读到UIP的值为1就马上退出这个for循环。第二个for循环同样不停地读取UIP标志位,但他只要一读到UIP的值为0就马上退出这个for循环。这两个for循环的目的就是要在软件逻辑上同步RTC的Update Cycle,显然第二个for循环最大可能需要2.228ms(TBUC max(TUC)=244us 1984us=2.228ms)



相关文章


Linux操作系统内核对RTC的编程详解(5)
Linux系统编程之C 游戏程序优化(1)
Linux操作系统内核对RTC的编程详解(4)
Linux操作系统内核对RTC的编程详解(3)
Linux操作系统内核对RTC的编程详解(1)
澳大利亚华人论坛
考好网
日本华人论坛
华人移民留学论坛
英国华人论坛