Mini2440 裸机编程-时钟中断

  使用2440 的时钟定时产生中断,在中断处理程序中修改led 灯的状态。


开头的汇编文件:

@********************************
@file: head.S @功能:初始化,设置中断模式,系统模式,设置好中断处理函数
@********************************

.extern main
.text
.global _start
_start:
@*******************************
@这段代码只核心写中断向量
@*******************************

        b Reset

@0x04:未定义指令终止模式的向量地址
HandleUndef:
        b        HandleUndef

@定义软中断的向量地址
HandleSWI:
        b        HandleSWI

@定义欲取址中断异常向量地址
HandlePrefectchAbort:
        b        HandlePrefectchAbort

@定义数据中止异常
HandleDataAbort:
        b        HandleDataAbort

@定义保留向量地址
HandleNotUsed:
        b        HandleNotUsed
        
@定义中断的向量地址
        b        HandleIRQ

@定义快中断的向量地址
HandleFIQ:
        b        HandleFIQ

@reset的处理函数
Reset:
        ldr        sp, =4096 			@先设置栈指针,以下都是C语言需要设置栈指针
        bl         disable_watch_dog 		@跳到init.c文件去执行

        msr        cpsr_c, #0xd2 		@进入中断模式
        ldr        sp, =3072 			@中断模式的栈指针定义

        msr        cpsr_c, #0xdf 		@进入系统模式
        ldr        sp, =4096 			@设置系统模式的栈指针

	bl 	   clk_init
	bl	   timer0_init	 
        bl         init_led 			@初始化led,使其均输出状态而且熄灭,在init.c
        bl         init_irq 			@调用init.c中的init_irq函数,初始化中断各个寄存器配置
        msr        cpsr_c, #0x5f 		@开启IRQ中断

        ldr        lr, =halt_loop 		@设置返回地址
        ldr        pc, =main 			@跳到main函数执行

halt_loop:
        b          halt_loop

HandleIRQ:
        sub        lr, lr, #4 			@计算中断的返回地址
        stmdb      sp!, {r0-r12, lr} 		@保存使用到的寄存器

	@ldr       r0, =0x56000014			@ WATCHDOG¼Ä´æÆ÷µØÖ·
	@ldr       r1, =0xFFDF
        @str   	   r1, [r0]    

        ldr        lr, =int_return
        ldr        pc, =EINT_Handle 		@调用中断处理函数

int_return:
        ldmia        sp!, {r0-r12, pc}^
上面程序在开头设置中断跳转指令,关闭看门狗,设置各种模式下的sp寄存器置,


初始化时钟

void clk_init(void)
{
    rLOCKTIME=0xffff;                                           //locktime
    rCLKCON |=(1<<13);                                          //gpio enable pclk  AC97 module
    rCLKDIVN =((PDIVN<<0) | (HDIVN<<1));                        //
    rMPLLCON |=((MDIV<<12) | (PDIV<<4) | (SDIV<<0));            // f_out=400MHZ
} 

LOCKTIME寄存器的作用这里说下:

  刚设置好PLL时,系统认为这是PLL还没稳定,所以这时不用PLL的时钟,而用外部晶振做时钟,将PLL锁住,过了LOCKTIME后认为PLL已经稳定了,才使用PLL给系统提供时钟。例如S3c2410手册上给出锁住时间必须大于150us,外部晶振为12M,那么(1/12M)*N>150us,其中N为U_LTIME或M_LTIME,N>1800,可以设置U_LTIME或和M_LTIME为0xfff,(0xfff=4096>1800),此时LOCKTIME=0xffffff.在2440 手册上这个时间是不低于300us,外部晶振12M,N > 900,一般这个值都是使用初始值,也就是最大值。

CPU 时钟即就是FCLK 时钟,HCLK 从FCLK 分频,PCLK 从HCLK 分频。具体的分频比例需要设置寄存器CLKDIVN ,详细见手册。

MPLLCON寄存器控制控制频率Mpll的输出 ,有一个公式:

