1. 程式人生 > >一起學mini2440裸機開發(三)--S3C2440時鐘學習

一起學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看出。