CPU核只有一个中断引脚和一个快速中断引脚。还需要一个中断控制器,中断控制器是外围设备,用于实现多中断引脚、优先级控制等。
中断控制器属于外围设备,外接各中断引脚,内接CPU核,控制中断的产生和优先级等。
中断处理框图如下:
s3c2440通过下列寄存器来控制中断控制器:
中断源编号,和寄存器中的位对应:
| Source | Bit | Source | Bit | Source | Bit | Source | Bit | 
| INT_ADC | 31 | INT_UART1 | 23 | INT_UART2 | 15 | nBATT_FLT | 7 | 
| INT_RTC | 30 | INT_SPI0 | 22 | INT_TIMER4 | 14 | INT_CAM | 6 | 
| INT_SPI1 | 29 | INT_SDI | 21 | INT_TIMER3 | 13 | EINT8_23 | 5 | 
| INT_UART0 | 28 | INT_DMA3 | 20 | INT_TIMER2 | 12 | EINT4_7 | 4 | 
| INT_IIC | 27 | INT_DMA2 | 19 | INT_TIMER1 | 11 | EINT3 | 3 | 
| INT_USBH | 26 | INT_DMA1 | 18 | INT_TIMER0 | 10 | EINT2 | 2 | 
| INT_USBD | 25 | INT_DMA0 | 17 | INT_WDT_AC97 | 9 | EINT1 | 1 | 
| INT_NFCON | 24 | INT_LCD | 16 | INT_TICK | 8 | EINT0 | 0 | 
子中断源和映射:
| SUBSRC | Bit | MAP to Source | SUBSRC | Bit | MAP to Source | 
| INT_AC97 | 14 | INT_WDT_AC97 | INT_RXD2 | 6 | INT_UART2 | 
| INT_WDT | 13 | INT_WDT_AC97 | INT_ERR1 | 5 | INT_UART1 | 
| INT_CAM_P | 12 | INT_CAM | INT_TXD1 | 4 | INT_UART1 | 
| INT_CAM_C | 11 | INT_CAM | INT_RXD1 | 3 | INT_UART1 | 
| INT_ADC_S | 10 | INT_ADC | INT_ERR0 | 2 | INT_UART0 | 
| INT_TC | 9 | INT_ADC | INT_TXD0 | 1 | INT_UART0 | 
| INT_ERR2 | 8 | INT_UART2 | INT_RXD0 | 0 | INT_UART0 | 
| INT_TXD2 | 7 | INT_UART2 | 
SRCPND
Source pending register,32位,每一位表示一个中断源,中断产生时,对应的位由控制器置1,且不会自动置为0,必须由软件手动置0。如果没有置0,将不停的产生该中断。置0的方法是向SRCPND赋值,对应位为1的,SRCPND中的位将被清掉。
| Register | Address | R/W | Description | Reset Value | 
| SRCPND | 0x4a000000 | R/W | Indicate the interrupt request status. 0 = the interrupt has bot been requested. 1 = the interrupt source has asserted the interrupt request  | 0x0 | 
INTMOD
Interrupt mode register,32位,表示产生FIQ还是IRQ。1表示FIQ,0表示IRQ。只能有一个FIQ,所以只能有1位为1。
| Register | Address | R/W | Description | Reset Value | 
| INTMOD | 0x4a000004 | R/W | Interrupt mode register 0 = IRQ mode 1 = FIQ mode  | 0x0 | 
INTMSK
Interrupt mask register,32位,对应位为1,CPU不服务该中断。被masked的中断,对应的SRCPND仍然会被置为1。
| Register | Address | R/W | Description | Reset Value | 
| INTMSK | 0x4a000008 | R/W | Determine which interrupt source is masked. The masked interrupt source will not be serviced 0 = Interrupt service is available 1 = Interrupt service is masked  | 0xFFFFFFFF | 
PRIORITY
Priority register,20位,中断优先级控制寄存器。
| Register | Address | R/W | Description | Reset Value | 
| PRIORITY | 0x4a00000C | R/W | IRQ priority control register  | 0x7F | 
本篇不打算分析优先级。
INTPND
Interrupt pending register,仲裁器选择一个优先级最高的中断后,将INTPND置为1,然后触发CPU该中断。由于INTPND位于优先级逻辑之后,所以只有1位能置为1,那个中断请求产生IRQ到CPU。中断服务程序中,可以通过该寄存器判断是哪个中断。和SRCPND类似,需要在ISR中手动清除被设置的位,清除方法和SRCPND相同。
| Register | Address | R/W | Description | Reset Value | 
| INTPND | 0x4a000010 | R/W | Indicate the interrupt request status. 0 = the interrupt has bot been requested. 1 = the interrupt source has asserted the interrupt request  | 0x0 | 
快速中断模式,对应的位不会打开,INTPND只服务IRQ模式。
INTOFFSET
Interrupt offset register,这个就是INTPND的位的整数,如16,便于获取中断的编号。清除SRCPND和INTPND时自动清除。
| Register | Address | R/W | Description | Reset Value | 
| INTOFFSET | 0x4a000014 | R/W | Indicate the interrupt request status.  | 0x0 | 
SUBSRCPND
Sub source pending register,14位,表示子中断,多个子中断对应到主中断。
| Register | Address | R/W | Description | Reset Value | 
| SUBSRCPND | 0x4a000018 | R/W | Indicate the interrupt request status. 0 = the interrupt has bot been requested. 1 = the interrupt source has asserted the interrupt request  | 0x0 | 
和SRCPND需要在ISR中手动清除,清除方法相同。
INTSUBMSK
Interrupt sub mask register,用来屏蔽子中断。和SUBMSK类似。
| Register | Address | R/W | Description | Reset Value | 
| INTSUBMSK | 0x4a00001C | R/W | Determine which interrupt source is masked. The masked interrupt source will not be serviced 0 = Interrupt service is available 1 = Interrupt service is masked  | 0xFFFF | 
注意,中断的清除顺序必须从前往后清除:SUBSRCPND->SRCPND->INTPND。否则高处被清除后会被重新触发置位。
程序状态寄存器(CPSR)的F位和I位控制是否接收FIQ和IRQ,如果设置为1,则不接收对应类型的中断。
发生异常时,处理器先执行下列操作,再跳转到vector table,这些操作由硬件自动完成:
根据接收到的中断类型,切换到对应的处理器模式。
将之前模式的CPSR拷贝到新模式的SPSR。
将之前模式的PC保存到新模式的LR。
禁止中断(设置CPSR的对应位),或者IRQ,或者IRQ与FIQ。
跳转到vector table对应的入口。
异常中断发生时,跳转到异常入口处执行,同时通常也意味着模式切换:
中断名称  | 跳转入口地址  | 含义  | 
复位  | 0x0  | 复位引脚有效时,系统产生复位异常中断。  | 
未定义指令  | 0x4  | |
软中断 SWI  | 0x8  | |
指令预取中止 prefech abort  | 0x0c  | 预取的指令地址不存在,或不允许访问。  | 
数据访问中止 Data abort  | 0x10  | |
外部中断请求  | 0x18  | |
快速中断请求  | 0x1c  | 
Link Register Offset
中断发生时,处理器将当前PC赋值给中断模式的LR,而PC是当前执行指令地址加8,从中断返回时,我们要执行被中断的下一条指令,所以,应该跳转到LR-4。
Non-nested interrupt handling
这是最简单的,只要在中断返回前关闭中断即可。
Nested interrupt handling
下一篇学习
我手头上的JZ2440开发板,按键和LED引脚映射如下:
实现按一个键就开始闪一段时间对应的灯,之后进入熄灭状态。
代码如下:
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  |     .text    .global _start_start:    b reset    b loop    b loop    b loop    b loop    b loop    b int    b loopreset:    ldr sp, =4096    bl init    bl main    b loopint:   sub lr, lr, #4   stmdb sp!, {r0-r12,lr}   bl irq_handler   ldmia sp!, {r0-r12,pc}^loop:    b loop | 
如上,vector table实现了reset和中断的跳转。
reset处理:设置栈,跳转到init()执行相关初始化。跳转到main()。
int处理:中断处理返回后,应该跳转到下一条指令执行,所以先将lr减4,然后保存栈,这里stmdb等价于stmfd,保存栈后就可以跳转到中断处理函数执行,返回后,恢复栈。
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  | // 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)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_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(){    // enable EINT11   EINTMSK &= ~(1 << 11);    intcont->INTMSK = ~ ((1<<0)|(1<<2)|(1<<5));}void init(){    disable_watchdog();    init_gpio();    init_interrupt_controller();          asm volatile (        "msr cpsr_c, #0xd2\n\t"     // enter IRQ mode        "ldr sp, =3072\n\t"         // set IRQ 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 (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;} | 
init()关看门狗,设置GPIO模式,LED设置为output,按键设置为中断模式。设置中断,主要是清除对应的掩码。最后通过修改CPSR进入中断模式,设置中断模式的栈,然后在返回svc模式,同时开启中断,整个初始化完成。
main()死循环,关闭所有灯。
irq_handler()获取是哪个中断,闪对应的灯,然后清除中断源。
1 可嵌套中断,只有高优先级的中断才可以打断低优先级的中断。因为中断处理时,不会清除中断控制器中的源,中断控制只会冒泡高优先级的中断。
2 可嵌套中断应该在非中断模式执行,因为如果在中断模式执行,而中断处理代码有函数调用,那么LR被当前PC覆盖,导致之前的中断处理代码无法从函数返回。通常使用system模式执行可嵌套中断。
3 中断由于优先级低或者中断暂时被屏蔽时,中断控制器仍然会保持对应的中断,当中断在后续仍然可以被处理。这样保证中断不会丢失。如在非嵌套中断,一个中断在处理,按了另外一个中断键, 那么该中断处理完毕后,会执行新的中断。
下一节将学习可嵌套中断。
[1] Ahmed Fathy Mohammed Abdelrazek. Exception and Interrupt Handling in ARM.
http://www.iti.uni-stuttgart.de/~radetzki/Seminar06/08_report.pdf
[2] Chapter 5, Handling Processor Exceptions. ARM Developer Suite v1.2. Developer Guide.
[3] A2.6 Exceptions. ARM ARM
[4] Chapter 9. Exception and Interrupt Handling. ARM sytem developer's guide, Designing and optimizing system software.