Arm 中断机制
中断

什么是中断
中断就是在运行间突然打断当前程序
处理结束后,继续处理
好处
提升实时性
实时性
中断是随机的,打断后要及时处理
处理结束后,继续处理打断的程序
引脚
信号通过引脚进入
中断信号如何进入CPU里面
形象的表示:过五关斩六将
- 配置串口的模式
GPIO控制 - 使能
- 分发器
分发使能 GIC - 接口
接口使能 - 优先级判断
过五关斩六将
cpu中的操作
- 打断当前程序,保护现场
- 执行irq
- 处理后回来继续执行,恢复现场
如何识别不同id
这里有个中断号的概念
中断标志
- 记录中断标志
- 清理中断标志
主板

按下按钮发送中断
步骤
- 看电路图 看链接的管脚
- 看芯片手册 找到哪个寄存器控制它的
- 编程控制
按键

寻找K2(UART_RING)在cpu的哪个引脚

找到XEINT9/KP_COL1/ALV_DBG5/GPX1_1
找到控制引脚的章节

寻找:GPX1_1
配置中断模式只需要 GPX1CON就可以
6.2.3.198 GPX1CON
Base Address: 0x1100_0000
Address = Base Address + 0x0C20, Reset Value = 0x0000_0000
我们的是[7:4]
设置中断模式对应的是
0xF = WAKEUP_INT1[1]
设置引脚中断模式
/*
实现k2中断
1. 看电路图找到CPU对应的控制管脚 GPX1_1
2. 看芯片手册,找到对应寄存器
a. 配置管脚为interrupt模式
b. 功能块设置
3. 编程
*/
#define GPX1CON_1 (*(volatile unsigned int *)0x11000c20)
void interrupt_init() {
// GPX1CON_1 = 0xF0; 这样影响到了其他位
// 设置GPX1CON_1为中断模式
GPX1CON_1 = (GPX1CON_1 & (0x0f << 4)) | (0x0f << 4);
//GPX1CON_1 & 0xffffff0f;
}
interrupt_init();
设置中断触发方式
一般触发方式和使能位都在同一片

EXT_INT41_CON
6.2.3.211 EXT_INT41CON
Base Address: 0x1100_0000
Address = Base Address + 0x0E04, Reset Value = 0x0000_0000

设置中断触发方式:
Sets signaling method of EXT_INT41[1]
0x0 = Low level 低电平
0x1 = High level 高电平
0x2 = Triggers Falling edge 下降沿
0x3 = Triggers Rising edge 上升沿
0x4 = Triggers Both edge 边缘触发
0x5 to 0x7 = Reserved

代码:
#define EXT_INT41_CON_1 (*(volatile unsigned int *)0x11000e04)
EXT_INT41_CON_1 = (EXT_INT41_CON_1 & (0x07 << 4)) | (0x02 << 4);打开进入cpu的开关(设置引脚中断使能)
EXT_INT41_MASK
6.2.3.223 EXT_INT41_MASK
Base Address: 0x1100_0000
Address = Base Address + 0x0F04, Reset Value = 0x0000_00FF

如果置为1 屏蔽信号
如果置为0 启用
0x0 = Enables Interrupt
0x1 = Masked
代码:
#define EXT_INT41_MASK_1 (*(volatile unsigned int *)0x11000F04)
EXT_INT41_MASK_1 = (EXT_INT41_MASK_1 & ~(0x01 << 1));控制中断功能的实现

主要看寄存器描述部分
9.5 Register Description
9.5.1 Register Map Summary
Base Address: 0x1048_0000
通过案例查询寄存器设置
//----功能块设置
ICDISER1_CPU0 = ICDISER1_CPU0 | (1<<25); //EINT9 (GPX1_1) GIC 中断使能
ICDIPTR14_CPU = 0x01010101; //参考例子背景,用默认设置
ICDDCR = ICDDCR | 1; // GIC 分发总使能
ICCICR_CPU0 = 1; // CPU0 中断使能
ICCPMR_CPU0 = 0xFF; // 设置CPU0的优先级门槛为最低有三种中断

- 软件中断
- 私有中断
- 共享中断
这里看共享中断
我们的XEINT9
就是我们的:
我们要实现k2按下 中断信号能够进入我们的cpu核
通过中断打断当前程序,输出一个字符
接下来结合代码来找一下这些寄存器
9.5.1.16 ICDISER_CPU
Base Address: 0x1049_0000
Address = Base Address + 0x0104, Reset Value = 0x0000_0000 (ICDISER1_CPU0)

