s3c6410 时钟设置


S3C6410的时钟是挺复杂的。比51单片机的时钟要是要复杂多了去了。下面是时钟的框图。

s3c6410 时钟设置

S3C6410共有3个PLL。PLL是时钟倍频用的。我使用的OK6410外部晶振是12M的。但是CPU的时钟是可以跑600多M的,这怎么实现的了。就是靠PLL来实现的。PLL对输入的频率可以进行倍频,倍频的倍数可以通过软件配置,所以才可以用外部的12M晶振给CPU提供600M的时钟。

第一个APLL。这个PLL是提供系统时钟的。就是供给ARM11核的时钟

第二个MPLL。这个PLL是提供AHB,APB时钟的。

第三个EPLL。这个PLL是提供某些外设的时钟。

 


在这里,我们只关心APLL和MPLL的配置。下图是默认的时钟配置

s3c6410 时钟设置

 


红色标明的线是说明该时钟走的路线。图中的选择器,默认为都是选择0那一端的。所以在开始的时候,

1、系统时钟:外部晶振直接通过MUXapll和第二个选择器,所以ARMCLK时钟就是外部晶振时钟12M。

2、AHB,APB总线:外部晶振直接通过MUXmpll和第二个选择器,第三个选择器,后面的CLKGEN不分频的,所以AHB和APB时钟也是12M。

 


下面我们就要改变这条时钟路线,并配置PLL。改变的路线如下所以

s3c6410 时钟设置

1、  系统时钟:从外部晶振到APLL,APLL将外部时钟倍频到时钟1。在通过选择器,在通过第二个选择器,分频不分频,所以ARMCLK时钟就是时钟1。

2、  AHB,APB总线时钟:从外部晶振到MPLL。将MPLL倍频到时钟2。在通过第一个选择器,第二个选择器,第三个选择器,CLKGEN进行分频,所以AHB/AXI总线时钟就是时钟3。PCLK时钟就是时钟4

 


知道了时钟的走向,但是时钟1,时钟2,时钟3,时钟4应该是多少了。

s3c6410 时钟设置

上图,是S3C6410手册中提供的几个频率值。那我们到底是使用哪一个了。这个时候,就去看uboot,看他配置的哪个时钟。

查阅,uboot代码,得知,配置的APLL时钟是533M,MPLL是533M。时钟1是533M,时钟2是533M,时钟3是133M,时钟4是66M。

即PCLK:AHBCLK:APBCLK = 533:133:66 = 1:4:8

 


知道了这些参数,就可以去写程序配置了。不过,在写程序之前,先了解下时钟产生序列。

s3c6410 时钟设置

上图,是复位之后时钟产生序列。

当电源电压大于一个值后,外部晶振的频率进入到芯片中。复位无效后,系统时钟就有了,这个时候系统时钟就是外部晶振时钟。正是有了这个时钟,所以可以执行初始化的代码。然后我们就要配置PLL时钟了。这个有个锁定时间的概念。当改变PLL的输出时钟后,并不是马上时钟就产生了,而是需要一定的时间,这个时间就是锁定时间,在这个锁定时间内,输出的时钟不稳定,所以这时钟就不会进入到系统时钟去,所以这个时钟,CPU是停止工作的。当锁定时间结束后,PLL输出时钟稳定,系统时钟等于输出时钟,CPU继续工作。

 


配置PLL流程

1、  配置PLL锁定时间(lock time)

2、  配置PLL的倍频系数

3、  开启PLL

4、  等待锁定时间后,PLL输出频率稳定



来看看时钟初始化中用到的寄存器。

1、PLL锁定时间设置寄存器

s3c6410 时钟设置

s3c6410 时钟设置

这个寄存器是配置3个PLL对应的锁定时间的。只有低16位有用。默认值是0XFFFF。这个值我们通常是不更改的,所以我们可以不用设置寄存器。

 


2、APLL和MPLL控制寄存器

  s3c6410 时钟设置

这个寄存器是用来配置PLL的倍频系数和PLL开启的。倍频系数是由寄存器中的三个参数决定。MDIV,PDIV,SDIV。设置不同的值,得到不同的倍频系数,也就得到不同的输出时钟。

计算的公式,手册中也有给,

s3c6410 时钟设置

不过,要去计算挺复杂的,手册中给了参考值。我们直接使用这些参考值就行了。

s3c6410 时钟设置

 对于12M的输入时钟,如果要配置输出频率是533M。那么MDIV参数是266,PDIV参数是3,SDI参数是1。

 我们是要配置PLL输出是533M的。

  所以这个寄存器配置的参数就是

                                        1<<31 | 266<<16|3<<8|1<<0

 


3、时钟选择控制寄存器

s3c6410 时钟设置

这个寄存器是用来选择时钟源的。在时钟框图中,有很多的选择器,通过选择器,可以选择不同的时钟,这儿寄存器就是配置时钟源选择哪一个。从框图中,看来我们是有两个时钟要选择的。一个是APLL后的选择器要选择APLL的输出,另一个是MPLL之后的选择器要选择MPLL的输出。从框图中知道这个和CLK_SRC寄存器的最后两位有关

