2440时钟设置浅谈(带程序)

来源:互联网 发布:xshell mac 代替 编辑:程序博客网 时间:2024/06/16 09:01

2440时钟设置浅谈(带程序)-gududesiling-ChinaUnix博客 http://blog.chinaunix.net/uid-25100840-id-271075.html

这篇我自己写的关于2440时钟控制的的理解,这下面的代码是我在一个“ram9之家论坛”(在这里宣传一下,呵呵)上貌似是一个叫做wi100sh的博主贴子那里下来的代码,相当经典(不过其中大部分都是友善官方的源码,只是翻译了一下),在这里面我就一步一步谈谈对这个裸机源码初始化时钟的理解,对于裸机的朋友,这是个相当不错的程序。

 

一.首先,我们看到#include "2440lib.h"#include "2440slib.h" 其实还需要2440init.s(不过这个文件在工程目录中引用了),这是裸机程序基本上必须的三个启动文件,,我也不是很清楚实在那里引用了。不过这不影响我们写裸机程序,这些太底层,还没到那个地步。

我们看这个Main()开始,从这里面,我们看到红字注释的地方,开始调用初始化时钟,函数在下面我列了出来

/**************************************************************

4*4 Key Scan

**************************************************************/

#define GLOBAL_CLK 1            //相当于定义了FCLK,HCLK,PCLK,UCLK

#include "def.h"

#include "option.h"

#include "2440addr.h"

#include "2440lib.h"

#include "2440slib.h"

#include "mmu.h"

#include "RGB.h"

extern void InitAllClock(void);

extern void Test_Touchpanel(void);

extern void TFT_LCD_Init(void);

 

int Main()

{//

       InitAllClock();              //初始化时钟,由这个地方来调用初始化时钟函数这篇就从这开始

       MMU_Init();         //初始化MMU

       TFT_LCD_Init();

       Test_Touchpanel();

       //return 0;

       while(1)

       {

              if(Uart_GetKey() == ESC_KEY)

              {    

                     Lcd_ClearScr(White);

              }

       }

}

 

 

.以下是初始化时钟的一个文件,在这里,基本上把时钟设置好了,不过用户可以在这里面修改,很多都是默认值,

#include "def.h"

#include "2440addr.h"

/******************************************************************************Function name: ChangeClockDivider,设置分频,并写进并确定了rCLKDIVN的值,从而也确定了HCLK,PCLK的值

** Descriptions: 设置时钟寄存器CLKDIVN的分频比寄存器值,包括HDIVNPDIVN

** Input: hdivn_val,pdivn_val分别对应寄存器HDIVN[2:1]PDIVN[0]的值

//因为24402410不一样,重要的一点是hdivnHCLK的分频位),24402位,2410是一位,也就确定了,2410只能2分频,而2440正常就能够4分频,如果加上rCAMDIVN的话,就能6分频,8分频。多说一点,因为2440//的是2位,如果为00,01的话,不用考虑rCAMDIVN,如果是10,11,时就要考虑了,看最后的图

***************************************************************/

static U32 cpu_freq;//系统总线的时钟频率

static U32 UPLL;

static void ChangeClockDivider(int hdivn_val,int pdivn_val)  //static限定该函数为内部函数!