For SPIs and PPIs, for each bit:
Reads)
0 = Disables the corresponding interrupt.
1 = Enables the corresponding interrupt.
Writes)
0 = No effect.
1 = Enables the corresponding interrupt. A subsequent
Read of this bit returns the value 1.读的时候0表示中断关掉的,1表示中断使能的
写的时候1表示使能中断
这里有128个使能中断
我们的spi【31:0】
结合刚才看见的EINT9 找到号码是25、57
这里的寄存器表示0x0104
然后置为1
#define ICDISER1_CPU0 (*(volatile unsigned int *)0x10490104)
ICDISER1_CPU0 = ICDISER1_CPU0 | (1<<25);
//EINT9 (GPX1_1) GIC 中断使能中断使能现在打通了
9.5.1.22 ICDIPTR_CPU
Base Address: 0x1049_0000
Address = Base Address + 0x0838, Reset Value = 0x0000_0000 (ICDIPTR14_CPU0)

9.5.1.12 ICDDCR
Base Address: 0x1049_0000
Address = Base Address + 0x0000, Reset Value = 0x0000_0000

全局的使能
#define ICDDCR (*(volatile unsigned int *)0x10490000)
ICDDCR = ICDDCR | 1; // GIC 分发总使能9.5.1.1 ICCICR_CPUn
Base Address: 0x1048_0000
Address = Base Address + 0x0000, Reset Value = 0x0000_0000 (ICCICR_CPU0)

#define ICCICR_CPU0 (*(volatile unsigned int *)0x10480000)
ICCICR_CPU0 = 1; // CPU0 中断使能这就是CPU核的接口层
9.5.1.2 ICCPMR_CPUn
Base Address: 0x1048_0000
Address = Base Address + 0x0004, Reset Value = 0x0000_0000 (ICCPMR_CPU0)

优先级
数字越小优先级越大
#define ICCPMR_CPU0 (*(volatile unsigned int *)0x10480004)
ICCPMR_CPU0 = 0xFF; // 设置CPU0的优先级门槛为最低异常向量表


协处理器指令
CP15协助处理器
MRC、MCR命理
可以设置异常向量表的地址
ldr r0,=0x40008000 @设置异常向量表的启始地址为 0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
现在我们需要改写中断相关代码:
_irq: .word irq_handler
irq_handler:
sub lr,lr,#4
stmfd sp!,{r0-r12,lr} @进栈保存现场
bl do_irq
irq_handler_end:
ldmfd sp!,{r0-r12,pc}^ @出栈恢复现场如果是软中断,lr就是他的地址
如果是中断,返回的地址lr要多减去4
当我们按下后,cpu打断当前程序
我们现在要打断现在一直循环的程序:
int main(void)
{
uart_init();
interrupt_init();
while(1)
{
putc('a');
delay1s();
}
return 0;
}按键按下后,就会到相应的中断处理函数 do_irq
如何识别是什么中断
读取中断ID

ICCIAR_CPU0 中断应答寄存器
9.5.1.4 ICCIAR_CPUn
Base Address: 0x1048_0000
Address = Base Address + 0x000C, Reset Value = 0x0000_03FF (ICCIAR_CPU0)
Address = Base Address + 0x400C, Reset Value = 0x0000_03FF (ICCIAR_CPU1)
Address = Base Address + 0x800C, Reset Value = 0x0000_03FF (ICCIAR_CPU2)
Address = Base Address + 0xC00C, Reset Value = 0x0000_03FF (ICCIAR_CPU3)
【9:0】The interrupt ID 中断id号
我们要考虑得到最低的十位:
#define ICCIAR_CPU0 (*(volatile int *)0x1048000C)
int irq_num;
irq_num = ICCIAR_CPU0&0x3ff; ////中断ID号接下来判断中断的id
EINT9 对应的是57
还需要清理中断的标志位
6.2.3.211 EXT_INT41CON
Base Address: 0x1100_0000
Address = Base Address + 0x0E04, Reset Value = 0x0000_0000
6.2.3.227 EXT_INT41_PEND
Base Address: 0x1100_0000
Address = Base Address + 0x0F44, Reset Value = 0x0000_0000

这个为1的时候说明中断已经发生
写1表示清除中断
#define EXT_INT41_PEND (*(volatile int *)0x11000f44)
EXT_INT41_PEND = EXT_INT41_PEND|(1<<1); //清GPX1_1中断标志9.5.1.19 ICDICPR_CPU
Base Address: 0x1049_0000
Address = Base Address + 0x0284, Reset Value = 0x0000_0000 (ICDICPR1_CPU0)

清除挂起中断信息

也就是说清除按钮部分标志
#define ICDICPR1_CPU0 (*(volatile int *)0x10490284)
ICDICPR1_CPU0 = ICDICPR1_CPU0|(1<<25); //清GIC GPX1_1中断标志switch(irq_num)
{
case 57:
putc('i');
EXT_INT41_PEND = EXT_INT41_PEND|(1<<1); //清GPX1_1中断标志
ICDICPR1_CPU0 = ICDICPR1_CPU0|(1<<25); //清GIC GPX1_1中断标志
break;
default: //其他中断
putc('e');
break;
}别的芯片可能就已经结束了
但是arm有个结束中断寄存器
9.5.1.5 ICCEOIR_CPUn
Base Address: 0x1048_0000
Address = Base Address + 0x0010, Reset Value = Undefined (ICCEOIR_CPU0)

#define ICCEOIR_CPU0 (*(volatile int *)0x10480010)
ICCEOIR_CPU0 = (ICCEOIR_CPU0&0x3FF)|irq_num; //结束中断 将处理完成的中断ID号写入该寄存器,则表示相应的中断处理完成代码1:
/*
实现k2中断
1. 看电路图找到CPU对应的控制管脚 GPX1_1
2. 看芯片手册,找到对应寄存器
a. 配置管脚为interrupt模式
b. 功能块设置
3. 编程
*/
#define GPX1CON_1 (*(volatile unsigned int *)0x11000c20)
#define EXT_INT41_CON_1 (*(volatile unsigned int *)0x11000e04)
#define EXT_INT41_MASK_1 (*(volatile unsigned int *)0x11000F04)
#define ICDISER1_CPU0 (*(volatile unsigned int *)0x10490104)
#define ICDIPTR14_CPU (*(volatile unsigned int *)0x10490838)
#define ICDDCR (*(volatile unsigned int *)0x10490000)
#define ICCICR_CPU0 (*(volatile unsigned int *)0x10480000)
#define ICCPMR_CPU0 (*(volatile unsigned int *)0x10480004)
void interrupt_init() {
// GPX1CON_1 = 0xF0; 这样影响到了其他位
// 设置GPX1CON_1为中断模式
GPX1CON_1 = (GPX1CON_1 & (0x0f << 4)) | (0x0f << 4);
//GPX1CON_1 & 0xffffff0f;
//中断触发方式下降沿
EXT_INT41_CON_1 = (EXT_INT41_CON_1 & (0x07 << 4)) | (0x02 << 4);
//关闭屏蔽状态
EXT_INT41_MASK_1 = (EXT_INT41_MASK_1 & ~(0x01 << 1));
//----功能块设置
ICDISER1_CPU0 = ICDISER1_CPU0 | (1<<25); //EINT9 (GPX1_1) GIC 中断使能
ICDIPTR14_CPU = 0x01010101; //参考例子背景,用默认设置
ICDDCR = ICDDCR | 1; // GIC 分发总使能
ICCICR_CPU0 = 1; // CPU0 中断使能
ICCPMR_CPU0 = 0xFF; // 设置CPU0的优先级门槛为最低
}
interrupt_init();代码2:
/*
功能: 实现k2 interrupt 输出字符显示
1. 看电路图找到CPU对应的控制管脚 GPX1_1
2. 看芯片手册,找到对应寄存器
a. 配置管脚为interrupt模式
b. 功能块设置
3. 编程
*/
#define GPX1CON (*(volatile unsigned int *)0x11000C20)
#define EXT_INT41CON (*(volatile unsigned int *)0x11000E04)
#define EXT_INT41_MASK (*(volatile unsigned int *)0x11000F04)
#define ICDISER1_CPU0 (*(volatile unsigned int *)0x10490104)
#define ICDDCR (*(volatile int *)0x10490000)
#define ICCICR_CPU0 (*(volatile int *)0x10480000)
#define ICCPMR_CPU0 (*(volatile int *)0x10480004)
#define ICDIPTR14_CPU0 (*(volatile int *)0x10490838)
#define ICCIAR_CPU0 (*(volatile int *)0x1048000C)
#define EXT_INT41_PEND (*(volatile int *)0x11000f44)
#define ICDICPR1_CPU0 (*(volatile int *)0x10490284)
#define ICCEOIR_CPU0 (*(volatile int *)0x10480010)
#define GPA1CON (*(volatile unsigned int *)0x11400020)
#define ULCON2 (*(volatile unsigned int *)0x13820000)
#define UCON2 (*(volatile unsigned int *)0x13820004)
#define UBRDIV2 (*(volatile unsigned int *)0x13820028)
#define UFRACVAL2 (*(volatile unsigned int *)0x1382002c)
#define UTXH2 (*(volatile unsigned int *)0x13820020)
#define UTRSTAT2 (*(volatile unsigned int *)0x13820010)
void uart_init(void)
{
//1. config pin GPA1_1 GPA1_0 uart mode
GPA1CON = 0x22;
//2. set uart function block
ULCON2 = 0x03; // data bit =8 parity=none stop bit=1
UCON2 = 0x05; //set polling mode
/*
* set Baud-Rate = 115200
* DIV_VAL = (100000000/(115200 *16)) - 1 = 53.253
UBRDIV2 =50
UFRACVAL2 = 0x253*16=4
*
*/
UBRDIV2 = 53;
UFRACVAL2 = 4;
}
void putc(char c)
{
while(1)
{
if( UTRSTAT2&0x02)
break;
}
UTXH2 = c;
}
void interrupt_init()
{
GPX1CON=(GPX1CON&~(0x0F<<4))|(0x0F<<4); //set gpx1_1 interrupt mode
EXT_INT41CON =(EXT_INT41CON&~(0x07<<4))|(0x02<<4);// set GPX1_1 falling edge triger
EXT_INT41_MASK = (EXT_INT41_MASK &~(0x01<<1)); //set pin interrupt enable
ICDISER1_CPU0 = ICDISER1_CPU0 | (1<<25); //EINT9 (GPX1_1) GIC中断使能
ICDIPTR14_CPU0 = 0x01010101;
ICDDCR = ICDDCR|1; //GIC 分发总使能
ICCICR_CPU0 = 1; // CPU0 中断使能
ICCPMR_CPU0 = 0XFF; //设置CPU0的优先级门槛为最低
}
void do_irq(void )
{
int irq_num;
irq_num = ICCIAR_CPU0&0x3ff; ////中断ID号
switch(irq_num)
{
case 57:
putc('i');
EXT_INT41_PEND = EXT_INT41_PEND|(1<<1); //清GPX1_1中断标志
ICDICPR1_CPU0 = ICDICPR1_CPU0|(1<<25); //清GIC GPX1_1中断标志
break;
default:
putc('e');
break;
}
ICCEOIR_CPU0 = (ICCEOIR_CPU0&0x3FF)|irq_num; //结束中断 将处理完成的中断ID号写入该寄存器,则表示相应的中断处理完成
}
int main(void)
{
uart_init();
interrupt_init();
while(1)
{
putc('a');
delay1s();
}
return 0;
}start:
.global delay1s
.text
.global _start
_start:
b reset @0x00
ldr pc,_undefined_instruction @0x04
ldr pc,_software_interrupt
ldr pc,_prefetch_abort
ldr pc,_data_abort
ldr pc,_not_used
ldr pc,_irq @0x18
ldr pc,_fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word irq_handler
_fiq: .word _fiq
irq_handler:
sub lr,lr,#4
stmfd sp!,{r0-r12,lr} @进栈保存现场
bl do_irq
irq_handler_end:
ldmfd sp!,{r0-r12,pc}^ @出栈恢复现场
reset:
ldr r0,=0x40008000 @设置异常向量表的启始地址为 0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
init_stack:
ldr r0,stacktop /*get stack top pointer*/
/********svc mode stack********/
mov sp,r0
sub r0,#128*4 /*512 byte for irq mode of stack*/
/****irq mode stack**/
msr cpsr,#0xd2
mov sp,r0
sub r0,#128*4 /*512 byte for irq mode of stack*/
/***fiq mode stack***/
msr cpsr,#0xd1
mov sp,r0
sub r0,#0
/***abort mode stack***/
msr cpsr,#0xd7
mov sp,r0
sub r0,#0
/***undefine mode stack***/
msr cpsr,#0xdb
mov sp,r0
sub r0,#0
/*** sys mode and usr mode stack ***/
msr cpsr,#0x10
mov sp,r0 /*1024 byte for user mode of stack*/
b main
delay1s:
ldr r4,=0x1ffffff
delay1s_loop:
sub r4,r4,#1
cmp r4,#0
bne delay1s_loop
mov pc,lr
.align 4
/**** swi_interrupt handler ****/
stacktop: .word stack+4*512
.data
stack:
.space 4*512
.end