改為重新編譯,下載內(nèi)核到板子上,boot后可以見(jiàn)到/dev/rtc0節(jié)點(diǎn),hwclock命令也有效了。我使用busybox的hwclock和date命令來(lái)設(shè)置RTC,為什么不行呢?
下面是我的順序操作步驟:
[root@myboad:~]# date -s 042215452009
Wed Apr 22 15:36:00 UTC 2009
[root@myboad:~]# date
Wed Apr 22 15:45:25 UTC 2009
[root@myboad:~]# hwclock
Wed Dec 31 23:59:59 1969 0.000000 seconds
[root@myboad:~]# hwclock -w
[root@myboad:~]# hwclock
Wed Dec 31 23:59:59 1969 0.000000 seconds
[root@myboad:~]# date
Wed Apr 22 15:45:52 UTC 2009
使用date命令時(shí),輸出的是我設(shè)置的時(shí)間,使用hwclock-w命令把日期時(shí) 間寫入RTC硬件,沒(méi)有提示錯(cuò)誤,然后使用hwclock查看,時(shí)間沒(méi)有設(shè)置成功,使用date還是自己設(shè)的日期時(shí)間。
這問(wèn)題出在哪呢?真是搞不懂?!∈紫?,明確一個(gè)問(wèn)題:
1、嵌入式系統(tǒng)板子上的時(shí)間是用date標(biāo)準(zhǔn)系統(tǒng)命令查看的,date是SHELL命令,例如busybox或者uClinux上的sash等。這個(gè)時(shí)間是有運(yùn)行起來(lái)的嵌入式LINUX
軟件
維護(hù)的,其實(shí)就是內(nèi)存中的一個(gè)全局變量,LINUX默認(rèn)啟動(dòng)給這個(gè)全局變量賦值就是19700101這樣的數(shù)值。
2、RTC
芯片
(很多是嵌入式
處理器
內(nèi)置RTC模塊,那么就是CPU內(nèi)部寄存器)內(nèi)部的寄存器維護(hù)的時(shí)間值。
一般的,LINUX啟動(dòng)后,您可以通過(guò)date命令來(lái)設(shè)置更改系統(tǒng)時(shí)間,但掉電就會(huì)丟失的,啟動(dòng)后又是1970這樣的時(shí)間了。若要能date設(shè)置后保存系統(tǒng)時(shí)間,使得在下次重啟后還能保持的話,就必須有RTC+后備電池的軟
硬件
支持。
例如,我們PC上可以設(shè)置系統(tǒng)時(shí)間,重啟后也不會(huì)丟失,就是因?yàn)槲覀働C
主板
上有RTC支持。
RTC可以是外接的一個(gè)芯片,例如常見(jiàn)的X1226/1227等,它們就是通過(guò)I2C接到處理器上的。
當(dāng)然,現(xiàn)在更多的情況是CPU內(nèi)置RTC模塊,這樣您硬件設(shè)計(jì)的話就只要提供后備電池即可。
明確了系統(tǒng)時(shí)間的兩個(gè)概念后,我們來(lái)看看RTC的實(shí)現(xiàn)機(jī)制。
在嵌入式系統(tǒng)上,實(shí)現(xiàn)的方法可以靈活多樣,只要能達(dá)到最終的目的:
您可通過(guò)某種操作獲取當(dāng)前的正確的時(shí)間,而且重啟不會(huì)丟失。
那么看看幾種實(shí)現(xiàn)機(jī)制。
在開始介紹幾種方法前,我們先說(shuō)明一下軟件時(shí)間的方式:
我們的平臺(tái)是嵌入式LINUX,要實(shí)現(xiàn)RTC支持,則必須是“驅(qū)動(dòng)+應(yīng)用程序”的方式,而我們的驅(qū)動(dòng)都建議是采用MODULES方式獨(dú)立加載的方式,這樣可不影響整個(gè)LINUX內(nèi)核。
下面開始介紹實(shí)現(xiàn)方法:
從上面可以看到,時(shí)間實(shí)際上是兩個(gè)地方同時(shí)在維護(hù)的,一個(gè)是RTC芯片內(nèi)部寄存器或CPU的RTC寄存器;另一個(gè)則是LINUX維護(hù)的時(shí)間。LINUX的時(shí)間重啟就會(huì)丟失,而RTC由于有后備電池保護(hù),則不會(huì)丟失,在板子斷電后還可以繼續(xù)維持計(jì)時(shí)。所以,最好理解的實(shí)現(xiàn)方式就是讓LINUX內(nèi)核啟動(dòng)的時(shí)候,從RTC芯片里面讀取時(shí)間值,賦給LINUX的時(shí)間變量。這樣LINUX一啟動(dòng)時(shí)間就校正過(guò)來(lái),不再是1970了。當(dāng)然,這樣做,就不能用獨(dú)立的RTC驅(qū)動(dòng)的MODULES形式了。而當(dāng)您通過(guò)date命令設(shè)置LINUX時(shí)間時(shí),您還要修改date命令的代碼,使之同時(shí)還要通過(guò)I2C修改RTC芯片內(nèi)部寄存器數(shù)值(或CPU內(nèi)部寄存器數(shù)值),當(dāng)然了,這樣還是需要一個(gè)讀寫RTC的驅(qū)動(dòng)的。
下面則是一個(gè)更簡(jiǎn)化的實(shí)現(xiàn)方法
即LINUX啟動(dòng)時(shí),不從RTC芯片里面讀取時(shí)間,而您直接修改date命令的代碼,讓它不要從LINUX提供的接口讀取,而是直接通過(guò)驅(qū)動(dòng)從RTC里面直接讀取。
另外,如果您的系統(tǒng)允許的話,您都可以不走date的路線,即讀取系統(tǒng)時(shí)間不用date命令也可以,可以自己直接寫個(gè)讀取時(shí)間的函數(shù),例如read_rtc/write_rtc,就用這兩個(gè)函數(shù)取代date命令讀取和設(shè)置系統(tǒng)時(shí)間的功能。
呵呵,寫了這么多,好像也沒(méi)說(shuō)清楚,最后,大家記?。?br /> 我們看到的時(shí)間,實(shí)際是在兩個(gè)不同的地方維護(hù)的
一個(gè)是LINUX維護(hù)的,一個(gè)是RTC芯片里面的。
這樣就存在一個(gè)兩個(gè)時(shí)間同步的問(wèn)題,一個(gè)發(fā)生在LINUX啟動(dòng)的時(shí)候,需要從RTC里面獲取時(shí)間;另一個(gè)發(fā)生在您設(shè)置系統(tǒng)時(shí)間的時(shí)候,需要兩個(gè)同時(shí)更改。
當(dāng)然了,中間一些貓膩就可以發(fā)生,例如您可以偷懶跳過(guò)LINUX時(shí)間,讓date或者您自己的代碼直接讀取RTC時(shí)間,而完全不理會(huì)LINUX的時(shí)間(還讓它是1970...吧)
在ARM9實(shí)驗(yàn)箱等板子上,我們是通過(guò)修改busybox的date.c代碼來(lái)實(shí)現(xiàn)的的;而在HHGW-L
IT
E-R3等HHPPC平臺(tái)上則是通過(guò)自己寫的writeRTC來(lái)作的。
前一種方法改變了系統(tǒng)運(yùn)行方式,后一種則沒(méi)有把硬件時(shí)間同LINUX系統(tǒng)時(shí)間聯(lián)系起來(lái)。在
嵌入式Linux
中,與設(shè)置時(shí)間相關(guān)的常用命令有兩個(gè):date和hwclock。
1、date命令用于顯示或設(shè)定當(dāng)前系統(tǒng)時(shí)間,其格式如下:
date [-參數(shù)] [日期和時(shí)間格式]
在 嵌入式 領(lǐng)域的常用參數(shù)-s:設(shè)置時(shí)間
示例:
顯示當(dāng)前系統(tǒng)時(shí)間:
[root@EM9X60 /]# date
設(shè)置時(shí)間為2010年12月23日下午16:50:00:
[root@EM9X60 /]# date -s “2010-12-23 16:50:00”
修改時(shí)間為16:54:20:
[root@EM9X60 /]# date -s 16:54:20
2、hwclock命令用于顯示或設(shè)定硬件實(shí)時(shí)時(shí)鐘RTC,其格式如下:
hwclock [-參數(shù)]
在 嵌入式 領(lǐng)域的常用參數(shù)--systohc:將硬件時(shí)鐘RTC調(diào)整為與當(dāng)前系統(tǒng)時(shí)間一致
示例:
顯示當(dāng)前RTC時(shí)間:
[root@EM9X60 /]# hwclock
將設(shè)置好的系統(tǒng)時(shí)間存入實(shí)時(shí)時(shí)鐘RTC:
[root@EM9X60 /]# hwclock –systohc
如上所述,用戶如需修改英利
嵌入式Linux工控主板
的時(shí)間,有兩種方法,一種是通過(guò)超級(jí)終端直接鍵入命令進(jìn)行修改,具體方法如前文命令介紹中的示例;另一種是在應(yīng)用程序中通過(guò)函數(shù)system調(diào)用date和hwclock命令進(jìn)行時(shí)間修改,具體代碼如下:
// 生成時(shí)間設(shè)置命令字符串
sprintf(str,'date -s %s', timestr );
// 設(shè)置系統(tǒng)時(shí)間
system( str );
//將系統(tǒng)時(shí)間寫入到RTC硬件中,以保留設(shè)置。這一操作是為了將修改好的時(shí)間寫入到RTC中保存。如果不進(jìn)行這一步操作,則重新上電開機(jī)以后系統(tǒng)從RTC中讀取到的仍然是原來(lái)的時(shí)間
system('hwclock --systohc');首先搞清楚RTC在kernel內(nèi)的作用:
linux
系統(tǒng)有兩個(gè)時(shí)鐘:一個(gè)是由主板電池驅(qū)動(dòng)的“Real Time Clock”也叫做RTC或者叫
CMOS
時(shí)鐘,
硬件時(shí)鐘。當(dāng)
操作系統(tǒng)
關(guān)機(jī)的時(shí)候,用這個(gè)來(lái)記錄時(shí)間,但是對(duì)于運(yùn)行的系統(tǒng)是不用這個(gè)時(shí)間的。
另一個(gè)時(shí)間是 “System clock”也叫內(nèi)核時(shí)鐘或者軟件時(shí)鐘,是由軟件根據(jù)時(shí)間中斷來(lái)進(jìn)行計(jì)數(shù)的,
內(nèi)核時(shí)鐘在系統(tǒng)關(guān)機(jī)的情況下是不存在的,所以,當(dāng)操作系統(tǒng)啟動(dòng)的時(shí)候,內(nèi)核時(shí)鐘是要讀取RTC時(shí)間
來(lái)進(jìn)行時(shí)間同步。并且在系統(tǒng)關(guān)機(jī)的時(shí)候?qū)⑾到y(tǒng)時(shí)間寫回RTC中進(jìn)行同步。如前所述,Linux內(nèi)核與RTC進(jìn)行互操作的時(shí)機(jī)只有兩個(gè):
1) 內(nèi)核在啟動(dòng)時(shí)從RTC中讀取啟動(dòng)時(shí)的時(shí)間與日期;
2) 內(nèi)核在需要時(shí)將時(shí)間與日期回寫到RTC中。系統(tǒng)啟動(dòng)時(shí),內(nèi)核通過(guò)讀取RTC來(lái)初始化內(nèi)核時(shí)鐘,又叫墻上時(shí)間,該時(shí)間放在xtime變量中。
The current time of day (the wall time) is defined inkernel/timer.c:
struct timespec xtime;
The timespec data structure is defined in as:struct timespec{
time_t tv_sec;
long tv_nsec;
};
問(wèn)題1:系統(tǒng)啟動(dòng)時(shí)在哪讀取RTC的值并設(shè)置內(nèi)核時(shí)鐘進(jìn)行時(shí)間同步的呢?
最有可能讀取RTC設(shè)置內(nèi)核時(shí)鐘的位置應(yīng)該在arch/arm/kernel/time.c里的time_init函數(shù)內(nèi).time.c為系統(tǒng)的時(shí)鐘驅(qū)動(dòng)部分.time_init函數(shù)會(huì)在系統(tǒng)初始化時(shí),由init/main.c里的start_kernel函數(shù)內(nèi)調(diào)用.
X86
架構(gòu)就是在這里讀RTC值并初始化系統(tǒng)時(shí)鐘xtime的. ARM架構(gòu)的time_init代碼如下:void __inittime_init(void)
{
if (system_timer->offset == NULL)
system_timer->offset = dummy_gettimeoffset;
system_timer->init();#ifdef CONFIG_NO_IDLE_HZ
if (system_timer->dyn_tick)
system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED;
#endif
} 上面system_timer->init()實(shí)際執(zhí)行的是時(shí)鐘驅(qū)動(dòng)體系架構(gòu)相關(guān)(具體平臺(tái))部分定義的init函數(shù),若是s3c2410平臺(tái),則執(zhí)行的為arch/arm/mach-s3c2410/time.c里定義的s3c2410_timer_init函數(shù).不過(guò)s3c2410_timer_init()也沒(méi)有讀RTC的代碼.整個(gè)時(shí)鐘驅(qū)動(dòng)初始化的過(guò)程大致就執(zhí)行這些代碼.既然在系統(tǒng)時(shí)鐘驅(qū)動(dòng)初始化的過(guò)程中沒(méi)有讀RTC值并設(shè)置內(nèi)核時(shí)鐘,那會(huì)在哪設(shè)置呢?我搜了一下,發(fā)現(xiàn)內(nèi)核好象只有在arch/cris/kernel/time.c里有RTC相關(guān)代碼,如下:
//讀RTC的函數(shù)
unsigned long get_cmos_time(void)
{
unsigned int year, mon, day, hour, min, sec;
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);
…………
return mktime(year, mon, day, hour, min, sec);
} 這個(gè)函數(shù)會(huì)在update_xtime_from_cmos內(nèi)被調(diào)用:
void update_xtime_from_cmos(void)
{
if(have_rtc) {
xtime.tv_sec = get_cmos_time();
xtime.tv_nsec = 0;
}
} 另外還有設(shè)置rtc的函數(shù)
int set_rtc_mmss(unsigned long nowtime);不過(guò)我加了printk測(cè)試了一下,好象arch/cris/kernel/time.c這個(gè)文件和這兩個(gè)函數(shù)只是適用與X86?
arm平臺(tái)啟動(dòng)時(shí)并不走這邊.因此執(zhí)行不到這些函數(shù)。
那arm平臺(tái)啟動(dòng)時(shí),系統(tǒng)是在哪讀RTC的值并對(duì)內(nèi)核時(shí)鐘(WallTime)進(jìn)行初始化的呢? 已解決:
嵌入式Linux內(nèi)核(arm)是在系統(tǒng)啟動(dòng)時(shí)執(zhí)行/etc/init.d/hwclock.sh腳本,這個(gè)腳本會(huì)調(diào)用hwclock小程序讀取RTC的值并設(shè)置系統(tǒng)時(shí)鐘。
(換句話說(shuō),這要取決于你制作的文件系統(tǒng)里是否有這樣的腳本)
DAEMON1=/sbin/hwclock
start() {
local RET ERROR= [ ! -f /etc/adjtime ]&&echo"0.0 00.0">/etc/adjtime

log_status_msg"Setting the System Clock using the Hardware Clockas reference..."-n # Copies Hardware Clock time to System Clockusing the correct
# timezone for hardware clocks in local time, and setskernel
# timezone. DO NOT REMOVE.
[""!= no ]&& --hctosys #
# Now that /usr/share/zoneinfo should be available,
# announce the local time.
#
log_status_msg"System Clock set. Local time: `date`"
log_status_msg""
return 0
}
hwclock最先讀取的設(shè)備文件是 /dev/rtc ,busybox里面的hwclock是這樣實(shí)現(xiàn)的:
static int xopen_rtc(int flags)
{
int rtc; if (!rtcname) {
rtc = open("/dev/rtc", flags);
if (rtc>= 0)
return rtc;
rtc = open("/dev/rtc0", flags);
if (rtc>= 0)
return rtc;
rtcname ="/dev/misc/rtc";
}
return xopen(rtcname, flags);
} 2. 內(nèi)核如何更新RTC時(shí)鐘?
通過(guò)set_rtc函數(shù)指針指向的函數(shù),set_rtc在arch/arm/kernel/time.c內(nèi)
int(*set_rtc)(void);但是set_rtc函數(shù)指針在哪初始化的呢?set_rtc應(yīng)該是和RTC驅(qū)動(dòng)相關(guān)的函數(shù).搜索kernel源碼后發(fā)現(xiàn),好象內(nèi)核其他地方并沒(méi)有對(duì)其初始化。待解決!
set_rtc在do_set_rtc內(nèi)調(diào)用
static inline void do_set_rtc(void)
{
……
if (set_rtc())
next_rtc_update = xtime.tv_sec 60;
else
next_rtc_update = xtime.tv_sec 660;
} do_set_rtc在timer_tick里調(diào)用
void timer_tick(struct pt_regs *regs)
{
profile_tick(CPU_PROFILING, regs);
do_leds();
do_set_rtc();
do_timer(1);
……
}
timer_tick為Kernel提供的體系架構(gòu)無(wú)關(guān)的時(shí)鐘中斷處理函數(shù),通常會(huì)在體系架構(gòu)相關(guān)的時(shí)鐘中斷處理函數(shù)內(nèi)調(diào)用它。如s3c2410是這樣的:在arch/arm/mach-s3c2410/time.c中
* IRQ handler for the timer
*/
static irqreturn_t
s3c2410_timer_interrupt(int irq, void *dev_id, struct pt_regs*regs)
{
write_seqlock(&xtime_lock);
timer_tick(regs);
write_sequnlock(&xtime_lock);
return IRQ_HANDLED;
愛(ài)華網(wǎng)本文地址 » http://www.klfzs.com/a/25101014/234985.html
愛(ài)華網(wǎng)


