定时器中断---那些年我们一起玩mini2440(arm9)裸机

时间:2022-10-03 23:30:38

(时钟体系)

时钟概念:

★时钟脉冲:一个按一定电压幅度,一定时间间隔连续发出的脉冲信号;

 

★时钟频率:在单位时间(如:1秒)内产生的时钟秒冲数;

时钟的作用:

时钟信号是时序逻辑的基础,它用于决定逻辑单元中的状态何时更新。数字芯片中众多的晶体管都工作在开关状态,它们的导通和关断动作无不是按照时钟信号的节奏进行的。

 

时钟的作用(简洁版)

系统中的设备需要有个时间来和它进行同步即靠时钟!

时钟的产生-晶振

晶振:晶体振荡器,是用石英晶体精密切割做成。

优点:振荡频率非常稳定;振荡频率很准确;结构简单、噪声低。

缺点:生产成本高,交货周期较长,不利于客户加快产品上市时间,而且难以获得非常标准的频率。

 

时钟产生-PLL

PLL:(锁相环)合成器是一种更为复杂的系统时钟源。通过PLL合成器需要一个外部晶体并包含一个能够对晶体的特定频率加倍或分频的集成锁相环PLL电路

 

S3C2440时钟体系

S3C2440的主时钟晶振来自外部晶振(XTIPLL,或者是外部时钟(EXTCLK)。时钟生成器包含了一个振荡器(振荡放大器),其连接外部晶振,可以产生需要的高频,通过引脚OM[3:2]来决定时钟源时Crystal还是EXTCLK.

 

S3C2440时钟体系

S3C2440有两个PLL:(1)MPLL(2)UPLL

UPLL专用于USB设备。

MPLL 用于CPU及其他外围器件。

通过MPLL会产生三个部分的时钟频率:FCLKHCLKPLCK.

FCLK:用于CPU核;

HCLK:用于AHB(常用于高速外设)总线的设备,比如:SDRAM

PCLK:用于APB(常用于低速外设)总线的设备,比如:UART.

定时器中断---那些年我们一起玩mini2440(arm9)裸机

       时钟启动流程:

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的时钟频率就是设置相关的几个寄存器:

1.LOCKTIME寄存器

2.MPLLCON寄存器

3.CLKDIVN寄存器

 

寄存器-LOCKTIME

MPLL启动后需要等待一段时间(Lock Time,使得其输出稳定。

[31:16]用于UPLL,

[15:0]用于MPLL.

使用缺省值0xffff ffff

 

 

寄存器-MPLLCON

该寄存器用于设置FCLK(cpu的时钟频率)Fin的倍数(Fin:输入的时钟频率)

[19:12]的值称为MDIV

[9:4]的值称为PDIV

[1:0]的值称为SDIV

 

FCLKFin的计算关系式如下:

MPLL(FCLK) = (2*m*Fin)/(p*2^s)

其中:m= MDIV+8  ,p=PDIV+2 , s= SDIV

定时器中断---那些年我们一起玩mini2440(arm9)裸机

寄存器-CLKDIVN

该寄存器用于设置FCLKHCLKPCLK三者的比例

  HDIVN:[2:1],用来设置HCLKFCLK比例关系

  PDIVN:[0],用来设置PCLKHCLK比例关系

 

例如:

FCLKHCLKPCLK=4:2:1

FCLK=400MHZ(主频)

HCLK=200M

PCLK=100M

巧计,速记2图:

图1:

定时器中断---那些年我们一起玩mini2440(arm9)裸机

图2:

定时器中断---那些年我们一起玩mini2440(arm9)裸机

S3C2440定时器

S3C2440共有516位的定时器。其中定时器0123PWM功能,他们都有一个输出引脚,可以通过定时器来控制引脚周期性的高、低电平变化;

定时器4没有输出引脚。

 

S3C2440定时器

定时器部件的时钟源为--PCLK,(经过两次分频)

首先通过两个8位的预分频器降低频率:定时器01共用第一预分频器

定时器234公用第二个预分频器

再次预分频器的输出将进入第二级分频器,它们输出5种频率的时钟:2分频、4分频、8分频、16分频或者外部时钟TCLK0TCLK1,每个定时器的工作时钟也可以从这5种频率中选择。

 

定时器初始化:步骤:

1.定时器时钟频率

(定时器的时钟频率需要经过两次的分频)例如:定时器的时钟频率50--表示:每一秒钟把定时器的初始值减去50

2.设置定时器计数值;

--即设置定时器的初始值(比较值为0

3.设置中断处理函数;--即定时器到时间时去处理什么函数

 

定时器输出时钟频率 =PCLK/{prescaler value+1} /{divider value}

{prescaler  value} =0 ~255 (第一次预分频通过TCFG0设置)

{divider value} = 2, 4, 8, 16 (第二次预分频通过TCFG1设置)

 定时器中断---那些年我们一起玩mini2440(arm9)裸机

 

定时器中断---那些年我们一起玩mini2440(arm9)裸机

定时器中断---那些年我们一起玩mini2440(arm9)裸机

 

定时器中断---那些年我们一起玩mini2440(arm9)裸机

定时器中断---那些年我们一起玩mini2440(arm9)裸机

 

 

TCMPB0:定时器n的比较值

 

TCNTB0:初始计数值(放到TCNT0TCNT0会自动减一直到减到== TCMPB0

 

之后设置TCON会自动重新装载TCMPB0TCNTB0再次重新计数

 

 

定时器工作流程

1.      程序初始化,设置TCMPBnTCNTBn这两个寄存器,它们表示定时器n的比较值、初始计数值。

2.      设置TCON寄存器定时器n,这时TCMPBnTCNTBn的值将被装入其内部寄存器TCMPnTCNTn中。在定时器n的工作频率下,TCNTn开始减一计数,其值可以通过读取TCNTOn寄存器得知。

3.      TCNTn的值等于TCMPn的值时,定时器n的输出管脚TOUTn反转;TCNTn继续减一计数。

4.      TCNTn的值到达0时,器输出管脚TOUTn再次反转,并触发定时器n的中断。

5.      TCNTn的值到达0时,如果TCON寄存器中将定时器n设为“自动加载”,则TCMPB0TCNTB0寄存器的值被自动装入TCMP0TCNT0寄存器中,下一个计数流程开始。

 

 

#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);

/*************************************************

Function name: delay

Parameter    : times

Description    : 延时函数

Return          : void

Argument     : void

Autor & date :

**************************************************/

void delay(int times)

{

    int i,j;

    for(i=0;i<times;i++)

       for(j=0;j<400;j++);

}

/*************************************************

Function name: Main

Parameter    : void

Description    : 主功能函数

Return          : void

Argument     : void

Autor & date : Daniel

**************************************************/

void Main(void)

{      

   

    Set_Clk();

    MMU_Init();

    //Uart_Init(0,115200);

    //Uart_Select(0);

    Led1_init();

    Timer0_init();

       while(1);

       

}      

     

/*************************************************

Function name: Timer0_init(void)

Parameter    : void

Description    : 定时器的初始化

Return          : void

Argument     : void

Autor & date : Daniel

**************************************************/     

void Timer0_init(void)

{

  

   //Timer 0 init

  rTCFG0 = 49;              //pclk/(49+1)

  rTCFG1 = 0x03;            //16分频=62500HZ

  rTCNTB0 = 62500/2;          //TCNTB0[15:0]=计数值  

  rTCMPB0 = 0;

  rTCON |=(1<<1);           //将计数值装入TCNTB0、TCMPB0

  

  rTCON =0x09;

  

  rPRIORITY = 0x00000000;     // 默认优先级

  rINTMOD = 0x00000000;       // 默认优先级

  

ClearPending(BIT_TIMER0);

/*当达到0.5秒时,执行中断函数IRQ_Timer0_Handle */

  pISR_TIMER0 = (U32)IRQ_Timer0_Handle;

 

  EnableIrq(BIT_TIMER0); 

}

/*************************************************

Function name: IRQ_Timer0_Handle(void)

Parameter    : void

Description    : 定时器0的中断处理函数

Return          : void

Argument     : void

Autor & date :

**************************************************/     

static void __irq IRQ_Timer0_Handle(void)

{

    ClearPending(BIT_TIMER0);  //清中断

    Led1_run();

    

}

 

     

/*************************************************

Function name: Set_Clk()

Parameter    : void

Description    : 设置CPU的时钟频率

************

MDIV: 19~12  所以MDIV是92;

PDIV : 9~4  所以PDIV是4

SDIV : 1~0  所以SDIV是1

根据公式:FCLK与Fin的计算关系式如下:

MPLL(FCLK) = (2*m*Fin)/(p*2^s)

其中:m= MDIV+8  ,p=PDIV+2 , s= SDIV

所以:MPLL(FCLK) = (2*100*12)/(3*2^1)=400

*************

**************************************************/

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//经过CLKDIVE设置FCLK、HCLK、PCLK三者之间的关系

       cal_cpu_bus_clk();    //HCLK=100M   PCLK=50M

}

/*************************************************

Function name: cal_cpu_bus_clk

Parameter    : void

Description    : 设置PCLK\HCLK\FCLK的频率

/******/

寄存器-CLKDIVN

该寄存器用于设置FCLK、HCLK、PCLK三者的比例

★   HDIVN:位[2:1],用来设置HCLK与FCLK 比例关系

★   PDIVN:位[0],用来设置PCLK与HCLK 比例关系

/******/

**************************************************/

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;

}

 

/*************************************************

Function name: Led1_init()

Parameter    : void

Description    : 初始化Led1

Return          : void

Argument     : void

Autor & date : Daniel

**************************************************/

void Led1_init(void)

{

    rGPBCON &= ~(0x3<<10);

    rGPBCON |=  (0x1<<10);

}

/*************************************************

Function name: Led1_run()

Parameter    : void

Description    : 运行Led1

Return          : void

Argument     : void

Autor & date : Daniel

**************************************************/

void Led1_run(void)

{

    //rGPBDAT = rGPBDAT^(0x1<<5);

   

    if(rGPBDAT &(1<<5))

       rGPBDAT &=~(1<<5);

    else

       rGPBDAT |=(1<<5);

}