s3c6410 时钟设置

所以,这里要配置最后两位的值都是1。选择PLL的输出。

 


4、时钟分频控制寄存器

s3c6410 时钟设置

这个是设置时钟线上某些分频器的分频。我们这里是设置ARMCLK,AHBCLK,APBCLK。

所以找到相应的位。

   s3c6410 时钟设置     

     设置这些参数就可以得到ARMCLK,AHBCLK,APBCLK。但是这些参数怎么设置了,里面还有HCLKX2,这个是什么了。这个从手册中找到的以下这张表就明白了。

     s3c6410 时钟设置

这个的参数就是配置红色框部分的分频器。

1、  对于1号分频器。FOUTAPL通过选择器,那么DOUTAPL时钟是533M。通过上路选择器,到达1号分频器,我们是要ARM系统时钟是在533M。所以这里分频器不分频。所以寄存器的3:0位设置为0

2、  对于2号分频器。FOUTMPL通过选择器到达MOUTMPL,那么MOUTMPL时钟是533M。在通过下路的SYNCMUX选择器,在通过上路的SYNC667选择器到达2号分频器。2号分频器的输出是HCLK*2,这个时钟是给ddr使用的。这个时钟是要配置为266M。所以2号分频器二分频。所以寄存器的11:9位值为1

3、  对于3号分频器。HCLK*2时钟是266M。PCLK时钟是66M。要分频4。所以寄存器的15:12位值是3

4、  对于4号分频器。HCLK*2是266M,而HCLK时钟是133M。所以4号分频器要设置为2.所以寄存器的第8位值是1。

 


所以这个寄存器设置的值就是

                       3 << 12 | 1<<9 | 1 <<8 | 0<<0

 


上面的方法可有点慢了,手册中还有另外一张图:

s3c6410 时钟设置

图中说明了配置HCLK*2,PCLK,HCLK的分频器的值在寄存器的哪几位。

 


后面的寄存器就和初始化时钟没有多大关系了。但是在后面的时候发现有以下寄存器。

     s3c6410 时钟设置

         从名字就可以看出,这个是门控时钟寄存器。就是控制外设功能的时钟是否使能的。不过默认为这些门控时钟都是开启的。这个和STM32是不一样的。STM32是默认为都关闭的。之前以为这ARM11没有门控时钟,原来其实ARM11是有门控时钟的。只不过默认都是开启的而已。

 


总结时钟初始化流程

1、  设置时钟分频控制寄存器,先配置各个分频器的值

2、  将CPU设置为异步模式,当AHB时钟和PCLK时钟不一样的时候,就要设置为异步模式

3、  配置APLL

4、  配置MPLL

5、  配置时钟源选择控制寄存器,选择对应的时钟

 


中间有一个设置CPU为异步模式。这个是手册中规定的,这个在OTHER寄存器中设置。

s3c6410 时钟设置

所以,我们要将这个寄存器的第7位设置为0。同时也我们也要讲第6位设置为0.这个也是一个时钟源选择的。如下图,是选择下一路的时钟是APLL还是MPLL。

s3c6410 时钟设置

 


         下面就可以写代码了,有了上面的知识后,这代码可就容易了,就是修改寄存器的值就行了。

         s3c6410 时钟设置

先定义所要操作寄存器的地址,然后再定义两个参数,第一个参数是配置PLL倍频系数和开启PLL的。第二个参数是配置FCLK,HCLK*2,HCLK,PCLK时钟的。这两个参数在之前已经分析过了。

 


         下面就是初始化时钟的代码了

     s3c6410 时钟设置

1、  先配置CLK_DIV0寄存器,配置各个分频器的分频系数。

2、  将CPU设置为异步模式,就是将other寄存器的第7位写入0。首先读出这个寄存器,将值存在r1寄存器中。将r1寄存器的第七位和第八位给清零,在写入到other寄存器。

3、  设置APLL

4、  设置MPLL

5、  设置时钟源选择,选择APLL和MPLL的输出

 


这样,就完成了时钟的初始化。在reset中,把上次点亮led的程序放在调用时钟初始化代码后,会发现,led闪烁的频率变高了。因为这时候已经把时钟的频率改到533M了。也就是时钟跑快了。

 


下面和STM32的时钟初始化:

本质上,STM32的时钟初始化和S3C6410的时钟初始化时差不多。也是配置PLL,选择时钟源。只是STM32不用将CPU设置为异步模式。而且STM32没有锁定时间,但是有一个寄存器的某一位来指示PLL输出是否准备好。

先来看看时钟树框图:

s3c6410 时钟设置

好大一棵时钟树。STM32支持4种时钟输入,

1:高速外部时钟,一般都是使用这个,外接的晶振一般为8M

2:高速内部时钟,8M。这个一般不用,因为内部的时钟性能不太好,时钟频率精度较差。

