本文针对s3c2440 CPU,ARM920T core, ARMv4t architecture. 现代的处理器和中断控制器有很大不同,对嵌套中断和其它特性支持的更好,有机会后面再学习。
中断模式下被打断的话,则会将PC拷贝到LR,原来的LR被覆盖了。为了解决这个问题,ARMv4t增加了System模式。嵌套中断的基本套路是,处理器响应中断后,在中断模式只做一些保存工作,然后切换到系统模式处理中断。在系统模式下在打开中断。处理完后,切换回中断模式,同时关闭中断,在返回被打断的环境。
直接看代码把,头晕的很。
start.S
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | .text .global _start _start: b reset b loop b loop b loop b loop b loop b int b loop reset: ldr sp, =4096 bl init bl main b loop int: sub lr,lr,#4 push {r0-r3,r12,lr} @ save unbanked caller-saved register and lr mrs lr, spsr @ copy spsr to lr push {lr} @ save spsr msr cpsr_c, #0xdF @ enter SYSTEM mode (with IRQ / FIQ disabled) push {lr} @ save old interrupt handler's lr bl irq_handler @ interrupt handler pop {lr} @ pop old interrupt handler's lr msr cpsr_c, #0xd2 @ enter IRQ mode (with IRQ / FIQ disabled) pop {lr} @ pop spsr to lr msr spsr_cxsf, lr @ restor spsr ldmfd sp!, {r0-r3,r12,pc}^ @ return loop: b loop |
init.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | // watchdog #define WTCON (*(volatile unsigned long *)0x53000000) #define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned char *)0x56000054) #define GPGCON (*(volatile unsigned long *)0x56000060) #define EINTMSK (*(volatile unsigned long *)0x560000a4) #define EINTPND (*(volatile unsigned long *)0x560000a8) #define EXTINT0 (*(volatile unsigned long *)0x56000088) #define EXTINT1 (*(volatile unsigned long *)0x5600008c) typedef unsigned long uint32_t; typedef unsigned char uint8_t; struct S3C2440_INT_CONT { uint32_t SRCPND; uint32_t INTMOD; uint32_t INTMSK; uint32_t PRIO; uint32_t INTPND; uint32_t INTOFFSET; uint32_t SUBSRCPND; uint32_t INTSUBMSK; }; volatile struct S3C2440_INT_CONT *intcont = ( volatile struct S3C2440_INT_CONT *)0x4a000000; void disable_watchdog() { WTCON = 0; } void init_sdram() { *( volatile unsigned long *)0x48000000 = 0x22011110; // BWSCON *( volatile unsigned long *)0x48000004 = 0x00000700; // BANKCON0 *( volatile unsigned long *)0x48000008 = 0x00000700; // BANKCON1 *( volatile unsigned long *)0x4800000c = 0x00000700; // BANKCON2 *( volatile unsigned long *)0x48000010 = 0x00000700; // BANKCON3 *( volatile unsigned long *)0x48000014 = 0x00000700; // BANKCON4 *( volatile unsigned long *)0x48000018 = 0x00000700; // BANKCON5 *( volatile unsigned long *)0x4800001c = 0x00018005; // BANKCON6 *( volatile unsigned long *)0x48000020 = 0x00018005; // BANKCON7 *( volatile unsigned long *)0x48000024 = 0x008C07A3; // REFRESH *( volatile unsigned long *)0x48000028 = 0x000000B1; // BANKSIZE *( volatile unsigned long *)0x4800002c = 0x00000030; // MRSRB6 *( volatile unsigned long *)0x48000030 = 0x00000030; // MRSRB7 } void init_gpio() { #define set_gpio(r,v,n) r &= ~(3 << 2*n); r |= (v << 2*n) unsigned long r1 = GPFCON; unsigned long r2 = GPGCON; // 4,5,6 LED output mode set_gpio(r1,1,4); set_gpio(r1,1,5); set_gpio(r1,1,6); // 0(EINT0),2(EINT2) Button INT Mode set_gpio(r1,2,0); set_gpio(r1,2,2); // 3(EINT11) BUtton INT Mode set_gpio(r2,2,3); GPFCON = r1; GPGCON = r2; } void init_interrupt_controller() { int m = 0; // enable EINT11 EINTMSK &= ~(1 << 11); EXTINT0 = (m << 2*4) | (m << 0*4); intcont->INTMSK = ~ ((1<<0)|(1<<2)|(1<<5)); EXTINT1 = (m << (11-8)*4); } void init() { disable_watchdog(); init_sdram(); init_gpio(); init_interrupt_controller(); asm volatile ( "msr cpsr_c, #0xd2\n\t" // enter IRQ mode "ldr sp, =0x32000000\n\t" // set IRQ mode stack "msr cpsr_c, #0xdf\n\t" // enter system mode "ldr sp, =0x31000000\n\t" // set system mode stack "msr cpsr_c, #0x53" ); // re-enter svc mode and enable IRQ and FIQ } void main() { int i = 0; while (1) { i++; if (i == 300) { GPFDAT = -1; i = 0; } } } void irq_handler() { unsigned long off = intcont->INTOFFSET; unsigned long led = 0; int i = 0; int j = 30; if (off == 0) led = 6; else if (off == 2) led = 5; else if (off == 5) led = 4; if (intcont->SRCPND & 1) GPFDAT &= ~(1 << 6); if (intcont->SRCPND & (1 << 2)) GPFDAT &= ~(1 << 5); if (intcont->SRCPND & (1 << 5)) GPFDAT &= ~(1 << 4); intcont->INTMSK |= (1 << off); // mask this interrupt intcont->INTPND = 1 << off; // clear INTPND asm volatile ( "msr cpsr_c, #0x1F" ); // Open IRQ if (led != 0) while (1) { i++; if (i == 10000) { GPFDAT = -1; } else if (i == 20000) { GPFDAT = ~(1 << led); j--; i = 0; if (j == 0) break ; } } // clear int if (off == 5) { EINTPND = 1 << 11; } intcont->SRCPND = 1 << off; intcont->INTPND = 1 << off; // clear mask intcont->INTMSK &= ~(1 << off); } |
在中断处理中开中断之前,需要先屏蔽本中断,然后清掉INTPND,否则本中断会持续触发。(想想:如果中断被配置为电平模式,开中断后,你肯定是按一定时间,再快也有个时间,这段时间不断触发,自己打断自己,所以需要屏蔽本中断)。由于本中断被屏蔽,那就没有优先级的概念了,所有其它中断都可以打断它。
思考:
Nested interrupt 和 Re-entrant interrupt 的异同?
参考:
ARMCC: NESTING INTERRUPTS. http://www.keil.com/support/docs/3353.htm