從S5PV210學習最基礎的定時器(RTC篇)
注:下文都以S5PV210為背景。
本文只學習RTC的讀取和設定還有鬧鐘功能。
RTC,一個較為特殊的定時器,其他定時器都是定的時間段,而RTC定的是時間點。
一.RTC介紹
(1)real time clock,真實時間,就是所謂的xx年x月x日x時x分x秒星期x
(2)RTC是SoC中一個內部外設,RTC有自己獨立的晶振提供RTC時鐘源(32.768KHz),內部有一些暫存器用來記錄時間(年月日時分秒星期)。一般情況下為了在系統關機時時間仍然在走,還會給RTC提供一個電池供電。
(3)RTC的時間暫存器們(SEC,MIN,HOUR,YEAR...)使用的是BCD碼儲存時間資料。
注:什麼是BCD碼?
(1)RTC中所有的時間(年月日時分秒星期,包括鬧鐘)都是用BCD碼編碼的。
(2)BCD碼本質上是對數字的一種編碼。用來解決這種問題:由56得到0x56(或者反過來)。也就是說我們希望十進位制的56可以被編碼成56(這裡的56不是十進位制56,而是兩個數字5和6).
(3)BCD碼的作用在於可以將十進位制數拆成組成這個十進位制數的各個數字的編碼,變成編碼後就沒有位數的限制了。譬如我有一個很大的數123456789123456789,如果這個數純粹當數字肯定超出了int的範圍,計算機無法直接處理。要想讓計算機處理這個數,計算機首先得能表達這個數,表達的方式就是先把這個數轉成對應的BCD碼(123456789123456789)
(4)BCD碼在計算機中可以用十六進位制的形式來表示。也就是說十進位制的56轉成BCD碼後是56,在計算機中用0x56來表達(暫時儲存與運算)。
(5)所以我們需要寫兩個函式進行十進位制和BCD的互轉,示例程式碼如下:
static unsigned int bcd_2_dec(unsigned int bcd) { volatile unsigned int dec_h, dec_l; dec_h = ((bcd &(0xf << 4))>>4); dec_l = bcd&0xf; return dec_h*10 + dec_l; } static unsigned int dec_2_bcd(unsigned int dec) { return ((dec/10)<<4)|(dec%10); }
二.S5PV210的RTC
(1)看圖說話:
分析:
(1)使用獨立的晶振。
(2)對外可以輸出兩種TICK訊號:TICK中斷和TICK喚醒。
(3)對外可以輸出兩種ALARM訊號:ALARM中斷和ALARM喚醒。
Time Tick Generator:產生tick,說明這個RTC可以用來生成tick給作業系統用。
Clock Divider:分頻器,但RTC的分頻器是設定好了的,所以軟體操作不需要設定。
Control Register:控制各大部門的,主要是用來控制enable和disable。
Reset Register:復位。
Leap Year Generator:閏年生成器。
Alarm Generator:鬧鐘生成器。
SEC,MIN,HOUR...:記錄各個時間級的暫存器。
(2)重要暫存器和操作注意
重要暫存器簡介:
(1)INTP 中斷掛起暫存器
(2)RTCCONRTC控制暫存器
(3)RTCALM ALMxxx鬧鐘功能有關的暫存器
(4)BCDxxx 時間暫存器
操作注意:
(1)基礎使用的RTC是不需要初始化的。
(2)為了避免RTC的時間暫存器被誤操作,所以預設情況下RTC讀寫是禁止的,因此當我們要更改RTC時間時,應該先開啟RTC的讀寫開關,然後再進行讀寫操作,操作完了後立即關閉讀寫開關。
(3)RTC各個時間級的ALARM是獨立的,如:只使用ALMSEC,並設定ALMSEC為1,那麼每次SEC為1是都會發出中斷。
三.實戰程式設計
void rtc_set_time(const struct rtc_time *p)
{
// 第一步,開啟RTC讀寫開關
rRTCCON |= (1<<0);
// 第二步,寫RTC時間暫存器
rBCDYEAR = num_2_bcd(p->year - 2000);
rBCDMON = num_2_bcd(p->month);
rBCDDATE = num_2_bcd(p->date);
rBCDHOUR = num_2_bcd(p->hour);
rBCDMIN = num_2_bcd(p->minute);
rBCDSEC = num_2_bcd(p->second);
rBCDDAY = num_2_bcd(p->day);
// 最後一步,關上RTC的讀寫開關
rRTCCON &= ~(1<<0);
}
void rtc_get_time(struct rtc_time *p)
{
// 第一步,開啟RTC讀寫開關
rRTCCON |= (1<<0);
// 第二步,讀RTC時間暫存器
p->year = bcd_2_num(rBCDYEAR) + 2000;
p->month = bcd_2_num(rBCDMON);
p->date = bcd_2_num(rBCDDATE);
p->hour = bcd_2_num(rBCDHOUR);
p->minute = bcd_2_num(rBCDMIN);
p->second = bcd_2_num(rBCDSEC);
p->day = bcd_2_num(rBCDDAY);
// 最後一步,關上RTC的讀寫開關
rRTCCON &= ~(1<<0);
}
void rtc_set_alarm(void)
{
rALMSEC = num_2_bcd(23);
rRTCALM |= 1<<0;
rRTCALM |= 1<<6;
}
void isr_rtc_alarm(void)
{
static int i = 0;
printf("rtc alarm, i = %d...", i++);
rINTP |= (1<<1);
intc_clearvectaddr();
}
需要注意的:
(1)讀取時間,都需要使能RTCEN。
(2)RTC是使用BCD碼,而人是使用十進位制,所以要進行轉碼。
(3)RTC的鬧鐘是使用哪個就設定哪個,並開啟ALMEN(鬧鐘功能)和對應的鬧鐘(如SEC就是SECEN)。