定时器中断---那些年我们一起玩mini2440(arm9)裸机
(时钟体系)http://blog.sina.com.cn/s/blog_815420370101ni84.html
时钟概念:
★时钟脉冲:一个按一定电压幅度,一定时间间隔连续发出的脉冲信号;
★时钟频率:在单位时间(如: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会产生三个部分的时钟频率:FCLK、HCLK、PLCK.
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的时钟频率就是设置相关的几个寄存器:
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
FCLK与Fin的计算关系式如下:
MPLL(FCLK) = (2*m*Fin)/(p*2^s)
其中:m= MDIV+8 ,p=PDIV+2 , s= SDIV
寄存器-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
巧计,速记2图:
S3C2440定时器
S3C2440共有5个16位的定时器。其中定时器0、1、2、3有PWM功能,他们都有一个输出引脚,可以通过定时器来控制引脚周期性的高、低电平变化;
定时器4没有输出引脚。
S3C2440定时器
定时器部件的时钟源为--PCLK,(经过两次分频)
首先通过两个8位的预分频器降低频率:定时器0、1共用第一预分频器,
定时器2、3、4公用第二个预分频器。
再次预分频器的输出将进入第二级分频器,它们输出5种频率的时钟:2分频、4分频、8分频、16分频或者外部时钟TCLK0、TCLK1,每个定时器的工作时钟也可以从这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设置)
TCMPB0:定时器n的比较值
TCNTB0:初始计数值(放到TCNT0后TCNT0会自动减一直到减到== TCMPB0)
之后设置TCON会自动重新装载TCMPB0和TCNTB0再次重新计数
定时器工作流程
1. 程序初始化,设置TCMPBn、TCNTBn这两个寄存器,它们表示定时器n的比较值、初始计数值。
2. 设置TCON寄存器定时器n,这时TCMPBn、TCNTBn的值将被装入其内部寄存器TCMPn、TCNTn中。在定时器n的工作频率下,TCNTn开始减一计数,其值可以通过读取TCNTOn寄存器得知。
3. 当TCNTn的值等于TCMPn的值时,定时器n的输出管脚TOUTn反转;TCNTn继续减一计数。
4. 当TCNTn的值到达0时,器输出管脚TOUTn再次反转,并触发定时器n的中断。
5. 当TCNTn的值到达0时,如果TCON寄存器中将定时器n设为“自动加载”,则TCMPB0和TCNTB0寄存器的值被自动装入TCMP0和TCNT0寄存器中,下一个计数流程开始。
- #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);
- /*************************************************
- Description : 延时函数
- **************************************************/
- void delay(int times)
- {
- int i,j;
- for(i=0;i<times;i++)
- for(j=0;j<400;j++);
- }
- /*************************************************
- Description : 主功能函数
- **************************************************/
- void Main(void)
- {
- Set_Clk();
- MMU_Init();
- //Uart_Init(0,115200);
- //Uart_Select(0);
- Led1_init();
- Timer0_init();
- while(1);
- }
- /*************************************************
- Description : 定时器的初始化
- **************************************************/
- 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);
- }
- /*************************************************
- Description : 定时器0的中断处理函数
- **************************************************/
- static void __irq IRQ_Timer0_Handle(void)
- {
- ClearPending(BIT_TIMER0); //清中断
- Led1_run();
- }
- /*************************************************
- 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
- }
- /*************************************************
- 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;
- }
- /*************************************************
- Description : 初始化Led1
- **************************************************/
- void Led1_init(void)
- {
- rGPBCON &= ~(0x3<<10);
- rGPBCON |= (0x1<<10);
- }
- /*************************************************
- Description : 运行Led1
- **************************************************/
- void Led1_run(void)
- {
- //rGPBDAT = rGPBDAT^(0x1<<5);
- if(rGPBDAT &(1<<5))
- rGPBDAT &=~(1<<5);
- else
- rGPBDAT |=(1<<5);
- }