ARM9(S3C2440)时钟与定时器
时钟概念
一、时钟脉冲:一个按一定电压幅度,一定时间间隔连续发出的脉冲信号。
二、时钟频率:单位时间(如一秒)内产生的时钟脉冲个数。
时钟作用
时钟信号时时序逻辑(如一些芯片要一定的延时时间才能工作)的基础,它用于决定逻辑单元中的状态何时更新。数字芯片中众多的晶体管都工作在开关状态,他们的导通和关断动作无不是按照时钟信号的节奏进行的。
(1)时钟产生-----晶振
优点:性能稳定,频率稳定,准确。
缺点:频率仅由晶体决定,通常是特定晶体被制成客户所需要的振荡器,导致成本,周期较长,不利于快速上市,而且难以获得非标准的频率。
(2)时钟产生-----PLL
PLL(锁相环)合成器是一种更为复杂的系统时钟源。通用PLL合成器需要一个外部晶体并包含一个能够对晶体的特定频率加倍或分频的集成锁相环(PLL)电路。
S3C2440有两个PLL:MPLL与UPLL.
(1)、UPLL专用于USB设备。
(2)、MPLL用于CPU及其他外围器件。
通过MPLL会产生三个部分的时钟频率:FCLK,HCLK,PCLK.
FCLK用于CPU核,HCLK用于AHB(Advanced High-performance Bus:常用语高速外设),PCLK用于APB(Advanced Peripheral Bus:常用语低速外设)总线的设备(比如UART).
(1)、上电几毫秒后,(按下复位键(可以不需要这一步,系统自动复位)),外部晶振输出稳定,FCLK=外部晶振频率(12MHZ),nRESET信号恢复高电平后,
CPU开始执行命令。
(2)、在设置MPLL几个寄存器后,需要等待一段时间(Lock Time),MPLL的输出才稳定。在这段时间内,FCLK停止震动,CPU停止工作。Lock Tine的长短
由寄存 器LOCKTIME设定。
(3)、Lock Time之后,MPLL输出正常,CPU工作在新的FCLK(如400MHZ)下。
寄存器
S3C2440的时钟频率寄存器:
(1)、LOCKTIME寄存器
(2)、MPLLCON寄存器
(3)、CLKDIVN寄存器
MPLLCON
该寄存器用于设置FCLK与Fin (如我们的外部晶振为12MHz,那么Fin就是12MHz) 的倍数。公式如下:
MPLL(FCLK) = ( 2*m*Fin ) / (p * 2^s)
其中:m=MDIV+8 , p=PDIV+2, s=SDIV
CLKDIVN
该寄存器用于设置FCLK,HCLK,PCLK三者之间的比例。
定时器
定时器部件的时钟源为PCLK,首先通过两个8位的预分频器降低频率:定时器0、1共用第一个预分频器,定时器2、3、4共用第二个预分频器。预分频器的输出将进入第二级分频器,它们输出5中频率的时钟:2分频、4分频、8分频、16分频或者外部TCLK0\TCLK1,每个定时器的工作时钟也可以从这5种频率中选择。
定时器驱动流程:初始化--------------->启动
(1)、定时器初始化
1、定时器时钟频率 (如定时器时钟频率为50hz,则每秒减去50)
2、设置定时器计数器 (如计数值为100,时钟频率为50hz,则定时器在2秒后超时)
3、设置中断处理函数 (定时器超时或产生中断要处理的函数)
注意:(本例使用定时器0)
定时器时钟频率:
设置定时器计数器
通过设置TCON寄存器可以装载定时器计数值,并启动定时器。(设置第四位从新加载定时器)。
定时器工作流程
1、程序初始化,设置TCMPBn,TCNTBn这两个寄存器,他们表示定时器n的比较值、初始计数值。
2、随之设置TCON寄存器启动定时器n,这时,CMPBn,TCNTBn的值将被装入其内部寄存器TCMPn、TCNTn中。在定时器n的工作频率下,TCNTn开始减一计数,其值可
以通过TCNTO0寄存器得知。
3、当TCNTn的值等于TCMPn的值时,定时器n的输出管脚TOUTn反转;TCNTn继续减一计数。
代码:定时器控制LED.
主要代码为:
#define GLOBAL_CLK 1
#include <stdlib.h>
#include <string.h>
#include "def.h"
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"
#include "2440slib.h"
#include "mmu.h"
#include "profile.h"
#include "memtest.h"
void Timer0_init(void);
static void __irq IRQ_Timer0_Handle(void);
void Set_Clk(void);
static void cal_cpu_bus_clk(void);
void Led1_init(void);
void Led1_run(void);
/*延时函数*/
void delay(int times)
{
int i,j;
for(i=0;i<times;i++)
for(j=0;j<400;j++);
}
/*主函数*/
void Main(void)
{
/*系统时钟设置*/
Set_Clk();
/*LED初始化*/
Led1_init();
/*定期器初始化*/
Timer0_init();
while(1);
}
/*定时器初始化*/
void Timer0_init(void)
{
//Timer 0 init
rTCFG0 = 49; //pclk/(49+1)
rTCFG1 = 0x03; //16分频为62500HZ ,每秒减去62500,现在我们控制led每隔0.5秒闪烁一次,
//则: rTCNTB0 = 62500/2;
rTCNTB0 = 62500/2; //TCNTB0[15:0]=计数值
rTCMPB0 = 0;
rTCON =0x09; //将计数值装入TCNTB0、TCMPB0
ClearPending(BIT_TIMER0);
pISR_TIMER0 = (U32)IRQ_Timer0_Handle;
EnableIrq(BIT_TIMER0);
}
/*定时器中断处理函数*/
static void __irq IRQ_Timer0_Handle(void)
{
ClearPending(BIT_TIMER0); //为了下次继续产生中断
Led1_run();
}
/*系统时钟设置函数*/
void Set_Clk(void)
{
int i;
U8 key;
U32 mpll_val = 0 ;
i = 2 ; //don't use 100M!
//boot_params.cpu_clk.val = 3;
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;
case 3: //440!!!
key = 14;
mpll_val = (102<<12)|(1<<4)|(1);
break;
default:
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
}
//init FCLK=400M, so change MPLL first
ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); //set the register--rMPLLCON
ChangeClockDivider(key, 12); //the result of rCLKDIVN [0:1:0:1] 3-0 bit
cal_cpu_bus_clk(); //HCLK=100M PCLK=50M
}
static void cal_cpu_bus_clk(void)
{
static U32 cpu_freq;
static U32 UPLL;
U32 val;
U8 m, p, s;
val = 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;
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;
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;
}
/*LED初始化*/
void Led1_init(void)
{
rGPBCON &= ~(0x3<<10);
rGPBCON |= (0x1<<10);
}
/*LED点亮*/
void Led1_run(void)
{
//rGPBDAT = rGPBDAT^(0x1<<5);
if(rGPBDAT &(1<<5))
rGPBDAT &=~(1<<5);
else
rGPBDAT |=(1<<5);
}
2440addr.h
#define ClearPending(bit) {\
rSRCPND = bit;\
rINTPND = bit;\
rINTPND;\
}
#define pISR_TIMER0 (*(unsigned *)(_ISR_STARTADDRESS+0x48))
#define EnableIrq(bit) rINTMSK &= ~(bit)
define rINTMSK (*(volatile unsigned *)0x4a000008) //Interrupt mask control
#define BIT_TIMER0 (0x1<<10)
void ChangeMPllValue(int mdiv,int pdiv,int sdiv)
{
rMPLLCON = (mdiv<<12) | (pdiv<<4) | sdiv;
}
void ChangeClockDivider(int hdivn,int pdivn)
{
// hdivn,pdivn FCLK:HCLK:PCLK
// 0,0 1:1:1
// 0,1 1:1:2
// 1,0 1:2:2
// 1,1 1:2:4
rCLKDIVN = (hdivn<<1) | pdivn;
if(hdivn)
MMU_SetAsyncBusMode();
else
MMU_SetFastBusMode();
}
.............