{

       int hdivn,pdivn;     //hdivn代替HDIVN=CLKDIVN[2:1], pdiv代替PDIVN=CLKDIVN[0]

       hdivn=2;               //初始化数值(默认值,呵呵),

       pdivn=1;

       switch(hdivn_val)

       {

              case 11:hdivn=0;break;         //11表示FCLK:HCLK=1:1

              case 12:hdivn=1;break;         //12表示HCLK=FCLK/2 

              case 13:hdivn=3;break;         //13表示HCLK=FCLK/3

              case 16:                               //16表示HCLK=FCLK/6

                     hdivn=3;

                     rCAMDIVN=(rCAMDIVN&~(3<<8))|(1<<8);    //HDIVN=3CAMDIVN[8]=1,HCLK=FCLK/6

                     break;

              case 14:hdivn=2;break;         //14表示HCLK=FCLK/4 (这个就是默认值了,不过一般也是传的这个值,这个时钟文件中就是设置的这个值)

              case 18:                               //18表示HCLK=FCLK/8

                     hdivn=2;

                     rCAMDIVN=(rCAMDIVN&~(3<<8))|(1<<9);    //HDIVN=2CAMDIVN[9]=1,HCLK=FCLK/8

                     break; //16,18是分别是4分频,8分频,用到摄像头的时候用的,最后的图片

              default:

                            hdivn=2;break;       //设置默认值HCLK=FCLK/4

       }

       switch(pdivn_val)                 //变量pdivn_val的值就是寄存器PDIVN的值

       {

              case 11:pdivn=0;break;  //11表示PCLK=HCLK

              case 12:pdivn=1;break;  //12表示PCLK=HCLK/2  //这个也是默认的值

       }    

       rCLKDIVN=(hdivn<<1)|pdivn;    //变量hdivnpdivn的值分别是CLKDIVN寄存器中HDIVNPDIVN的值

}

 

 

/******************************************************************************

** Function name: ChangeMPllValue

** Descriptions: 设置寄存器MPLLCON的值,确定下来FCLK的时钟值

** Input: mdiv,pdiv,sdiv 分别对应寄存器MPLLCON中的MDIV[19:12],PDIV[9:4],SDIV[1:0]

***************************************************************/

void ChangeMPllValue(int mdiv,int pdiv,int sdiv)

{

    rMPLLCON=(mdiv<<12)|(pdiv<<4)|sdiv;    //给寄存器MDIV,PDIV,SDIV赋值

}

 

 

/******************************************************************************

** Function name:cal_cup_bus_clk

** Descriptions: 读取寄存器的值并用来计算CPU总线的时钟频率,FCLK,HCLK,PCLK(就是得到3个数值,因为这三个数值是全局变量)   计算公式FCLK=MPLL=(2*(MDIV+8)*FIN)/((PDIV+2)*(1<<sdiv))   //FIN是系统输入的晶振频率12MHZ

** Input: 

***************************************************************/

static void cal_cpu_bus_clk(void)         //static关键词申明该函数为内部函数!

