使用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 }
刚设置好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