三、系统时钟和定时器的设置
⑴系统时钟原理分析
时钟决定2440执行速度,2440可以使用外部提供的时钟源,也可以使用外部的晶振然后通过内部的晶振获得时钟频率;具体选择使用哪一个时钟源看下图:
开发板一般吧引脚M2和M3连接的GND,所以说全部使用的是晶振(crystal)
除此之外,2440提供了3个时钟源FCLK(用于cpu核),HCLK(advancedhigh performance bus,用于AHB总线设备,比如cpu核,dma,usb等),PCLK(advanced performance bus,用于外围设备,比如看门狗,pwm定时器,adc等等),对于锁相环,2440拥有两种PLL其中一个为UPLL,其专用于usb设备,还有一个是MPLL用于设置fclk,hclk,pclk。
假如不设置PLL此时FCLK为Fin 也就是外部晶振为12M。
设置PLL后,就是设置相应的寄存器来得到所需要的值,S3C2440使用了三个倍频因子MDIV、PDIV和SDIV来设置倍频,通过寄存器MPLLCON和UPLLCON可设置倍频因子。其中MPLLCON寄存器用于设置处理器内核时钟主频FCLK,其输入输出频率间的关系为
FCLK=MPLL=(2*m*Fin)/(p*2^s)
其中m=(MDIV+8), p=(PDIV+2), s=SDIV。
其中UPLLCON寄存器用于产生48MHz或96MHz,提供USB时钟(UCLK),其输入输出频率间的关系为
UCLK=UPLL=(m *Fin) / (p * 2^s)
其中m=(MDIV+8), p=(PDIV+2), s=SDIV。
上面的MPLLCON和UPLLCON与MDIV,PDIV,SDIV的关系为:
也就PLLCON的01位是SDIV,4-9位是PDIV,12-19位MDIV。
为了方便,2440的芯片手册给出了参考设置PLL的数值:
假设晶振为12M,可以参考上图的蓝色加深部分的设置数值。上图的48M和96M是设置UPLLCLK使用的。
通过表可以完成FCLK的设置,正如前面所说的,除了FCLK还有HCLK和PCLK需要设置,接下来就可以设置HCLK和PCLK,此时需要两个寄存器:
对于CLKDIVN
通过此寄存器可以得到相应的HCLK和PCLK。但是由于在设置HDIVN(也就是CLKDIVN的1、2位)的时候需要用到CAMDIVN的第8位和第9位,CAMDIVN寄存器为:
结合在上面的这两个寄存器可以得到频率。通过上面的两个表格可以得到四个值:HDIVN ,PDIVN,HCLK3_HALF和 HCLK4_HALF,为了方面描述它们的关系,可以参考表:
下手册的后面有对时钟的如下注释:
这是说假如HDIVN不是0的话要加上:
mrcp15,0,r0,c1,c0,0
orr r0,r0,#R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
通过上面的设置可以完成设置FCLK、HCLK、PCLK,实际上2440在设置时钟前后的运行状态是这样的:(下面再给出设置MPLL的过程图(UPLL类似))
通过上图可以看到,在上电一段是时间后,等待nRESET信号(复位信号)恢复高电平后,然后可以执行PLL有关寄存器的操作了。其中OSC为晶振的频率大小为12M,VCO为输出频率。还可以看到在设置完PLL需要等待一段时间才可以输出设定频率,这个等待时间是可以通过LOCKTIME寄存器来设置,这个时间是需要满足一定的最小值的,使用默认值就可以。该寄存器说明见下图:
总结:
设置时钟相对来说比较简单,这里给出的其设置流程:
1、 设置cpu由新的时钟频率到旧的频率下的间隔时间 设置LOCKTIME(默认值即可)
2、 设置FCLK、HCLK、PCLK频率比例,可以通过设置寄存器CLKDIVN 辅助CAMDIVN
3、 /* 如果CLKDIVN 中的HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronousbus mode” */
_asm__(
"mrc p15, 0, r1, c1, c0,0\n" /* 读出控制寄存器 */
"orr r1, r1,#0xc0000000\n" /* 设置为“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
);
4、 根据个人要求设置FCLK时钟 可以通过设置MPLLCON来设置
5、 假如有需要,设置UCLK时钟,可以通过设置UPLLCON来设置
测试设定代码:
void mpll_init(void )
{
//1、 设置cpu由新的时钟频率到旧的频率下的间隔时间设置LOCKTIME(默认值即可)
//2、 设置FCLK、HCLK、PCLK频率比例,可以通过设置寄存器CLKDIVN CAMDIVN
//若设置FCLK为400M HCLK=100M PCLK=50M
//hdivn=2 pdivn=1
rCLKDIVN |= (2<<1) | 1;
rCAMDIVN &=~(1<<9);
//3、 /*如果CLKDIVN中的HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
__asm{
mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */
orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */
mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */
};
// MMU_SetAsyncBusMode();
//4、 根据个人要求设置FCLK时钟可以通过设置MPLLCON来设置
// FCLK=MPLL=(2*m*Fin)/(p*2^s)
// 其中m=(MDIV+8), p=(PDIV+2), s=SDIV
// MDIV=0X5C PDIV=1 SDIV=1 FCLK=400M
//
rMPLLCON=((0x5c<<12) | (1<<4) | 1);
//5、 假如有需要,设置UCLK时钟,可以通过设置UPLLCON来设置
}
⑵定时器原理分析
s3c2440拥有Timer0,1,2,3,4五个16位定时器,其中Timer0,1,2,3具有Pulse Width Modulation (PWM)功能,Timer4引脚没有输出,Timer0比较特殊,拥有适用于大电流的死区生成器。
定时器0 和1 共享一个8 位的预分频器(预定标器),定时器2,3,4 共享另一个8 位预分频器,其构造图如图所示:
可以看出,定时器的时钟源为PCLK,首先经过预分频器降低频率后,进入第二个分频.可以生成5 种不同的分频信号(1/2,1/4,1/8,1/16 和外部时钟TCLK0、TCLK1。
这两个预分频器可以使用TCFG0寄存器来设置,其功能如图:
可以看到,TCFG0的0-7位设置分频器0,用来控制定时器0和定时器1,8到15位用来设置预分频器1,用来控制定时器2、3、4。其频率的计算公式就是:
定时器的时钟频率 = PCLK / {prescaler value+1 } /{divider value}
prescaler value的大小就是上面的寄存器设置每个8位大小是0到255 ,divider value的大小通过TCFG1来设置可以取值2、4、8、16,看下图:
通过上面两个寄存器设置完时钟之后,就可以来控制定时器了,包括是否打开定时器,定时器的时间等等。
首先,需要看到的是TCON,用来控制定时器的打开与关闭,是否自动重新装入初值:
通过上表可实现对5个定时器的控制,其中在人工装入定时器值的时候需要注意,每次重新写入初值的时候需要清零后再写。以timer3为例,介绍一下每一位作用:
Timer 3 auto reload on/off 这是说定时器是是否自动载入定时时间,或者定时器是否是周期性的。
Timer 3 output inverter on/off 这是说是否开启反转模式,开通与否的效果为:
Timer 3 manualupdate该为设置为1是手工把数据装入TCNTB3 & TCMPB3(下面会介绍这两个寄存器)。
Timer 3 start/stop 打开或者关系定时器3.
在打开定时器之前,还需要给定时器装入初值,以确定定时器触发的时间,这里需要两个寄存器:TCMPBn,TCNTBn,这里以定时器0为例,接扫一下,先看寄存器介绍:
通过上表可以看出,TCMPBn用来设置比较值,TCNTBn用来设置初值。设定好这两个寄存器后,定时器就会在规定的频率下,TCNTBn开始减去1计数,直到减小到TCMPBn设定的比较值,此时可以通过读取TCNTOn来观察TCNTBn的值。
使用定时器设置如下:
1、 选定timer0-5定时器,设置预分频器 TCFG0 和分频器TCFG1
2、 给定时器设定定时时间,也就是给TCNTB0装入初值 和 装入TCMPBn寄存器比较器(默认0)
3、 设置定时时间载入方式及其打开定时器 使用寄存器TCON
定时器设置参考代码:
/*
void timer0_init()
{
//1、选定timer0-5定时器,设置预分频器TCFG0 和分频器TCFG1
//定时器的时钟频率 = PCLK / {prescaler value+1 } / {divider value}
// PCLK=50m {prescaler value+1} / {divider value}
rTCFG0 &= ~(0xFF) ;
rTCFG0 |= 99 ; //预分频系数为99
rTCFG1 &= ~(0xf);
rTCFG1 |= 0x03; //16分频
//定时器的时钟频率=50000000/100/16=31250
//2、 给定时器设定定时时间,也就是给TCNTB0装入初值和装入TCMPBn寄存器比较器(默认0)
rTCNTB0=31250;//每秒一次
//TCMPBn取默认值0
//3、 设置定时时间载入方式及其打开定时器使用寄存器TCON
rTCON |= (1 << 1) ; //将TCNTB0和TCMPB0装入内部的TCNT0和TCMP0
rTCON &=~(0xf);
rTCON |= 0x09 ; //自动重载并且打开定时器0
rINTMSK&=~(1<<10);//打开定时器0 中断
timer0_service_Init();//中断服务函数初始化
}
参考:
http://www.cnblogs.com/idle_man/archive/2011/03/01/1968168.html
http://blog.csdn.net/mr_raptor/article/details/6555734
http://www.cnblogs.com/hnrainll/archive/2011/06/28/2092292.html