时钟概念:
★时钟脉冲:一个按一定电压幅度,一定时间间隔连续发出的脉冲信号;
★时钟频率:在单位时间(如:1秒)内产生的时钟秒冲数;
作用:
时钟信号是时序逻辑的基础
时钟的产生-晶振
晶振:晶体振荡器,是用石英晶体精密切割做成。
时钟产生-PLL
PLL:(锁相环)合成器=外部晶体+PLL电路。
S3C2440的主时钟晶振来自外部晶振(XTIPLL),或者是外部时钟(EXTCLK)。时钟生成器包含了一个振荡器(振荡放大器),其连接外部晶振,可以产生需要的高频,通过引脚OM[3:2]来决定时钟源时Crystal还是EXTCLK.
S3C2440有两个PLL:(1)MPLL和(2)UPLL,
UPLL专用于USB设备。
MPLL 用于CPU及其他外围器件。
通过MPLL会产生三个部分的时钟频率:
FCLK:用于CPU核;
HCLK:用于AHB(常用于高速外设)总线的设备,比如:SDRAM;
PCLK:用于APB(常用于低速外设)总线的设备,比如:UART.
时钟启动流程:
1.上电几毫秒后,外部晶振输出稳定,FCLK=外部晶振频率(12MHZ),nRESET
信号恢复高电平后,CPU开始执行命令。
2.在设置MPLL的几个寄存器后,需要等待一段时间(Lock Time),MPLL的输出才稳定。在这段时间(Lock Time)内,FCLK停振,CPU停止工作。
Lock Time的长短由寄存器LOCKTIME设定。
3.Lock Time之后,MPLL输出正常,CPU工作在新的FCLK(如:400MHZ)下。
设置S3C2440的时钟频率就是设置相关的几个寄存器:
CLKDIVN
寄存器用于设置FCLK、HCLK、PCLK三者的比例
★ HDIVN:位[2:1],用来设置HCLK与FCLK比例关系
★ PDIVN:位[0],用来设置PCLK与HCLK比例关系
例如:
FCLK:HCLK:PCLK=4:2:1
FCLK=400MHZ(主频)
HCLK=200M
PCLK=100M
void Set_Clk(void)
{
int i;
U8 key;
U32 mpll_val = 0 ;
i = 2 ;
switch ( i ) {
case 0: //200
key = 12;
mpll_val = (92<<12)|(4<<4)|(1);
break;
case 1: //300
key = 13;
mpll_val = (67<<12)|(1<<4)|(1);
break;
case 2: //400
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break; //FCLK=2*(92+8)*(12000000)/(3+2^1)=400000000=400MHz
case 3: //440!!!
key = 14;
mpll_val = (102<<12)|(1<<4)|(1);
break;
default:
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
}
ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); //设置rMPLLCON得到FCLK
ChangeClockDivider(key, 12); //经过CLKDIVE设置,确定FCLK、HCLK、PCLK三者之间的关系
}
int i;
U8 key;
U32 mpll_val = 0 ;
i = 2 ;
switch ( i ) {
case 0: //200
key = 12;
mpll_val = (92<<12)|(4<<4)|(1);
break;
case 1: //300
key = 13;
mpll_val = (67<<12)|(1<<4)|(1);
break;
case 2: //400
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break; //FCLK=2*(92+8)*(12000000)/(3+2^1)=400000000=400MHz
case 3: //440!!!
key = 14;
mpll_val = (102<<12)|(1<<4)|(1);
break;
default:
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
}
ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); //设置rMPLLCON得到FCLK
ChangeClockDivider(key, 12); //经过CLKDIVE设置,确定FCLK、HCLK、PCLK三者之间的关系
}
void ChangeClockDivider(int hdivn_val,int pdivn_val)
{
int hdivn=2, pdivn=0;
// hdivn_val (FCLK:HCLK)ratio hdivn
// 11 1:1 (0)
// 12 2:1 (1)
// 13 3:1 (3)
// 14 4:1 (2)
// pdivn_val (HCLK:PCLK)ratio pdivn
// 11 1:1 (0)
// 12 2:1 (1)
switch(hdivn_val) {
case 11: hdivn=0; break;
case 12: hdivn=1; break;
case 13:
case 16: hdivn=3; break;
case 14:
case 18: hdivn=2; break;
}
switch(pdivn_val) {
case 11: pdivn=0; break;
case 12: pdivn=1; break;
}
//Uart_Printf("Clock division change [hdiv:%x, pdiv:%x] ", hdivn, pdivn);
rCLKDIVN = (hdivn<<1) | pdivn; PCLK HCLK 分频
switch(hdivn_val) { //摄像头时钟分频
case 16: // when 1, HCLK=FCLK/8.
rCAMDIVN = (rCAMDIVN & ~(3<<8)) | (1<<8);
break;
case 18: // when 1, HCLK=FCLK/6.
rCAMDIVN = (rCAMDIVN & ~(3<<8)) | (1<<9);
break;
}
if(hdivn!=0)
MMU_SetAsyncBusMode();
else
MMU_SetFastBusMode();
{
int hdivn=2, pdivn=0;
// hdivn_val (FCLK:HCLK)ratio hdivn
// 11 1:1 (0)
// 12 2:1 (1)
// 13 3:1 (3)
// 14 4:1 (2)
// pdivn_val (HCLK:PCLK)ratio pdivn
// 11 1:1 (0)
// 12 2:1 (1)
switch(hdivn_val) {
case 11: hdivn=0; break;
case 12: hdivn=1; break;
case 13:
case 16: hdivn=3; break;
case 14:
case 18: hdivn=2; break;
}
switch(pdivn_val) {
case 11: pdivn=0; break;
case 12: pdivn=1; break;
}
//Uart_Printf("Clock division change [hdiv:%x, pdiv:%x] ", hdivn, pdivn);
rCLKDIVN = (hdivn<<1) | pdivn; PCLK HCLK 分频
switch(hdivn_val) { //摄像头时钟分频
case 16: // when 1, HCLK=FCLK/8.
rCAMDIVN = (rCAMDIVN & ~(3<<8)) | (1<<8);
break;
case 18: // when 1, HCLK=FCLK/6.
rCAMDIVN = (rCAMDIVN & ~(3<<8)) | (1<<9);
break;
}
if(hdivn!=0)
MMU_SetAsyncBusMode();
else
MMU_SetFastBusMode();
}
ARM920T有三种时钟模式:FastBus(快总线模式),synchronous(同步模式), asynchronous(异步模式)
FastBus(快总线模式)该模式将时钟BCLK作为时钟GCLK的源,时钟FCLK被忽略。主要用于带有高速存储器的系统。
synchronous(同步模式)
该模式主要用于带有低速存储器的系统。时钟BCLK和时钟FCLK作为时钟GCLK的源,BCLK用于控制AMBA存储接口,FCLK用于控制内部的ARM9TDMI处理器核和任何cache缓存的操作。 FCLK的频率必须比BCLK高且是其整数陪,且在FCLK是高电平时BCLK才跳变。
asynchronous(异步模式)
该模式主要用于带有低速存储器的系统。时钟BCLK和时钟FCLK作为时钟GCLK的源,BCLK用于控制AMBA存储接口,FCLK用于控制内部的ARM9TDMI处理器核和任何cache缓存的操作。FCLK的频率只需比BCLK高。
static void cal_cpu_bus_clk(void) //cal = “calculator”,计算器的简写
{ //这个函数用来计算cpu总线时钟
static U32 cpu_freq;
static U32 UPLL;
U32 val;
U8 m, p, s;
val = rMPLLCON; //将rMPLLCON拆开
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
//(m+8)*FIN*2 不要超出32位数!
FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1<<s))*100; //FCLK=400M FIN=12000000
val = rCLKDIVN; //将rCLKDIVN拆开
m = (val>>1)&3;
p = val&1;
val = rCAMDIVN;
s = val>>8;
switch (m) {
case 0:
HCLK = FCLK;
break;
case 1:
HCLK = FCLK>>1;
break;
case 2:
if(s&2)
HCLK = FCLK>>3;
else
HCLK = FCLK>>2;
break;
case 3:
if(s&1)
HCLK = FCLK/6;
else
HCLK = FCLK/3;
break;
}
if(p)
PCLK = HCLK>>1;
else
PCLK = HCLK;
if(s&0x10)
cpu_freq = HCLK;
else
cpu_freq = FCLK;
val = rUPLLCON;//rUPLLCON内存的值为0x00038022,求出UPLL的值为48MHz;时钟分频控制寄存器CLKDIVN[3]=0时UCLK=UPLL
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
UPLL = ((m+8)*FIN)/((p+2)*(1<<s));
UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL; USBCLK的值必须是48MHz
Uart_Printf(" FCLK:%dMHz,HCLK:%dMHz,PCLK=%dMHZ ",FCLK/1000/1000,HCLK/1000/1000,PCLK/1000/1000);
}
/
{ //这个函数用来计算cpu总线时钟
static U32 cpu_freq;
static U32 UPLL;
U32 val;
U8 m, p, s;
val = rMPLLCON; //将rMPLLCON拆开
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
//(m+8)*FIN*2 不要超出32位数!
FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1<<s))*100; //FCLK=400M FIN=12000000
val = rCLKDIVN; //将rCLKDIVN拆开
m = (val>>1)&3;
p = val&1;
val = rCAMDIVN;
s = val>>8;
switch (m) {
case 0:
HCLK = FCLK;
break;
case 1:
HCLK = FCLK>>1;
break;
case 2:
if(s&2)
HCLK = FCLK>>3;
else
HCLK = FCLK>>2;
break;
case 3:
if(s&1)
HCLK = FCLK/6;
else
HCLK = FCLK/3;
break;
}
if(p)
PCLK = HCLK>>1;
else
PCLK = HCLK;
if(s&0x10)
cpu_freq = HCLK;
else
cpu_freq = FCLK;
val = rUPLLCON;//rUPLLCON内存的值为0x00038022,求出UPLL的值为48MHz;时钟分频控制寄存器CLKDIVN[3]=0时UCLK=UPLL
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
UPLL = ((m+8)*FIN)/((p+2)*(1<<s));
UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL; USBCLK的值必须是48MHz
Uart_Printf(" FCLK:%dMHz,HCLK:%dMHz,PCLK=%dMHZ ",FCLK/1000/1000,HCLK/1000/1000,PCLK/1000/1000);
}
/
定时器
频率
计数值
void Timer0_init(void)
{
rTCFG0 = 49; //pclk/(49+1)
rTCFG1 = 0x03; //16分频=62500HZ
//定时器频率= 1M /(49+1)/16=62500HZ
rTCNTB0 = 62500/2; //TCNTB0[15:0]=计数值
rTCMPB0 = 0; //比较值
rTCON |=(1<<1); //将计数值装入TCNTB0、TCMPB0
rTCON =0x09; //1001 自动重载,启动。
ClearPending(BIT_TIMER0); //清除SRCPND、INTPND 此处课省略
/*当达到0.5秒时,执行中断函数IRQ_Timer0_Handle */
pISR_TIMER0 = (U32)IRQ_Timer0_Handle; //中断向量 = 中断函数
EnableIrq(BIT_TIMER0); //手动打开屏蔽 INTMSK
}
static void __irq IRQ_Timer0_Handle(void)
{
ClearPending(BIT_TIMER0); //清中断
Led1_run();
}
{
rTCFG0 = 49; //pclk/(49+1)
rTCFG1 = 0x03; //16分频=62500HZ
//定时器频率= 1M /(49+1)/16=62500HZ
rTCNTB0 = 62500/2; //TCNTB0[15:0]=计数值
rTCMPB0 = 0; //比较值
rTCON |=(1<<1); //将计数值装入TCNTB0、TCMPB0
rTCON =0x09; //1001 自动重载,启动。
ClearPending(BIT_TIMER0); //清除SRCPND、INTPND 此处课省略
/*当达到0.5秒时,执行中断函数IRQ_Timer0_Handle */
pISR_TIMER0 = (U32)IRQ_Timer0_Handle; //中断向量 = 中断函数
EnableIrq(BIT_TIMER0); //手动打开屏蔽 INTMSK
}
static void __irq IRQ_Timer0_Handle(void)
{
ClearPending(BIT_TIMER0); //清中断
Led1_run();
}