一起學mini2440裸機開發(三)--S3C2440時鐘學習
轉載地址:http://www.oschina.net/question/565065_115209?sort=time
首先,我們應該知道一點,mini2440開發板在沒有開啟時鐘前,整個開發板全靠一個12MHz的外部晶振提供頻率來工作執行的,也就是說CPU、記憶體、UART、ADC等所有需要用到時鐘頻率的硬體都工作在12MHz下,而S3C2440A可以正常工作在400MHz下,可想而知兩者速度相差會有多大了。如果CPU工作在12MHz頻率下,開發板的使用效率非常低,所有依賴系統時鐘工作的硬體,其工作效率也很低,比如,我們電腦裡面經常提到的超頻,超頻就是讓CPU工作在更高的頻率下,讓電腦運算速度更快,雖然頻率是越高越好,但是由於硬體特性決定了任何一個裝置都不可能無止境的超頻,電腦超頻時要考慮到CPU或主機板發熱過大,燒壞的危險,同樣開發板的主機板上的外設和CPU也有一個頻率限度,ARM920T核心的S3C2440的最高正常工作頻率如下:
● FCLK:400MHz
● HCLK:100MHz
● PCLK:50MHz
那麼咱們怎樣讓CPU工作在400MHz下,執行的速度大為提高呢?(本段主要是別的老師的話,嘿嘿,借用沒事,只要吸收成自己的知識就行了)
S3C2440有關的時鐘種類
總體來說,與S3C2440處理器有關的時鐘主要有4種:Fin、FCLK、HCLK、PCLK。
● Fin:外部輸入的晶振頻率。
● FCLK:用於CPU核。 由Fin得來
● HCLK:用在與AHB匯流排互連的裝置(如儲存控制器、LCD控制器、NAND、中斷控制器、DMA等)上。 由FCLK得來
● PCLK:用在與APB匯流排互連的低速裝置(如定時器、UART、ADC等)上。 由FCLK得來
為什麼需要不同種類的時鐘呢?
由於不同的硬體外設工作時需要的額定頻率不同,所以需要產生不同種類的時鐘頻率。也就是說,對於一些需要時鐘工作的硬體,如果切斷其時鐘源,就不會再工作了,從而達到低功耗的目的,這也是便攜嵌入式裝置的一個特點。
時鐘源:開發板外部時鐘頻率太高容易受到外界環境的干擾,同時為了降低成本,通常開發板的外部晶振時鐘頻率都很低,mini2440開發板就用用1個12MHz的晶振來提供時鐘源。但是S3C2440處理器內部工作頻率較高,這就需要用鎖相環(PLL)來實現倍頻功能。
鎖相環PLL
鎖相環是實現倍頻功能的,說白了就是將12MHz成倍的增加,達到實際所需頻率。雖然鎖相環有很多指標,咱們完全可以將其理解為一個時鐘變換電路,低頻晶振輸入即可得到處理器所使用的較高頻率的時鐘。
S3C2440裡有兩個PLL:MPLL和UPLL。MPLL用來產生FCLK、HCLK、PCLK的高頻工作時鐘,UPLL用來為USB提供工作頻率。下圖為Fin通過MPLL產生FCLK、HCLK、FCLK的框圖。
上圖還有兩個控制暫存器(MPLLCON和CLKDIVN),分別用於控制分頻比。
● MPLLCON控制FCLK和Fin的比例關係
● CLKDIVN控制FCLK、HCLK和PCLK之間的比例關係
Fin通過UPLL產生USB裝置正常工作所需要的時鐘頻率,工作原理與上面的MPLL類似。
系統時鐘初始化
這一節很重要啊!!!一定要好好理解,明白系統時鐘初始化的流程。
系統上電後,S3C2440處理器會自動鎖存OM3和OM2引腳的電平值,這兩個引腳用於選擇外部時鐘輸入方式,如下表所示。你可以從我們的mini2440開發板的電路圖看到,開發板上的OM3和OM2均接地,即OM[3:2]=00。所以,時鐘源為外部晶振。
模式 OM[3:2] |
MPLL狀態 |
UPLL狀態 |
主時鐘源 |
USB 時鐘源 |
00 |
開啟 |
開啟 |
晶振 |
晶振 |
01 |
開啟 |
開啟 |
晶振 |
外部時鐘 |
10 |
開啟 |
開啟 |
外部時鐘 |
晶振 |
11 |
開啟 |
開啟 |
外部時鐘 |
外部時鐘 |
注意:雖然MPLL在復位後就開啟,MPLL輸出(Mpll)並沒有作為系統時鐘,直到軟體寫入有效值來設定MPLLCON暫存器。在設定此值之前,是將外部晶振或外部時鐘源提供的時鐘直接作為系統時鐘。即使使用者不想改變MPLLCON暫存器的預設值,使用者也應當寫入與之相同的值到MPLLCON暫存器中。
咱們再分析上圖1,系統時鐘初始化流程如下:
①系統剛上電幾毫秒後,FCLK等於外部晶振(OSC)的時鐘頻率,即FCLK=Fin;
②當復位訊號nRESET恢復高電平後,鎖相環按照暫存器MPLLCON和CLKDIVN設定的倍頻比例開始生成所需要的時鐘頻率。從圖1可以看到,從鎖相環開始工作到輸出新的穩定的頻率值需要一定的時間(Lock Time,也叫鎖相環的捕獲時間),經過這段時間後,鎖相環輸出新的頻率值,這時FCLK等於鎖相環的輸出。暫存器LOCKTIME中的值對應著圖1中的Lock Time,初始化時一般將其設為0xffffff,這是S3C2440資料手冊上給出的預設值,一般按照這個值初始化LOCKTIME暫存器即可滿足要求。
③經過一段時間後,鎖相環PLL輸出新的時鐘頻率。
FCLK、HCLK、PCLK與Fin的關係
相信你看完上面的關於初始化的,應該大概懂了初始化流程,但可能還是不知道怎麼產生對應的MCLK、PCLK和FCLK。
那麼,如何控制鎖相環PLL的輸出頻率呢?S3C2440處理器內部有兩個暫存器:MPLLCON暫存器控制FCLK與Fin的比例關係,CLKDIVN暫存器控制FCLK和HCLK、PCLK的比例關係,咱也可以籠統的用下圖表示這四者的關係
(1)Fin得到FCLK
對於S3C2440,Fin與FCLK之間的關係為:FCLK=(2*m*Fin) / (p*2^s)。 多說一句,對於S3C2410,Fin與FCLK之間的關係為:FCLK=(m*Fin) / (p*2^s)。 這也是在U-boot移植時需要注意的一點。
在上式中,m=MDIV+8,p=PDIV+2,s=SDIV。其中,MDIV、PDIV和SDIV是MPLLCON暫存器中的資料,如圖2所示
注意:在上圖2中,可以看到最下面的NOTE用來提醒讀者注意,在系統初始化階段,如果UPLL和MPLL都需要設定,應該先初始化UPLL(USB時鐘),然後等待大約7個nop指令(控指令)後,再初始化MPLL。
現在咱們應該有個大概的意識了,設定MPLLCON中相應的位,就可以通過Fin獲得FCLK了。但是這傢伙還得一步步的算MDIV、PDIV、SDIV的值,太麻煩了,對於我這懶人,這樣不好不好。那麼怎麼得到這三者的值呢?
雖然PLL給使用者提供了靈活變換系統時鐘的功能,但是,並不是任意的時鐘下處理器都能正常工作,基於此種原因,為了照顧我等懶人,官方給出了系統時鐘配置參考,如圖3所示。
下面以第5行為例講解,假設外部晶振輸入為12MHz,MDIV=127,PDIV=1,SDIV=1,則m=127+8=135,,p=2+2=4,s=1。則FCLK=(2*2*135)/(4*2^1)=405MHz。
小技巧:由上面的分析可以看到,MDIV、PDIV和SDIV都是佔用了MPLLCON的連續某幾位,如PDIV是MPLLCON暫存器的第4~9位。因此,初始化時可以使用移位指令來實現對MPLLCON的初始化。
其實咱們經常用的就是FCLK=400MHz和FCLK=200MHz,你只管記住這兩個對應的值就行了。
例如,已知系統外部晶振輸入為12MHz,要求FCLK輸出200MHz,經過計算可以得到MDIV=92,PDIV=4,SDIV=1。
對於ARM彙編,可以用下面的指令進行初始化。
M_MDIV EQU 92 ;Fin=12MHz,Fout=200MHz
M_PDIV EQU 4
M_SDIV EQU 1 ;S3C2440
ldr r0,=MPLLCON ;將MPLLCON暫存器的地址值暫存於r0
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ;將設定MPLLCON的值暫存於r1
str r1,[r0] ;將r1暫存器的值賦給MPLLCON暫存器
對於C語言,可以用下面的指令進行初始化
MPLLCON=((92<<12)|(4<<4)|(1<<0)); //貌似C語言很簡單呢
如果要求FCLK輸出400MHz,那麼相對應的值MDIV=92,PDIV=1,SDIV=1。這兩組值就是經常用到的。
(2)FCLK得到HCLK、PCLK
上一步通過Fin得到了FCLK,下面就可以通過FCLK獲得相應的PCLK、HCLK了。主要就是設定CLKDIVN暫存器,來實現FCLK、HCLK、PCLK之間的分頻比。
在CLKDIVN暫存器中,HDIVN用於控制FCLK和PCLK的比例關係,PDIVN主要用於控制HCLK和PCLK的比例關係,如圖4所示:
● 當CLKDIV_VAL=0 時,FCLK:HCLK:PCLK=1:1:1。
● 當CLKDIV_VAL=1 時,FCLK:HCLK:PCLK=1:1:2。
● 當CLKDIV_VAL=2 時,FCLK:HCLK:PCLK=1:2:2。
● 當CLKDIV_VAL=3 時,FCLK:HCLK:PCLK=1:2:4。
● 當CLKDIV_VAL=4 時,FCLK:HCLK:PCLK=1:4:4。
● 當CLKDIV_VAL=5 時,FCLK:HCLK:PCLK=1:4:8。
● 當CLKDIV_VAL=6 時,FCLK:HCLK:PCLK=1:3:3。
● 當CLKDIV_VAL=7 時,FCLK:HCLK:PCLK=1:3:6。
有的人可能會問,CLKDIV_VAL是什麼?其實就是一個巨集定義,方便直觀,因為CLKDIVN的DIVN_UPLL、HDIVN、PDIVN是連續的低4個位,所以根據你要設定的分頻
比,一起賦值個CLKDIVN就可以了,不用再移位什麼的了。
還是拿上面舉例,已知系統外部晶振輸入為12MHz,要求FCLK=200MHz,HCLK=100MHz,PCLK=50MHz,即FCLK:HCLK:PCLK=1:2:4,則CLKDIV_VAL=3。
對於ARM彙編,可以用下面的指令進行初始化。
M_MDIV EQU 92 ;Fin=12MHz,Fout=200MHz
M_PDIV EQU 4
M_SDIV EQU 1 ;S3C2440
CLKDIV_VAL EQU 3
ldr r0,=CLKDIVN ;將CLKDIVN暫存器的地址值暫存於r0
ldr r1,=CLKDIV_VAL ;將CLKDIV_VAL暫存於r1
ldr r1,[r0] ;將CLKDIV_VAL賦給CLKDIVN
ldr r0,=MPLLCON ;將MPLLCON暫存器的地址值暫存於r0
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ;將設定MPLLCON的值暫存於r1
str r1,[r0] ;將r1暫存器的值賦給MPLLCON暫存器
時鐘初始化實驗
下面是完整的時鐘初始化程式碼
ARM彙編版本:
;Fin=12MHz,FCLK=400MHz,HCLK=100MHz,PCLK=50MHz
;時鐘相關暫存器地址巨集定義
LOCKTIME EQU 0x4c000000
MPLLCON EQU 0x4c000004
CLKDIVN EQU 0x4c000014
;設定值巨集定義
M_MDIV EQU 92 ;Fin=12MHz,Fout=400MHz
M_PDIV EQU 1
M_SDIV EQU 1 ;S3C2440
CLKDIV_VAL EQU 5 ;FCLK:HCLK:PCLK=1:4:8
Clock_Init ;時鐘初始化程式碼,Clock_Init為標號
ldr r0,=LOCKTIME ;設定變頻鎖定時間
ldr r1,=0x00ffffff
str r1,[r0]
ldr r0,=CLKDIVN ;設定分頻比FCLK:HCLK:PCLK=1:4:8
ldr r1,=CLKDIV_VAL
str r1,[r0]
;注意如果HDIVN設定為非0,CPU的匯流排模式要進行改變,預設情況下FCLK=HCLK,CPU工作在快速匯流排模式(fast bus mode)下,
;HDIVN設定為非0後,FCLK與HCLK不再相等,要將CPU改為非同步匯流排模式(asynchronous bus mod)下,如下面程式碼所示 mrc p15,0,r1,c1,c0,0 ;修改CPU匯流排模式 為非同步匯流排模式
orr r1,r1,#0xc0000000
mcr p15,0,r1,c1,c0,0
ldr r0,=MPLLCON ;FCLK=400MHz
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)
str r1,[r0]
mov pc,lr ;函式呼叫子程式返回
C語言版本:
#define MPLL_400MHz ((92<<12)|(1<<4)|(1<<0))
#define CLKDIV_VAL 5
void Clock_Init(void)
{
//設定變頻鎖定時間
LOCKTIME=0x00ffffff;
//設定分頻比FCLK:HCLK:PCLK=1:4:8
CLKDIVN=CLKDIV_VAL;
//修改CPU匯流排模式,由於修改CPU匯流排模式時要使用mrc指令,因此只能使用C語言嵌入彙編方式來實現。
__asm__(
"mrc p15,0,r1,c1,c0,0\n"
"orr r1,r1,#0xc0000000\n"
"mcr p15,0,r1,c1,c0,0\n"
);
MPLLCON=MPLL_400MHz;
}
UPLL設定
前邊咱們都將UPLL的設定略過了,下面講講它吧。已經說過了,S3C2440有兩個鎖相環PLL,其中UPLL就是提供USB裝置正常工作所需要的時鐘頻率的,工作原理其實與MPLL的一樣,它是直接通過設定UPLL得到的,如下圖:
它的計算公式為:USB裝置工作頻率=(m*Fin) / (p*2^s)。其中m、s、p與MPLL的一模一樣!以下是S3C2440的資料手冊截圖
下面就舉個例子講講怎麼設定UPLL吧!
已知Fin=12MHz,設定FCLK=400MHz、HCLK=100MHz、PCLK=50MHz、UCLK=48MHz
ARM彙編:
;時鐘暫存器
LOCKTIME EQU 0x4c000000
CLKDIVN EQU 0x4c000014
MPLLCON EQU 0x4c000004
UPLLCON EQU 0x4c000008
;FCLK=400MHz、HCLK=100MHz、PCLK=50MHz
M_MDIV EQU 92
M_PDIV EQU 1
M_SDIV EQU 1
CLKDIV_VAL EQU 5 ;分頻比
;UCLK=48MHz
U_MDIV EQU 56
U_PDIV EQU 2
U_SDIV EQU 2
;設定鎖定時間
ldr r0,=LOCKTIME
ldr r1,=0x00ffffff
str r1,[r0]
;設定分頻比
ldr r0,=CLKDIVN
ldr r1,=CLKDIV_VAL
str r1,[r0]
;修改匯流排模式
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000
mcr p15,0,r0,c1,c0,0
;配置UPLL
ldr r0,=UPLLCON
;Fin=12.0MHz,UCLK=48MHz
ldr r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)
str r1,[r0]
nop ;先設定UPLL,間隔7個空指令後,再設定MPLL
nop
nop
nop
nop
nop
nop
;配置MPLL
ldr r0,=MPLLCON
;Fin=12.0MHz,FCLK=400MHz
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)
str r1,[r0]
總結
至此,關於S3C2440的時鐘已經整理完了,它是學習下一節定時器知識的前提,希望對你有所幫助!
補充說明:我糾正自己說的一些錯誤,我說的以上關於初始化時鐘時的嵌入彙編__asm__有錯誤,它可以在ADS下實現,但是在MDK中格式不對,我還沒弄明白怎麼用,等著弄明白了再回來補充,只是格式不對哈,原理其實都是對的。
另外,我還想說,其實咱們要初始化時鐘,很簡單的做法就是修改一下S3C2440.s中的一行程式碼就行了,也就是將CLOCK_SETUP EQU 0 修改為 CLOCK_SETUP EQU 1,如圖:
這樣,系統預設的初始化後的時鐘為FCLK=300MHz、HCLK=100MHz、PCLK=50MHz。你可以從上圖的MPLLCON_Val看出。