在分辨是哪个中断的时候非常有用,6410中64组中断,就可以使用向量中断来进行判断。
以前31组中断为例,假如Vectored interrupt 1发生了中断,它就会将Vectored interrup 1的地址告诉VectorAddr寄存器。如下图所示:
6410中有两个中断控制器,它们分别为VIC0,VIC1,假如VIC0的第0组中断源发生了中断,硬件上,6410就会把VIC0VECTADDR0赋值给VIC0ADDRESS,其中VIC0VECTADDR0对应于中断处理函数的地址。当某一组中断发生后,CPU就会自动分辨是哪个中断,从而进行处理相应的处理函数。
注意:向量中断只能分辨是哪一组,具体是其中一组的哪个中断,还需要继续进行细分。
程序代码:
.globl _start _start: /* 0 地址 */ b reset /* 复位时,cpu跳到0地址 */ ldr pc, =undefined_instruction /* cpu遇到不能识别的指令时 */ ldr pc, _vector_swi /* 当执行swi指令时, 进入swi模 式 */ b halt @ldr pc, _prefetch_abort /* 预取中止异常 */ b halt @ldr pc, _data_abort /* 数据访问异常 */ b halt @ldr pc, _not_used /* 没用到 */ ldr pc, _irq /* 0x18 中断异常 */ b halt @ldr pc, _fiq /* 快中断异常 */ _irq : .word vector_irq /*向量中断处理*/ _vector_swi: .word vector_swi vector_swi: /* 1. 保存现场 */ ldr sp, =0x56000000 stmdb sp!, {r0-r12, lr} /* lr就是swi的下一条指令地址 */ /* 2. 处理异常 */ mrs r0, cpsr ldr r1, =swi_str bl print_cpsr /* 3. 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^表示把spsr恢复到cpsr */ swi_str: .word 0x00697773 /* swi */ undefined_instruction: /* 1. 保存现场 */ ldr sp, =0x55000000 stmdb sp!, {r0-r12, lr} /* 2. 处理异常 */ mrs r0, cpsr ldr r1, =und_str bl print_cpsr /* 3. 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^表示把spsr恢复到cpsr */ und_str: .word 0x00646e75 /* und */ usr_str: .word 0x00727375 /* usr */ vector_irq: /* 1. 保存现场 */ ldr sp, =0x54000000 sub lr, lr, #4 stmdb sp!, {r0-r12, lr} /* lr就是swi的下一条指令地址 */ /* 2. 处理异常 */ bl do_irq /* 3. 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^表示把spsr恢复到cpsr */ reset: /* 硬件相关的设置 */ /* Peri port setup */ ldr r0, =0x70000000 orr r0, r0, #0x13 mcr p15,0,r0,c15,c2,4 @ 256M(0x70000000-0x7fffffff) /* 关看门狗 */ /* 往WTCON(0x7E004000)写0 */ ldr r0, =0x7E004000 mov r1, #0 str r1, [r0] /* 设置栈 */ ldr sp, =8*1024 /* 设置时钟 */ bl clock_init bl ddr_init bl init_uart /* 把程序的代码段、数据段复制到它的链接地址去 */ adr r0, _start /* 获得_start指令当前所在的地址 : 0*/ ldr r1, =_start /* _start的链接地址 0x51000000 */ ldr r2, =bss_start /* bss段的起始链接地址 */ sub r2, r2, r1 cmp r0,r1 beq clean_bss bl copy2ddr cmp r0, #0 bne halt /* 清BSS */ /* 把BSS段对应的内存清零 */ clean_bss: ldr r0, =bss_start ldr r1, =bss_end mov r3, #0 cmp r0, r1 ldreq pc, =on_ddr clean_loop: str r3, [r0], #4 cmp r0, r1 bne clean_loop ldr pc, =on_ddr on_ddr: bl irq_init mrs r0, cpsr bic r0,r0,#0x9f /* 清cpsr的I位,M4~M0 */ orr r0,r0,#0x10 msr cpsr,r0 /* 进入user mode */ ldr sp, =0x57000000 ldr r1, =usr_str bl print_cpsr swi 0 /* * cpu进入svc模式 * 把之前的cpsr保存到spsr_svc * 切换到r13_svc, r14_svc * 把swi的下一条指令存到r14(lr)_svc * 跳到地址8 */ bl hello undef: .word 0xff000000 /* * cpu进入Undefined模式 * 把之前的cpsr保存到spsr_und * 切换到r13_und, r14_und * 把下一条指令存到r14(lr)_und * 跳到地址4 */ swi_ret: bl main halt: b halt
#define GPNCON (*((volatile unsigned long *)0x7F008830)) #define GPNDAT (*((volatile unsigned long *)0x7F008834)) #define EINT0CON0 (*((volatile unsigned long *)0x7F008900)) #define EINT0MASK (*((volatile unsigned long *)0x7F008920)) #define EINT0PEND (*((volatile unsigned long *)0x7F008924)) #define PRIORITY (*((volatile unsigned long *)0x7F008280)) #define SERVICE (*((volatile unsigned long *)0x7F008284)) #define SERVICEPEND (*((volatile unsigned long *)0x7F008288)) #define VIC0IRQSTATUS (*((volatile unsigned long *)0x71200000)) #define VIC0FIQSTATUS (*((volatile unsigned long *)0x71200004)) #define VIC0RAWINTR (*((volatile unsigned long *)0x71200008)) #define VIC0INTSELECT (*((volatile unsigned long *)0x7120000c)) #define VIC0INTENABLE (*((volatile unsigned long *)0x71200010)) #define VIC0INTENCLEAR (*((volatile unsigned long *)0x71200014)) #define VIC0PROTECTION (*((volatile unsigned long *)0x71200020)) #define VIC0SWPRIORITYMASK (*((volatile unsigned long *)0x71200024)) #define VIC0PRIORITYDAISY (*((volatile unsigned long *)0x71200028)) #define VIC0VECTADDR0 (*((volatile unsigned long *)0x71200100)) #define VIC0VECTADDR1 (*((volatile unsigned long *)0x71200104)) #define VIC0ADDRESS (*((volatile unsigned long *)0x71200f00)) void eint0_3_irq(void)//对第0组中断的处理 { int i; printf("eint0_3_irq\n\r"); /* K1~K4 */ for (i = 0; i < 4; i ++) { if (EINT0PEND & (1<<i)) { if (GPNDAT & (1<<i)) { printf("K%d released\n\r", i+1); } else { printf("K%d pressed\n\r", i+1); } } } } void eint4_11_irq(void)//对第一组中断的处理 { int i; printf("eint4_11_irq\n\r"); /* K5~K6 */ for (i = 4; i < 6; i ++) { if (EINT0PEND & (1<<i)) { if (GPNDAT & (1<<i)) { printf("K%d released\n\r", i+1); } else { printf("K%d pressed\n\r", i+1); } } } } void irq_init(void)//向量中断初始化 { /* 配置GPIO引脚为中断引脚 */ /* GPN0~5 设为中断引脚 */ GPNCON &= ~(0xfff); GPNCON |= 0xaaa; /* 设置中断触发方式为: 双边沿触发 */ EINT0CON0 &= ~(0xfff); EINT0CON0 |= 0x777; /* 使能中断 */ EINT0MASK &= ~(0x3f); /* 在中断控制器里使能这些中断 */ VIC0INTENABLE |= (0x3); /* bit0: eint0~3, bit1: eint4~11 */ VIC0VECTADDR0 = eint0_3_irq; //直接向向量中断处理函数赋值给VIC0VECTADDR0,硬件上VIC0VECTADDR0会自动赋值给VIC0ADDRESS VIC0VECTADDR1 = eint4_11_irq; /* 设置优先级 */ } void do_irq(void) { int i = 0; void (*the_isr)(void);//函数指针 the_isr = VIC0ADDRESS;//将VIC0ADDRESS赋值给函数指针,就是将相关的中断处理函数赋值给它 /* 2.1 分辨是哪个中断 */ /* 2.2 调用它的处理函数 */ /* 2.3 清中断 */ the_isr();//处理中断 EINT0PEND = 0x3f; /* 清中断 */ VIC0ADDRESS = 0; }