s3c2440中断控制器(九)


翻到芯片手册的中断那一张,首先是一张图。


     S3C2440 的中断分为片内中断和片外终端,分别对应着SRCPND寄存器左边的两条路。

     我们先看简单一条路,外部中断,例如GPIO触发的中断。

     首先,当GPIO触发中断后,我们使得 SRCPND相应的为置1,如果MASK屏蔽位或者被设置为FIQ(mode位置1) 则进入到中断优先级判定
     通过PROITRITY的寄存器设置,选择出一个最高优先级的中断 将 INTPND寄存器中的相应位 置1 (同一时间只有一位被置1),
     然后CPU进入中断模式处理它。(ISR例程)

     内部的中断,显而易见多了一层的筛选,至于为什么要这样做,是为了归类罢了。比如串口有3中类型中断算是一个串口的中断。

 1.在ISR中如何知道是什么东西触发了中断?
          很清楚当然是看INTPND寄存器【也可以读INTOFFSET寄存器】
          当然了对于内部的中断 我们还应该参考SUBSRCPND 来确认  到底是串口1 还是串口2 的中断咯。
          如果是FIQ 很简单因为只有一个中断能被设置为FIQ,看MODE寄存器就行了。

     
 2.中断发生后如何找到处理函数?
          异常向量表。7种模式对应的0x0000 0000 开始 每4个字节为一个表项。 每一项都是一条跳转指令,因为B跳转不返回,也不应该返回,
          所以进入异常之前需要手动备份PC指针,处理完后再跳转回去,或者其他处理。

 3.处理完中断后如何恢复现场?
          这里我们需要先讲一下在中断前的准备工作。
          
          ARM体系结构由7中工作方式。
          用户模式usr、FIQ模式   IRQ模式 管理模式svc   数据访问终止模式abt 系统模式sys 未定义模式und
          大多程序运行在用户模式, 其他模式都是特权模式。
          对应的不同模式有不同寄存器,对于模式的切换我们需要保护的就是各个寄存器的状态。

         

                 R15 是PC寄存器,记录当前指令的位置,(忽略三级流水)。

                 R14 称为链接寄存器,当执行子程序调用指令(BL)时,R14可得到R15(程序计数器PC)的备份.发生异常或者中断时候相应的R14_XXX记录R15的值 
                【情景:发生中断,R14_irq =r15+4/8 ,中断处理结束,R15 =R14_irq】

                 灰色的寄存器当模式切换之前无需手动备份。
                R13 为相应模式下的栈

                CPSR 当前程序状态寄存器,用于标识当前各种状态,和工作模式
                其中中断相关的两个位 I位和F位,表示是否开启IRQ和FIQ。

               SPSR 是程序状态保存寄存器,当切换进入其他模式前,先将CPSR保存切换后模式的SPSR中。返回时将SPSR的值赋值给CPSR即可。
              【情景:发生中断,SPSR _irq =CPSR ,中断处理结束,CPSR = SPSR_irq】

           所以,中断处理整个流程应该是:
           1.设置好栈
           2.准备好中断向量表
            3.编写好ISR
           4.清楚中断【不清楚会让CPU以为又发生了一次】
               可以在调用ISR前清楚,也可以调用之后清楚,取决于这个中断是否可丢弃。 就是说这个中断是否可嵌套。
               清楚需要先清楚源  subsrcpnd  srcpnd intpnd [写1就会清零]

我们需要验证一下之前的理论是否正确,我们重新改写一下之前用控制LED灯的程序了。

1.中断向量表
2.设置好栈
3.配置好和中断相关的寄存器
4.配置GPIO管脚
5.编写ISR程序
6.死循环等待

编写的过程中,发现在GPIO这一章中,关于外部中断还存在一个 EINTMASK 和EINTPEND ,当然这个作用不明而喻。
不过EINTMASK对于4-23号的外部中断起作用,0-3在之前图上画的MASK限制用了,翻datesheet就能看到。
我们简单看一下INTSRC就能懂,这个寄存器4-7和8-23只有一个总屏蔽位和总pend的情况,所以要额外增加其他寄存器了。
简单的说就是在这个寄存器我们还是傻傻的分不清楚到底是哪一个中断产生,我们也不能精确的屏蔽比如EINT6或者eint11


这里开发板一共只有3个LED灯,4个按键,我的思路是3个按键分别控制3个灯,另外一个按键可以同时控制3个灯。
这里我们用到中断的上升沿和下降沿,检测到下降沿就是按键按下,否则按键抬起。
按下的时候点亮LED,抬起的时候熄灭。 对于0是点亮还是1是点亮,别偷懒去看原理图一瞧就明白。

head.S
.text
.global _start

_start:

    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			@ 设置堆栈指针
    bl  disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启
    
    @接下去设置中断模式的堆栈 
    msr cpsr_c, #0xd2			@跳转到中断模式
    ldr sp, =3072
    msr cpsr_c, #0xd3                   @回到管理模式,一上电是处于管理模式

    @初始化IRQ先关寄存器
    bl init_interrupt_register 
    @配置GPIO
    bl init_led

    @开中断
    msr cpsr_c, #0x5f       		@ 设置I-bit=0,开IRQ中断
halt_loop:
    b   halt_loop


