您的位置:首页 > 其它

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

2013-05-12 13:39 351 查看
前言

首先,我们应该知道一点,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看出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: