ILD

中断
作者:Herbert Yuan 邮箱:yuanjp89@163.com
发布时间:2017-10-29 站点:Inside Linux Development

CPU核只有一个中断引脚和一个快速中断引脚。还需要一个中断控制器,中断控制器是外围设备,用于实现多中断引脚、优先级控制等。


1 Interrupt Controller

中断控制器属于外围设备,外接各中断引脚,内接CPU核,控制中断的产生和优先级等。


中断处理框图如下:


s3c2440通过下列寄存器来控制中断控制器:


中断源编号,和寄存器中的位对应:

SourceBitSourceBitSourceBitSourceBit
INT_ADC31INT_UART123INT_UART215nBATT_FLT
7
INT_RTC30INT_SPI022INT_TIMER414INT_CAM6
INT_SPI129INT_SDI21INT_TIMER313EINT8_235
INT_UART028INT_DMA320INT_TIMER212EINT4_74
INT_IIC27INT_DMA219INT_TIMER111EINT33
INT_USBH26INT_DMA118INT_TIMER010EINT22
INT_USBD25INT_DMA017INT_WDT_AC979EINT11
INT_NFCON24INT_LCD16INT_TICK8EINT00


子中断源和映射:

SUBSRCBitMAP to SourceSUBSRCBitMAP to Source
INT_AC9714INT_WDT_AC97INT_RXD26INT_UART2
INT_WDT13INT_WDT_AC97INT_ERR15INT_UART1
INT_CAM_P12INT_CAMINT_TXD14INT_UART1
INT_CAM_C11INT_CAMINT_RXD13INT_UART1
INT_ADC_S10INT_ADCINT_ERR02INT_UART0
INT_TC9INT_ADCINT_TXD01INT_UART0
INT_ERR28INT_UART2INT_RXD00INT_UART0
INT_TXD27INT_UART2



SRCPND

Source pending register,32位,每一位表示一个中断源,中断产生时,对应的位由控制器置1,且不会自动置为0,必须由软件手动置0。如果没有置0,将不停的产生该中断。置0的方法是向SRCPND赋值,对应位为1的,SRCPND中的位将被清掉。

RegisterAddressR/WDescriptionReset Value
SRCPND0x4a000000R/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。

RegisterAddressR/WDescriptionReset Value
INTMOD0x4a000004R/W

Interrupt mode register

0 = IRQ mode    1 = FIQ mode

0x0


INTMSK

Interrupt mask register,32位,对应位为1,CPU不服务该中断。被masked的中断,对应的SRCPND仍然会被置为1。

RegisterAddressR/WDescriptionReset Value
INTMSK0x4a000008R/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位,中断优先级控制寄存器。

RegisterAddressR/WDescriptionReset Value
PRIORITY0x4a00000CR/W

IRQ priority control register

0x7F

本篇不打算分析优先级。


INTPND

Interrupt pending register,仲裁器选择一个优先级最高的中断后,将INTPND置为1,然后触发CPU该中断。由于INTPND位于优先级逻辑之后,所以只有1位能置为1,那个中断请求产生IRQ到CPU。中断服务程序中,可以通过该寄存器判断是哪个中断。和SRCPND类似,需要在ISR中手动清除被设置的位,清除方法和SRCPND相同。

RegisterAddressR/WDescriptionReset Value
INTPND0x4a000010R/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时自动清除。

RegisterAddressR/WDescriptionReset Value
INTOFFSET0x4a000014R/W

Indicate the interrupt request status.

0x0


SUBSRCPND

Sub source pending register,14位,表示子中断,多个子中断对应到主中断。

RegisterAddressR/WDescriptionReset Value
SUBSRCPND0x4a000018R/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类似。

RegisterAddressR/WDescriptionReset Value
INTSUBMSK0x4a00001CR/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。否则高处被清除后会被重新触发置位。


2 ARM920T Core

程序状态寄存器(CPSR)的F位和I位控制是否接收FIQ和IRQ,如果设置为1,则不接收对应类型的中断。


发生异常时,处理器先执行下列操作,再跳转到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

下一篇学习


3 实战

我手头上的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 loop
reset:
    ldr sp, =4096
    bl init
    bl main
    b loop
int:
   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.


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.