3:低速内部时钟,40K,这个时钟供给RTC或独立看门狗。

4:低速外部时钟,32.768K,这个时钟供RTC使用。

 


在STM32中,只有一个PLL,这个PLL就是对外部/内部高速时钟进行倍频的,倍频最大为16倍。然后通过选择器进入到系统时钟,系统时钟最大72M,比ARM11慢多了。然后再经过各个预分频器,到各自的时钟。

 


在系统复位后,HSI时钟被选为系统时钟,外部时钟是被旁路的。AHB预分频值为1,APB1和APB2预分频值都是1。所以系统时钟,AHB时钟,APB1,APB2时钟都是8M。工作在这个时钟肯定是不行的了。所以就要对时钟进行配置,就要用到PLL,将时钟升上去,让系统工作在最大72M下。

 


时钟配置的流程:

1、  打开外部时钟,配置外部时钟没有被旁路

2、  等待外部时钟就绪

3、  设置AHB,APB1,APB2,ADC预分频值。

4、  配置PLLTPRE和PLLSRC选择器,让外部时钟进入到PLL的输入。

5、  配置PLL倍频系数,并开启

6、  等待PLL锁定

7、  切换SW选择器,使系统时钟选择PLL输出

8、  等待PLL输出作为系统时钟

 


看看配置相关的寄存器

1、时钟控制寄存器

s3c6410 时钟设置

用来开启外部高速时钟和内部高速时钟,并可以得到时钟是否就绪。还可以开启PLL,并判断PLL是否锁定。

  s3c6410 时钟设置

截取一部分图。

 


2、时钟配置寄存器

s3c6410 时钟设置

配置时钟源选择,PLL倍频系数,预分频器系数

s3c6410 时钟设置

           截取一部分图。

 


 初始化时钟,官方库已经实现了这部分代码。下面可以来分析分析。

s3c6410 时钟设置

代码就是包括在SystemInit这个函数中的。这个函数在启动代码中就被调用。

s3c6410 时钟设置

在这个函数的中间,有调用这个SetSysClock()函数。注释也比较清楚,配置系统时钟频率,HCLK,PLCK2,PCLK1预分频系数。同时配置FLASH间隔周期和使能预先取数据功能。

 


进入到这个函数中,

s3c6410 时钟设置

通过宏判断系统时钟要设置为多少,在调用不同的函数。这个宏定义是在函数的开头部分定义的。

s3c6410 时钟设置

这里,把其他系统时钟宏注释,72M时钟注释去掉,就意味着将系统时钟设置为72M。

 


所以,就调用SetSysClockTo72()函数。

在去看这个函数实现什么。

我把这个代码中的宏,全部用他替换的值给替换掉了,这样可以知道配置什么值,就可以从寄存器去分析。代码稍微有点长,截成几部分来进行分析

s3c6410 时钟设置

首先定义两个无符号32位变量,变量是用__IO修饰。这个__IO,其实就是volatile的别名。

将RCC下的CR寄存器的值第16位置1.就是打开外部振荡器。

一个do while循环,就是判断外部振荡器是否就绪,其实就是判断RCC下的CR寄存器的第17位。为1就就绪,0就是没有就绪。这里多了一个变量来计数,防止如果外部晶振一直没有就绪,程序就死在这里了。

s3c6410 时钟设置

判断HSE就绪后,就将HSEStatus赋值为0x1。这个在下面会用到。

 


s3c6410 时钟设置

判断HSEStatus为0x1的话,说明外部晶振就绪,就接着下一步操作。下面三行代码是对FLASH操作。目前不知道这代码放在这里是干嘛的。先不管。

将RCC下的CFGR寄存器的第10位设置为1.

s3c6410 时钟设置

这里,就是设置AHB,APB1,APB2预分频值。AHB不分频,APB1二分频,APB2不分频。

 


s3c6410 时钟设置

 将RCC下的CFGR寄存器的21位到16位设置为011101。

s3c6410 时钟设置

 这里,就将讲HSE时钟给配置到PLL的输入,并且中间没有进行分频。在设置PLL的倍频系数是9。这样,就将输入的8M时钟给倍频到72M时钟了。

然后再开启PLL。将RCC下CR寄存器的24位设置为1.就开启了PLL。

在判断RCC下CR寄存器的25位是不是为1,为1的话,PLL就锁定了。没有的话,还没有锁定,那就等待。

s3c6410 时钟设置

 然后选中PLL时钟作为系统时钟,通过配置CR下的CFGR最低两位为10。将PLL输出切换为系统时钟。

s3c6410 时钟设置

最后,要检测PLL作为系统时钟是否准备好。检测CR下的CFGR的第2和第3为是否为10.是的话,PLL输出作为系统时钟。



这样,就完成了时钟的初始化。



      通过对比,还是有很多相通的地方。先设置预分频系数,在配置PLL并开启,最后配置时钟源。只是ARM11在设置预分频系数后,就要将CPU设置为异步模式。