s3c2440 ARM9 裸机驱动第三篇—定时器

来源:互联网 发布:淘宝在哪里交保证金 编辑:程序博客网 时间:2024/05/26 12:06

定时器这篇是比较艰辛的,过程中出现了很多小问题,有些解决了,有些还是没有完全弄明白,这些问题主要集中在汇编部分的程序。

此部分主要是通过定时器中断的方式实现LED灯的闪烁。

一、硬件部分:

1.LED部分:参见之前的文章

2.定时器:

定时器的频率:Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
{prescaler value} = 0~255
{divider value} = 2, 4, 8, 16

其中PCLK如果配置了MPLL就是50M,prescaler value配置TCFG0得到,divider value配置TCFG1得到。

TCFG0和TCFG1这两个寄存器为配置寄存器,涉及配置定时器的频率和PWM死区时间和通道,DMA请求通道。

TCON为控制寄存器,这里涉及到定时器的工作模式,和使能和失能,值得注意的是NOTE的那句话,The bit has to be cleared at next writing.

3.中断寄存器:

SRCPND中断源挂起寄存器,产生中断则对应位会置1,在中断服务函数中清除此位

INTMOD中断模式寄存器,用于设置对应中断源事件的处理方式,是FIQ(快中断)还是IRQ

INTMSK中断屏蔽寄存器,屏蔽或者打开对应的中断。默认屏蔽。

PRIORITY中断优先级寄存器

INTRND中断挂起寄存器,在中断服务函数中清除SRCPND寄存器后清除此寄存器。

INTOFFSET中断偏移寄存器,在IRQ模式下起作用,根据其值,指示中断源。

二、软件部分:

1.start.s启动的汇编代码:

此部分直接改的韦东山老师的代码

@******************************************************************************@ File:head.S@ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数@******************************************************************************          .extern     main.text .global _start _start:@******************************************************************************       @ 异常向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用@******************************************************************************           b   Reset@ 0x04: 未定义指令中止模式的向量地址HandleUndef:    b   HandleUndef  @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式HandleSWI:    b   HandleSWI@ 0x0c: 指令预取终止导致的异常的向量地址HandlePrefetchAbort:    b   HandlePrefetchAbort@ 0x10: 数据访问终止导致的异常的向量地址HandleDataAbort:    b   HandleDataAbort@ 0x14: 保留HandleNotUsed:    b   HandleNotUsed@ 0x18: 中断模式的向量地址    b   HandleIRQ@ 0x1c: 快中断模式的向量地址HandleFIQ:    b   HandleFIQ Reset:                      ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈    bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启@ldr pc, =disable_watch_dog    msr cpsr_c, #0xd2       @ 进入中断模式    ldr sp, =3072           @ 设置中断模式栈指针    msr cpsr_c, #0xd3       @ 进入管理模式    ldr sp, =4096           @ 设置管理模式栈指针,                            @ 其实复位之后,CPU就处于管理模式,                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略@    bl  init_led            @ 初始化LED的GPIO管脚@    bl  init_irq            @ 调用中断初始化函数,在init.c中    msr cpsr_c, #0x53       @ 设置I-bit=0,开IRQ中断        ldr lr, =halt_loop      @ 设置返回地址    ldr pc, =main           @ 调用main函数halt_loop:    b   halt_loopHandleIRQ:    sub lr, lr, #4                  @ 计算返回地址    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器                                    @ 注意,此时的sp是中断模式的sp                                    @ 初始值是上面设置的3072        ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址      ldr pc, =timer0_Handle          @ 调用中断服务函数,在timer.c中int_return:    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr


2.timer.c文件

#defineGPFCON(*(volatile unsigned long *)0x56000050)#defineGPFDAT(*(volatile unsigned long *)0x56000054)#define  WTCON   (*(volatile unsigned long *)0x53000000)//CLK#defineMPLLCON(*(volatile unsigned long *)0x4C000004)#defineCLKDIVN(*(volatile unsigned long *)0x4C000014)#defineCAMDIVN(*(volatile unsigned long *)0x4C000018)//TIME#defineTCFGO(*(volatile unsigned long *)0x51000000)//预分频#defineTCFG1(*(volatile unsigned long *)0x51000004)//分频#defineTCON(*(volatile unsigned long *)0x51000008)//控制寄存器#defineTCNTB0(*(volatile unsigned long *)0x5100000C)//计数寄存器//IRQ#defineSRCPND(*(volatile unsigned long *)0X4A000000)//中断源挂起#defineINTMOD(*(volatile unsigned long *)0X4A000004)//中断模式#defineINTMSK(*(volatile unsigned long *)0X4A000008)//中断屏蔽#defineINTPND(*(volatile unsigned long *)0X4A000010)//中断挂起#definePRITY(*(volatile unsigned long *)0X4A00000C)//中断优先级void clk_init(void);void timer0_init(unsigned char prescalers,unsigned int count);int main(int argv, char *argc){int i;GPFCON&=0x00ff;GPFCON|=0x5500;GPFDAT=0x0;clk_init();timer0_init(100,40000);GPFDAT=0xf0;while(1){}return 0;}void clk_init(void){//LOCKTIME默认值CLKDIVN=0x03;//MCLK:HCLK:PCLK=1:2:4//CAMDIVN默认值__asm__(//如果 HDIVN 不为 0,CPU 总线模式应该使用以下指令使其从"MRC p15, 0, r0, c1, c0, 0\n"//快总线模式改变为异步总线模式(S3C2440不支持同步总线模式)。"ORR r1, r1, #0xc0000000\n""MCR p15, 0, r0, c1, c0, 0\n");MPLLCON=((0x5c<<12)|(0x01<<4)|0x02);//MCLK=200M//MPLLCON=(0x5c<<12)|(1<<4)|2;}/**prescalers=0-255*中断频率=50/16/prescalers/count*/void timer0_init(unsigned char prescalers,unsigned int count){TCFGO=prescalers;//50M/prescalersTCFG1=0x3;//50M/prescalers/16TCNTB0=count;//计数值TCON |=1<<1;TCON |=1<<3;//自动重装载TCON &=~(1<<1);/*配置中断*/SRCPND |= (1<<10);//清除源挂起INTPND |= (1<<10);//清除中断挂起INTMOD &= ~(1<<10);//设置中断模式为IRQ模式INTMSK &= ~(1<<10);//使能定时器中断/*启动定时器*/TCON |= (1<<0);//启动定时器0}void timer0_Handle(){SRCPND |= (1<<10);//清除源挂起INTPND |= (1<<10);//清除中断挂起GPFDAT= ~GPFDAT;timer0_irq = 1;}void disable_watch_dog(void){    WTCON = 0;  // 关闭WATCHDOG很简单,往这个寄存器写0即可}
3.Makefile
CC=arm-linux-gccLD=arm-linux-ldtimer.bin:timer.o start.o$(LD) -Ttext 0x0000000 -g  start.o timer.o -o timer_elfarm-linux-objcopy -O binary -S timer_elf timer.binarm-linux-objdump -D -m arm  timer_elf > timer.distimer.o:timer.c$(CC)  -c -g -o timer.o timer.cstart.o:start.s$(CC)  -c -g -o start.o start.sclean:rm -f timer.bin led_elf timer.dis start.o