关闭

一起学mini2440裸机开发(三)--S3C2440时钟学习

217人阅读 评论(0) 收藏 举报
分类:

前言

       首先,我们应该知道一点,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看出。

   

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:283064次
    • 积分:4181
    • 等级:
    • 排名:第7658名
    • 原创:75篇
    • 转载:424篇
    • 译文:0篇
    • 评论:18条
    最新评论