Home » 华清嵌入式 » Arm 中断机制

Arm 中断机制

编 辑:Y ┊ 时 间:2022年03月07日 ┊ 访问: 30 次

中断

68276-z2turmcm5r.png

什么是中断

中断就是在运行间突然打断当前程序
处理结束后,继续处理

好处

提升实时性

实时性

中断是随机的,打断后要及时处理
处理结束后,继续处理打断的程序

引脚

信号通过引脚进入

中断信号如何进入CPU里面

形象的表示:过五关斩六将

  1. 配置串口的模式
    GPIO控制
  2. 使能
  3. 分发器
    分发使能 GIC
  4. 接口
    接口使能
  5. 优先级判断
过五关斩六将

cpu中的操作

  • 打断当前程序,保护现场
  • 执行irq
  • 处理后回来继续执行,恢复现场

如何识别不同id

这里有个中断号的概念

中断标志

  • 记录中断标志
  • 清理中断标志

主板

45282-nwctlr5d9f8.png

按下按钮发送中断

步骤

  1. 看电路图 看链接的管脚
  2. 看芯片手册 找到哪个寄存器控制它的
  3. 编程控制

按键

89082-ejzsxu5vlpt.png

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

46256-zf75xumx7en.png

找到XEINT9/KP_COL1/ALV_DBG5/GPX1_1

找到控制引脚的章节

06297-nx5n6n2aqj.png

寻找:GPX1_1
配置中断模式只需要 GPX1CON就可以
78313-jn01lreyk3.png

6.2.3.198 GPX1CON
 Base Address: 0x1100_0000
 Address = Base Address + 0x0C20, Reset Value = 0x0000_0000

我们的是[7:4]
39839-p6g7cykt8f.png

设置中断模式对应的是
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();

设置中断触发方式

一般触发方式和使能位都在同一片

81364-pseifjq7lzm.png

EXT_INT41_CON

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

38084-anne63m54f5.png

设置中断触发方式:
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

50781-3pjtdgn135.png

代码:

#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

17425-fphw1139lsq.png

如果置为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));

控制中断功能的实现

75999-adw0zz01qtk.png

主要看寄存器描述部分
21550-0q6pfaps0cfo.png

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的优先级门槛为最低

有三种中断

62279-quu0vzthgb.png

  • 软件中断
  • 私有中断
  • 共享中断

这里看共享中断
47576-04zfxnvfy7w5.png

我们的XEINT9
83522-drfuol0kdge.png

就是我们的:
30186-8sdi7m8iv34.png

我们要实现k2按下 中断信号能够进入我们的cpu核
通过中断打断当前程序,输出一个字符

接下来结合代码来找一下这些寄存器

9.5.1.16 ICDISER_CPU

 Base Address: 0x1049_0000
 Address = Base Address + 0x0104, Reset Value = 0x0000_0000 (ICDISER1_CPU0)

86504-o4rh4nzhfal.png

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表示使能中断
76641-0kpxlwxn2rb.png

这里有128个使能中断
我们的spi【31:0】
结合刚才看见的EINT9 找到号码是25、57
03693-v6bx6rhmgei.png

这里的寄存器表示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)

92643-dwlvqjt221.png

9.5.1.12 ICDDCR

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

20726-h5osjtsuoxl.png

全局的使能

#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)

42111-1xt9o6f1wj2.png

#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)

02848-iay6mk0f2a.png

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

异常向量表

84347-it3a5gj56z.png

77816-z6ddt5ccj2c.png

协处理器指令

CP15协助处理器
MRC、MCR命理
可以设置异常向量表的地址

ldr    r0,=0x40008000      @设置异常向量表的启始地址为 0x40008000
mcr    p15,0,r0,c12,c0,0        @ Vector Base Address Register

04828-vdbtq7b5r7.png

现在我们需要改写中断相关代码:

_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打断当前程序
38691-cvw4dfxldb7.png

我们现在要打断现在一直循环的程序:

int main(void) 
{
    uart_init();
    interrupt_init();
    while(1)
    {
      putc('a');
      delay1s();
    }
    return 0;
}

按键按下后,就会到相应的中断处理函数 do_irq

如何识别是什么中断

读取中断ID

34817-fbx5hqbczlw.png

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
06563-yuf0z1sc46.png

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

92656-rsu72mtqtts.png

这个为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)

48970-ewurfhjdnvb.png

清除挂起中断信息
21709-izya6l7974n.png

04523-xb7h1evxut.png

也就是说清除按钮部分标志

#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)

92121-m1cux7wue7.png

#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



Copyright © 2026 Y 版权所有.网站运行:13年238天21小时25分