ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ARM接口技术——中断控制器

2022-04-27 05:00:06  阅读:259  来源: 互联网

标签:控制器 r0 接口技术 中断 CPU ARM 寄存器 stack


ARM中断的介绍

ARM中断

不同的处理器对中断的处理流程大体相同,但是具体的实现细节会差别。

ARM异常源

ARM中断也是异常的一种,ARM处理器有以下几种异常源:

  • FIQ
  • IRQ
  • Reset
  • Software Interrupt
  • Data Abort
  • Prefetch Abort
  • Undefined Instruction

中断虽然是异常,但不完全是错误,异常也不是,而是为了完成某些功能的东西,只有Undefined Instruction异常是真正的系统错误。

这些在ARM异常机制也介绍过,这里主要介绍的是IRQ。

ARM中断执行过程

在ARM处理器中,CPU收到一个中断时,以IRQ为例,大概会执行以下过程:

  • 修改状态寄存器CPSR从User模式切换到到IRQ模式;
  • 把中断IRQ禁止位打开,防止再次接收IRQ中断信号(FIQ不禁止,FIQ中断信号可以打断IRQ中断程序);
  • 跳转到中断向量表IRQ的指令,该指令如果写了中断执行函数,再跳转到该代码段;
  • 执行完之后再跳转回正常程序。
ARM中断产生的问题

在ARM处理器中,有上百个中断源,可能同时产生多个中断信号,会产生问题。

比如:

  • 同样是产生IRQ的中断源有很多个,但是中断向量表的IRQ只有一条跳转指令,那么处理器不知道是谁产生的中断信号,该执行什么中断处理程序;
  • 在处理器正在执行中断程序的时候,CPSR打开了IRQ禁止位,那么其它中断就会被忽略;
  • Exynos4412芯片集成了4个同样的ARM cortex A9处理器,产生的中断应该让哪个处理器处理。

在复杂封装的ARM处理器芯片中还会产生一系列问题,这时候就需要一个中断控制器GIC,去统一管理外部产生的中断信号。

Exynos4412下的中断控制器GIC

中断控制器做了至少四件事:

  1. 接收外部设备产生的中断信号;
  2. 多个中断同时产生时,对中断信号挂起,并按照优先级依次发给CPU;
  3. 为每个中断选择一个CPU进行处理;
  4. CPU处理中断程序时,可以查询中断控制器获取中断信号是由哪个硬件产生的。

这样就解决了前面提到的几个问题,除此之外中断控制器还做了一系列管理中断的事情。

中断实验

中断实验分析

Exynos4412这种SOC要比单片机功能强大很多,相关的寄存器配置也及其复杂,SOC一般会安装一个操作系统,这些事情一般都由操作系统内核去调度。

这里裸机实验就一切从简,只实现基本的中断功能,对于设置中断类型、优先级和选择处理器等不做配置。

原理图:

外围电路原理图:按键Key2连接的引脚是UART_RING。

核心板原理图:所连接的是GPX1_1引脚,同时它是外部中断9的中断源:XEINT9。

中断相关寄存器的配置

Exynos4412中断机制比较复杂,有160个中断,分为三种中断类型:

 

 

我们使用的是SPI,提供的SPI类型中断有128个(0-127)。

1. 首先是中断源的配置,即引脚相关寄存器:

GPX1CON GPIO配置寄存器,GPX1CON[1]设为中断功能,0xF。


 

前面提到我们使用的是外部中断9,但是它使用的中断配置寄存器叫EXT_INT41,不知道三星公司为什么这样设计,但是它正好在寄存器列表的GPX相关寄存器下面。

EXT_INT41_CON 中断配置寄存器,EXT_INT41_CON[1]设为下降沿触发,0x2。


 

EXT_INT41_MASK 中断屏蔽寄存器,默认是打开屏蔽,需要关闭屏蔽使能中断功能,EXT_INT41_MASK[1]设为0。

 

 


EXT_INT41_PEND,挂起状态寄存器

 

 

EXT_INT41_PEND寄存器在该引脚产生中断时,会自动挂起变成1,在CPU处理了这个中断后,这个寄存器的值不会改变,会不断的发出中断信号,所以中断处理程序在处理完这个中断之后需要修改这个寄存器的值。

这个寄存器很特殊,写0的效果是变成1,写1变成0,所以中断处理程序需要写1,它会变成0。

 

至此中断源就配置好了,但是Exynos4412的CPU不直接去接收中断信号,而是通过一个中断控制器去管理中断,所以还需要配置一系列中断控制器的配置。


 

2. 中断控制器相关配置:

首先要找到这个中断源对应的中断号是多少。

中断表的中断号是第二列ID,第一列只是中断类型为SPI的编号。

...

ID: 141-68略

...

 可以看到我们要使用的外部中断9的中断号是57,后面会用到。


 

ICDDCR寄存器

ICDDCR,整个中断控制器的总开关,让中断控制器能够接收外部的中断信号,必须要打开。ICDDCR = 1。


 

ICDISER_CPU

 

 

这组寄存器的作用是使能某个中断与CPU的接口(它的表述上是打开中断与CPU的接口,实际上只是打开中断与中断控制器的接口,真正与CPU交互的是中断控制器)。

Exynos4412有160个中断,每个寄存器是32位,所以可以控制32个中断,一共需要5个寄存器。

(但是它提供了8个寄存器,后面3个是与PPI和SGI相关寄存器,与SPI中断无关)

它的对应关系如下:

 

 

57号中断所在的寄存器地址偏移量为0x0104,结合上一张图0x0104对应的寄存器名为ICDISER1_CPU0,在三星提供的头文件中寄存器名是ICDISER1。

所以打开中断57与CPU0的接口就是设置 ICDISER1_CPU0[25]=1。


 

ICDIPTR_CPU

ICDIPTR_CPU寄存器设置用于告诉中断控制器这个中断该转发给哪个CPU。

它的数量很多对应关系比较复杂,直接给出结果:ICDIPTR14[15:8] = 0x01。


 

ICCICR_CPU

至此只是告诉中断控制器这个中断转发给哪个CPU,但是中断控制器与这个CPU之间的接口还没打开。

刚刚设置了要转发给CPU0,那么需要打开GIC与CPU0的接口:ICCICR_CPU0 = 1。


 

ICCEOIR

 

 

这个寄存器的作用是当中断处理程序执行完以后,CPU告诉GIC当前处理的中断号程序已经处理完,GIC如果还有挂起的中断,就可以发给CPU了。

在中断处理程序结束之前:ICCEOIR_CPU0 = 57(告诉GIC处理完57号中断)。


 

配置完以上寄存器就配置了以下功能:

  • 打开中断控制器总开关
  • 中断控制器对某个中断号的使能
  • 配置某个中断发生后该发往哪个处理器
  • 打开中断控制器与某个处理器直接的接口

3. 除此之外,还有其它一系列寄存器设置,前面也提到只实现基本的中断功能,所以其它寄存器就不做设置。

中断实验代码

C语言执行环境的修改:

中断实验需要修改中断向量表,另外一篇会介绍裸机实验的工程模板,还没写好,这里不作介绍。

启动汇编程序start.S(这里只需要关注7-14行和93-98行):

  1 .text
  2 .global _start
  3 _start:
  4     /*
  5      * Vector table
  6      */ 
  7     b reset
  8     b .
  9     b .
 10     b .
 11     b .
 12     b .
 13     b irq_handler
 14     b .
 15 
 16 reset:
 17     /*
 18      * Set vector address in CP15 VBAR register
 19      */ 
 20     ldr    r0, =_start
 21     mcr    p15, 0, r0, c12, c0, 0    @Set VBAR
 22 
 23     /*
 24      * Set the cpu to SVC32 mode, Disable FIQ/IRQ
 25      */  
 26     mrs r0, cpsr
 27     bic r0, r0, #0x1f
 28     orr    r0, r0, #0xd3
 29     msr    cpsr ,r0
 30 
 31     /*
 32      * Defines access permissions for each coprocessor
 33      */  
 34     mov    r0, #0xfffffff
 35     mcr    p15, 0, r0, c1, c0, 2      
 36 
 37     /*
 38      * Invalidate L1 I/D                                                                                                                   
 39      */
 40     mov    r0, #0                    @Set up for MCR
 41     mcr    p15, 0, r0, c8, c7, 0    @Invalidate TLBs
 42     mcr    p15, 0, r0, c7, c5, 0    @Invalidate icache
 43     
 44     /*
 45      * Set the FPEXC EN bit to enable the FPU
 46      */ 
 47     mov r3, #0x40000000
 48     fmxr FPEXC, r3
 49     
 50     /*
 51      * Disable MMU stuff and caches
 52      */
 53     mrc    p15, 0, r0, c1, c0, 0
 54     bic    r0, r0, #0x00002000        @Clear bits 13 (--V-)
 55     bic    r0, r0, #0x00000007        @Clear bits 2:0 (-CAM)
 56     orr    r0, r0, #0x00001000        @Set bit 12 (---I) Icache
 57     orr    r0, r0, #0x00000002        @Set bit 1 (--A-) Align
 58     orr    r0, r0, #0x00000800        @Set bit 11 (Z---) BTB
 59     mcr    p15, 0, r0, c1, c0, 0
 60 
 61     /*
 62      * Initialize stacks                                                                                                                  
 63      */
 64 init_stack:     
 65     /*svc mode stack*/
 66     msr cpsr, #0xd3
 67     ldr sp, _stack_svc_end
 68 
 69     /*undef mode stack*/
 70     msr cpsr, #0xdb
 71     ldr sp, _stack_und_end
 72 
 73     /*abort mode stack*/    
 74     msr cpsr,#0xd7
 75     ldr sp,_stack_abt_end
 76 
 77     /*irq mode stack*/    
 78     msr cpsr,#0xd2
 79     ldr sp, _stack_irq_end
 80     
 81     /*fiq mode stack*/
 82     msr cpsr,#0xd1
 83     ldr sp, _stack_fiq_end
 84     
 85     /*user mode stack, enable FIQ/IRQ*/
 86     msr cpsr,#0x10
 87     ldr sp, _stack_usr_end
 88 
 89     /*Call main*/
 90     b main
 91 
 92 /* IRQ的异常处理程序 */    
 93 irq_handler:
 94     /* 因为产生IRQ异常时LR自动保存的是当前执行指令的下下条指令 */
 95     sub lr,lr,#4     /* 修正LR位为中断前执行指令的下条指令 */
 96     stmfd sp!,{r0-r12,lr}    /* 压栈保护现场 */
 97     bl do_irq    /* 跳转到中断处理程序 */
 98     ldmfd sp!,{r0-r12,pc}^    /* 出栈恢复现场,同时恢复中断之前的模式 */
 99 
100 
101 _stack_svc_end:      
102     .word stack_svc + 512
103 _stack_und_end:      
104     .word stack_und + 512
105 _stack_abt_end:      
106     .word stack_abt + 512
107 _stack_irq_end:      
108     .word stack_irq + 512
109 _stack_fiq_end:
110     .word stack_fiq + 512
111 _stack_usr_end:      
112     .word stack_usr + 512
113 
114 .data
115 stack_svc:      
116     .space 512
117 stack_und:
118     .space 512
119 stack_abt:      
120     .space 512
121 stack_irq:      
122     .space 512
123 stack_fiq:      
124     .space 512
125 stack_usr:      
126     .space 512

interface.c:

/*
 * 按下K2,串口打印信息
 * 按下K3,LED点亮,再按熄灭
 * */

#include "exynos_4412.h"

void do_irq()
{
    /* 从中断控制器中获取当前中断的中断号 */
    unsigned int IrqNum = CPU0.ICCIAR & 0x3FF;
    switch(IrqNum)
    {
        case 57:
            printf("Key2 pressed\n");  /* 另一个文件实现的重定向到UART的printf */
            EXT_INT41_PEND = (1<<1);    /* 把中断控制器的挂起清除 */
            /* 告诉中断控制器当前中断已经处理完成 */
            /* 把当前中断的中断号写回中断控制器 */
            CPU0.ICCEOIR = CPU0.ICCEOIR & (~0x3FF) | 57;
            break;
        case 58:
            LED2_Turn();
            EXT_INT41_PEND = (1<<2);    /* 把中断控制器的挂起清除 */
            /* 告诉中断控制器当前中断已经处理完成 */
            /* 把当前中断的中断号写回中断控制器 */
            CPU0.ICCEOIR = CPU0.ICCEOIR & (~0x3FF) | 58;

            break;
        default:
            break;
    }
}

void KEY2_INT_Init()
{
    /* 外围层次 */
    /* GPX1_1设为中断功能 */
    GPX1.CON = GPX1.CON | (0xF<<4);
    /* 下降沿触发 */
    EXT_INT41_CON = EXT_INT41_CON & (~(0x7<<4)) | (0x2<<4);
    /* 打开中断,写 0 Enable */
    EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1<<1));


    /* SOC内层 中断控制器 */
    /* 中断控制器全局使能,让中断控制器接收外部的中断信号 */
    ICDDCR = ICDDCR | 1;
    /* 中断控制器中使能57号中断 */
    ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1<<25);
    /* 设置57号中断发给CPU0处理 */
    ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF<<8)) | (0x01<<8);
    /* 打开中断控制器与CPU0的接口 */
    CPU0.ICCICR = CPU0.ICCICR | 1;

}

void KEY3_INT_Init()
{
    /* 外围层次 */
    /* GPX1_1 设为中断功能 */
    GPX1.CON = GPX1.CON | (0xF<<8);
    /* EXT_INT41_CON[2] 设为下降沿触发 */
    EXT_INT41_CON = EXT_INT41_CON & (~(0x7<<8)) | (0x2<<8);
    /* EXT_INT41_MASK[2] 打开中断,写 0 Enable */
    EXT_INT41_MASK = EXT_INT41_MASK & ~(0x1<<2);


    /* SOC内层 中断控制器 */
    /* 中断控制器全局使能,让中断控制器接收外部的中断信号 */
    ICDDCR = ICDDCR | 1;
    /* 中断控制器中使能58号中断 */
    ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1<<26);
    /* 设置58号中断发给CPU0处理 */
    ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF<<16)) | (0x1<<16);
    /* 打开中断控制器与CPU0的接口 */
    CPU0.ICCICR = CPU0.ICCICR | 1;

}


void LED2_Init()
{
    GPX2.CON = GPX2.CON & (~(0xF<<28)) | (0x1<<28);
}

void LED2_On()
{
    GPX2.DAT |= (1<<7);
}

void LED2_Off()
{
    GPX2.DAT &= (~(1<<7));
}

void LED2_Turn()
{
    if(GPX2.DAT & (1<<7))/* 条件为真,则关掉LED */
        LED2_Off();
    else
        LED2_On();
}

int main()
{
    KEY2_INT_Init();
    KEY3_INT_Init();
    LED2_Init();
    while(1);
    return 0;
}

实验现象

按下K2,终端会打印信息: Key2 pressed。

按下K3,LED2会打开,再次按下LED2会熄灭。

 

标签:控制器,r0,接口技术,中断,CPU,ARM,寄存器,stack
来源: https://www.cnblogs.com/zjccc/p/16192125.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有