{

       U32 val;

       U8 m,p,s;

       //从寄存器MPLLCON中提取MDIV,PDIV,SDIV的值,从而计算FCLK的值,就是MPLLout

       val=rMPLLCON;          //注意,之前在调用函数中已经设置好了rMPLLCON的值

       m=(val>>12)&0xff;      //m=MDIV=MPLLCON[19:12] 8位取值

       p=(val>>4)&0x3f;  //p=PDIV=MPLLCON[9:4] 6位取值

       s=val&3;               //s=SDIV=MPLLCON[1:0] 2位取值

       //(m+8)*FIN*2 不要超出32位数最大为0x100000000

       FCLK=((m+8)*(FIN/100)*2)/((p+2)*(1<<s))*100;    //(1<<s)< span="" style="word-wrap: break-word;">表示第s位置1,也就是2^s

                     //MPLL=(2*(MDIV+8)*FIN)/((PDIV+2)*(2^s)) FIN=12000000,从寄存器CLKDIVN中提取出HDIVNPDIVN,准备进行FCLK:HCLK:PCLK分频比的设置

       val=rCLKDIVN;    //在调用函数里面已经设置了rCLKDIVN的值

       m=(val>>1)&3;     //m=CLKDIVN[2:1]=HDIVN

       p=val&1;        //p=CLKDIVN[1]=PDIVN

                                   //CLKDIVN[3]=DIVN_UPLL

       //从寄存器CAMDIVN中提取出CAMDIVN[9:8]

       val=rCAMDIVN;   //在调用函数里面已经设置了rCAMDIVN的值

       s=val>>8;              //s[1:0]=CAMDIVN[9:8]=HCLK4_HALF,HCLK3_HALF

                                   //s[4]=CAMDIVN[12]=DVS_EN,如果DVS_EN=1,CPU时钟为HCLK,否则为FCLK

       switch (m)            //这里的m=HDIVN,FCLKHCLK进行分频设置

       {

              case 0:

                     HCLK=FCLK;

                     break;

              case 1:

                     HCLK=FCLK>>1;               //HCLK=FCLK/2

                     break;

              case 2:

                     if(s&2)                  //如果s[1]=1,也即CAMDIVN[9]=HCLK4_HALF=1

                            HCLK=FCLK>>3;        //HCLK=FCLK/8

                     else

                            HCLK=FCLK>>2;        //HCLK=FCLK/4

                     break;

              case 3:

                     if(s&1)                         //如果s[0]=1,也即CAMDIVN[8]=HCLK3_HALF=1

                            HCLK=FCLK/6;           //HCLK=FCLK/6

                     else

                            HCLK=FCLK/3;           //HCLK=FCLK/3

                     break;

       }    

       if(p)                                    //这里p=PDIVN,对HCLKPCLK进行分频设置

              PCLK=HCLK>>1;               //PDIVN=1,PCLK=HCLK/2;否则PCLK=HCLK

       else

              PCLK=HCLK;      

       if(s&0x10)                                 //如果s[4]=1,也即CAMDIVN[12]=DVS_EN=1

              cpu_freq=HCLK;                 //s3c2440手册中CAMDIVN[12],也表示CPU总线是快速总线模式

       else

              cpu_freq=FCLK;                  //CPU总线频率,也表示CPU总线是异步模式

      

       //设置UPLLCON寄存器,UPLLCON是用来设置usb的时钟的,还没学到,    将来学到了,再解释(和MPLLCON原理一样,不过不用*2  

       val=rUPLLCON;           //调用函数里面已经设置了rUPLLCON的值(在目前的程序还没有设置,应该是0

       m=(val>>12)&0xff;      //m=MDIV=UPLLCON[19:12] 8

       p=(val>>4)&0x3f;  //p=PDIV=UPLLCON[9:4] 6

       s=val&3;               //s=SDIV=UPLLCON[1:0] 2

       UPLL=((m+8)*FIN)/((p+2)*(1<<s));   //公式UPLL=((MDIV+8)*FIN)/((PDIV+2)*(2^s)) FIN为输入时钟频率

       UCLK=(rCLKDIVN&8)?(UPLL>>1):UPLL;      //如果CLKDIVN[3]=1,UPLL需要分频

}

 

 

 

/******************************************************************************

** Function name: InitAllClock

** Descriptions: 初始化时钟,包括FCLK,HCLK,PCLK,UPLL,cpu_freq(系统时钟)

** 变量说明: i=0FCLK=200MHZ,HCLK=FCLK/2=100MHZ,PCLKpdivn_val来确定,pdivn_val=1,PCLK=HCLK/2

                   i=1 FCLK=300MHZ,HCLK=FCLK/3=100MHZ,PCLKpdivn_val来确定,pdivn_val=1,PCLK=HCLK/2

                   i=2 FCLK=400MHZ,HCLK=FCLK/4=100MHZ,PCLKpdivn_val来确定,pdivn_val=1,PCLK=HCLK/2

                   i=3 FCLK=440MHZ,HCLK=FCLK/4=110MHZ,PCLKpdivn_val来确定,pdivn_val=1,PCLK=HCLK/2

*************************************************************************/

void InitAllClock()