HandleIRQ:
    sub lr, lr, #4                  @ 计算返回地址
    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器
                                    @ 注意,此时的sp是中断模式的sp
                                    @ 初始值是上面设置的3072
    
    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址  
    ldr pc, =irq_handle             @ 调用中断服务函数

int_return:
    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr
    

init.c
/* WATCHDOG寄存器 */
#define P_WTCON           ((volatile unsigned long *)0x53000000)
#define P_SRCPND           ((volatile unsigned long *)0x4A000000)
#define P_INTMASK          ((volatile unsigned long *)0x4A000008)
#define P_PRIORITY         ((volatile unsigned long *)0x4A00000C)
#define P_INTPND           ((volatile unsigned long *)0x4A000010)
#define P_INTPND           ((volatile unsigned long *)0x4A000010)
#define P_INTOFFSET           ((volatile unsigned long *)0x4A000014)
#define P_EINTMASK           ((volatile unsigned long *)0x560000A4)
#define P_EINTPEND           ((volatile unsigned long *)0x560000A8)

#define P_GPFCON      ((volatile unsigned long *)0x56000050)
#define P_GPFDAT      ((volatile unsigned long *)0x56000054)
#define P_GPGCON      ((volatile unsigned long *)0x56000060)
#define P_GPGDAT      ((volatile unsigned long *)0x56000064)

#define P_EXINT0      ((volatile unsigned long *)0x56000088)
#define P_EXINT1      ((volatile unsigned long *)0x5600008C)
#define P_EXINT2      ((volatile unsigned long *)0x56000090)

#include "../../pub/include/bit.h"


/*
 * 关闭WATCHDOG,否则CPU会不断重启
 */
void disable_watch_dog(void)
{
        *P_WTCON = 0;
}


void irq_handle(){
    unsigned long oft = *P_INTOFFSET;
    int  eintpend_indx = -1;
    int  data_index =-1;
    switch(oft){

	case 0:
		//eint0
		
	if(GetBit(*P_GPFDAT,0)==0){
		ClearBit(*P_GPFDAT,4);//LED1
	}else{
		 SetBit(*P_GPFDAT,4);
	}
	break;

	case 2:
		//eint2

	 if(GetBit(*P_GPFDAT,2)==0){
                ClearBit(*P_GPFDAT,5);//LED1
        }else{
                 SetBit(*P_GPFDAT,5);
        }

	break;

	case 5:
		//eint11 or19

	if(GetBit(*P_EINTPEND,11)==1){
		eintpend_indx=11;
		data_index = 3;
	}else{
		eintpend_indx=19;
		data_index = 11;
	}
	if(GetBit(*P_GPGDAT,data_index)==0){
		if(data_index ==11)
                	ClearBit(*P_GPFDAT,6);//LED1
		else
			setNbit(P_GPFDAT,3,4,0x0);			
		
        }else{
             if(data_index ==11)
                        SetBit(*P_GPFDAT,6);//LED1
                else
                        setNbit(P_GPFDAT,3,4,0x7); 
        }

	break;
    }	
	if(oft ==5)
		SetBit(*P_EINTPEND,5);		

	 SetBit(*P_SRCPND,oft);
	 SetBit(*P_INTPND,oft);
	
}

void init_interrupt_register(){
//1.清楚屏蔽位
	ClearBit(*P_INTMASK,0);
	ClearBit(*P_INTMASK,2);
	ClearBit(*P_INTMASK,5);
//2.设置优先级
	//EINT2>EINT0
//	setNbit(P_PRIORITY,2,7,0x2);
//3.清楚外部中断屏蔽位
	ClearBit(*P_EINTMASK,19);
	ClearBit(*P_EINTMASK,11);
//4.GPIO按键设置为中断
	setNbit(P_GPFCON,2,0,0x2);	//中断
	setNbit(P_GPFCON,2,4,0x2);	//中断
	setNbit(P_GPGCON,2,22,0x2);	//中断
	setNbit(P_GPGCON,2,6,0x2);	//中断
//5.配置为both
	setNbit(P_EXINT0,3,0,0x7);      
        setNbit(P_EXINT0,3,8,0x7);      
        setNbit(P_EXINT1,3,12,0x7);      
        setNbit(P_EXINT2,3,12,0x7);      


}


void init_led(){

	setNbit(P_GPFDAT,3,4,0x7);//111
	setNbit(P_GPFCON,2,4*2,0x1);
        setNbit(P_GPFCON,2,5*2,0x1);
        setNbit(P_GPFCON,2,6*2,0x1);
	//setNbit(P_GPFDAT,3,4,0x7);//111


}

Makefile
#objs = head.o init.o
SRCS = head.c init.c  ../../pub/bit.c

objs += $(SRCS:.c=.o)
#$(SRCS:.c=.o)----这个类似于模式替换$(pssubst  %.c,%.o,$(OBJS)),都是把变量的里面所有.c都的值变成.o

int.bin : $(objs)
	arm-linux-ld -Ttext 0x00000000 -o int.elf $^
	arm-linux-objcopy -O binary -S int.elf $@
	arm-linux-objdump -D -m arm int.elf > int.dis

#$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件




%.o:%.c
	arm-linux-gcc -Wall -O2 -c -o $@ $<

%.o:%.S
	arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
	rm -f *.dis *.bin *.elf *.o ../../pub/*.o






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值