Mpll = (2 * m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
这里输出FCLK = 400MHz。


初始化 timer0

void timer0_init(void)
{
    rTCFG0 = 119;  //(0x31<<0);
    rTCFG1 = 3; //&=~(15<<0);                   // timer0_Iinput_freq=500KHZ

    rTCNTB0 = 26041;                            //TOUTfreq=500KHZ/500=1KHZ
    rTCMPB0 = 0x00;

    rTCON |=(1<<1);                     // Determine the manual update for Timer 0. 1 = Update TCNTB0 

    rTCON = 0x09;                       // Determine start/stop for Timer 0.  1 = Start for Timer 0
                                        // Determine auto reload on/off for Timer 0.   1 = Interval mode(auto reload)
}

 预分频器值(prescaler value)在TCFG0(0X5100 0000)中设置,0-7位设置TIMER0,TIMER1的预分频值。8-15位设置TIMER2,3,4的预分频值。

 第二个分频器的值(divider value)在TCFG1(0X5100 0004)中设置,0-3位设置TIMER0的分频值,4-7位设置TIMER1的分频值,8-11位设置TIMER2的分频值,12-15位设置TIMER3的分频值,16-19位设置TIMER4的分频值。
定时器输入频率的计算公式是:
Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}

{prescaler value} = 0~255

{divider value} = 2, 4, 8, 16

由此可以算出 timer0的输入频率是

PCLK = 50MHZ.

FrequencyIn = 50M / (119 + 1) / 16 = 26041.

也就是说一秒内Timer0会进行26406次递减和比较操作,假设我们现在是要让Timer0每1秒产生一次中断的话,我们应该设置Counter=26406和Camparer=0,既:

    rTCNTB0 = 26041;                            //TOUTfreq=500KHZ/500=1KHZ
    rTCMPB0 = 0x00;
然后开启定时器。


再初始化led:

void init_led(void)
{       
        //设置led引脚为输出引脚,初始化都熄灭而且上拉电阻disable
        GPBCON = (1 << 10) | (1 << 12) | (1 << 14) | (1 << 16);
        GPBDAT |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
        GPBUP |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);  
}      

初始化中断控制:

void init_irq()    
{
        INTMSK=~(1<<10);
}       

系统启动后每隔一定的时间流水灯的状态改变一次。

中断服务程序如下:

/*file:interrupt.c*/
#define INTOFFSET (*(unsigned long *)0x4A000014)
#define EINTPEND (*(unsigned long *)0x560000a8)
#define GPBCON (*(unsigned long *)0x56000010)
#define GPBDAT (*(unsigned long *)0x56000014)
#define GPBUP (*(unsigned long *)0x56000018)
#define INTPND (*(unsigned long *)0X4A000010)
#define SRCPND (*(unsigned long *)0X4A000000)

unsigned long int count;

void EINT_Handle()
{
        unsigned long oft = INTOFFSET;
        unsigned long val;

        switch (oft)
        {
#define		NUM		4
		case 10:
		{
       			count ++;

			if( (count % NUM) == 0){
				GPBDAT |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
				GPBDAT &= ~(1 << 5);	
			}
			else if((count % NUM) == 1 /* (NUM/4 * 1) */ ){
				GPBDAT |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
				GPBDAT &= ~(1 << 6);
			}
			else if((count % NUM) ==  2 /*(NUM/4 * 2) */ ){
				GPBDAT |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
				GPBDAT &= ~(1 << 7);
			}
			else if((count % NUM) == 3 /*(NUM/4 * 3)*/ ){
				GPBDAT |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
				GPBDAT &= ~(1 << 8);
			}
		}
        }

        //清除中断
        SRCPND = 1 << oft;
        INTPND = 1 << oft;
}


整个工程的文件下载位置:

http://s.yunio.com/zaQL3h


网友相关文章:

http://www.jyguagua.com/?p=417












你可能感兴趣的:(Mini2440 裸机编程-时钟中断)