{

       U8 i;

       U32 mpll_val;

       int mdiv,pdiv,sdiv; //mdiv,pdiv,sdiv分别对应寄存器MPLLCON中的MDIV,PDIV,SDIV

       int hdivn_val,pdivn_val;       //hdivn_val,pdivn_val分别对应寄存器CLKDIVN中的HDIVNPDIVN

       i=2;        //设定i的初始值 don't use 100M!==>因为200M<=FCLK<=600M !!!

    //因为这个地方i=2,导致下面的设置,系统cpu是时钟为400Mhz,如果我们想要其他的频率的话,就要改变     / /这个i值了,具体多大这在函数的开头说明了

       switch(i)

       {

              //FCLK=MPLLout=(2*(MDIV+8)*FIN/100)/((PDIV+2)*(1<

        // hdivn_val这个参数是用来设置HCLK的(12,代表FCLK:HCLK=12,13:代表1:3终于明白了,这也真是为难了友善的技术师门,呵呵)

 

              case 0:     //FCLK=200M

                     hdivn_val=12;        //hdivn_val=HDIVN12表示HCLK=FCLK/2

                     mpll_val=(92<<12)|(4<<4)|(1);    //FCLK=200M,

                     break;

              case 1:     //FCLK=300M

                     hdivn_val=13;

                     mpll_val=(67<<12)|(1<<4)|(1);

                     break;

              case 2:     //FCLK=400M

                     hdivn_val=14;

                     mpll_val=(92<<12)|(1<<4)|(1);

                     break;

              case 3:     //FCLK=440M

                     hdivn_val=14;

                     mpll_val=(102<<12)|(1<<4)|(1);

                     break;

              default:   //FCLK=400M

                     hdivn_val=14;

                     mpll_val=(92<<12)|(1<<4)|(1);

                     break;

       }

       mdiv=(mpll_val>>12)&0xff;  //实际这个地方感觉多此一举,不过也是为了让用户修改方便吧(指的是上面)

       pdiv=(mpll_val>>4)&0x3f;

       sdiv=(mpll_val)&3;

       //init FCLK=400M, so change MPLL first

 

       ChangeMPllValue(mdiv,pdiv,sdiv);//初始化MPLLCON的值,这里默认是MDIV=92PDIV=1SDIV=1,上面的这个函数调用完后,就已经确认了FCLK的值了

pdivn_val=12; //表示PCLK=HCLK/2 设定的初值(即设置PCLK的分频

//数,这个是相对于HCLK而言的,即HCKL:PCLK=1:2,用于下面最用的设定值

 

//根据上面设置好的2个比例参数,来最终确定HCLKPCLK的值(通过给寄

器赋值)//这一点要注意一下,如果想要使用摄像头的话,在这里,要最后设定

一下hdivn_val的值,要不,系统不会对//摄像头时钟赋值的,即不会对

CAMDIVN进行赋值,关于CAMDIVNHCLK的影响,最后有一张图,不过大//

家也可以参考2440的技术手册。

 

       ChangeClockDivider(hdivn_val, pdivn _val);   //初始化寄存器CLKDIVNCAMDIVN

                                      

       //默认值hdivn_val(上面switch中得到的) =14,表示hdivn=2,HCLK=FCLK/4=100MHZ;第二个参数12表示pdivn=1,PCLK=HCLK/2=50MHZ

 

cal_cpu_bus_clk(); //调用这个函数的说明我改了一下,我认为这个函数,

是一个读取时钟函数

//因为时钟的设置在上面的2个重要函数中已经设置好了,而这个函数cal,主要

//是用来给这几个外部变量FCLK,HCLKPCLKUPLL,UCLK,cpu_freq,赋值用

//的,用来将来对这几个时钟值的读取,并作为外围芯片的时序参考

}

 

 

图片(摘自2440技术手册)

 

 

 

解释了这么多了,实际时钟控制就是,要根据自己需要的始终设置一下MPLLCON的值,而这个值的设定确实需要三个mps(用来实现乘除,最终倍频),确定下来FCLK,然后又hdivn hdivn来设定三个时钟的分频比,就最终确定的三大时钟(不过对于2440,还要考虑一下CAMDIVN的影响)

 

这些弄了这么长时间,不过也确实学习了很多东西,让我对时钟不在迷惑,在裸机的道路上又迈进了一步,在这里再次谢谢“ram9之家论坛“和上面的博主wi100sh,是这些人的无私奉献,让我学习到了很多,因此,如果我学会了,也会无私的奉献给大家

 

 

最后,如果谁要是想要这些文件的源码,请联系我,qq:117838621

Arm9之家论坛网址:http://www.arm9home.net/read.php?tid=5808&page=3#51406,希望更多的人来那里讨论,也希望大家都不要放弃,一定要坚持下去。谢谢


0 0
原创粉丝点击