中断控制器

1. 中断的理解

1.1 什么是中断

中断:  通常指  某种事件(中断源)  触发了  需要打断CPU , 让CPU暂停当前处理的(保存现场) 任务(usr模式下)  打断(irq异常) 转而去处理  这个事件(在irq模式中)  ,事件处理结束后 需要回到(恢复现场)  打断处继续向后执行  

1.2  中断控制器的作用

中断控制器作用: 通用中断控制器 

1. 扩展中断接口  CPU--irq   中断控制器(GIC)   ----- 中断源 

2. 中断源管理  使能或不使能 某些中断 

3. 管理中断源 优先级  

4. 管理 中断送哪个CPU处理  (多核处理器)

(NVIC) 嵌套向量中断控制器 

5. 中断嵌套   高优先级的中断 将 可以打断 低优先级的程序执行 

中断系统: 是CPU实现 异步功能的 必要设备  

1.3 中断控制器:  原理 

如图所示 内部结构

1.4 中断挂起

指中断触发时, 中断控制器或中断源本身, 将中断信号记录到一个寄存器(挂起寄存器)中若挂起寄存器中  有中断被挂起了, 表示 该中断触发了,但还没有处理或正在处理中 处理完中断后 应立即清除中断挂起, 否则该挂起的中断 将会持续尝试打断CPU 

若有多个地方存在中断挂起 清除中断时 应 先清除先挂起的中断寄存器 

3. 实验4 按键中断实验

3.1 原理图

实验4:  按键中断实验 :  使用中断法 实现 按键点击 控制LED 亮灭  

1. 硬件设备  

KEY  按键  使用 K2  ===>  GPX1_1  

2. 电路图  

使用 K2  ===>  GPX1_1  

3. 控制器   

外部中断:  中断信号来源于芯片外部  通过GPIO进入芯片内部 如 按键  一些模块  

内部中断:  中断信号来源于芯片内部  由控制器触发的 如 串口中断  定时器中断  ....

外部中断 经过了 GPIO控制器 传入

3.2 配置寄存器

配置寄存器   参考图片 

中断号:  就是芯片制造厂商 对 芯片中 所有的中断源 进行的 一个编号 

749 页  9.2.2 章节  Table 9-2 GIC Interrupt Table (SPI[127:0])

3.2.1 寄存器配置:  GPX1_1

GPX1_1 中断输入模式

GPX1CON[1]  [7:4] = 0xF = WAKEUP_INT1[1]   外部中断输入模式 

EXT_INT41_CON 中断触发模式

GPX1  对应   EXT_INT41_CON  (子啊比如GPX2----EXT_INT40_CON,以此类推)

EXT_INT41_CON [1]:  367 页   配置中断触发模式  

低电平触发 

高电平触发

上升沿触发  [6:4] = 0x3  上升沿触发

下降沿触发 

边沿触发 

EXT_INT41_MASK  中断屏蔽寄存器

EXT_INT41_MASK:  GPIO中断屏蔽寄存器

EXT_INT41_MASK [1] = 0  使能中断  

3.2.2 GIC配置 面向中断源

ICDDCR 总中断使能

ICDDCR  总中断使能寄存器  = 1使能  打开总中断

ICDISER 端口中断使能

ICDISER_CPU: 配置端口中断使能寄存器 ,就是配置哪些中断源能够使能,哪些不能

该寄存器 存储了 所有的 中断端口的 使能情况 共 0-4 个寄存器

其中 0 号寄存器存储 CPU 的  PPI (私有中断SGI  (软件中断)

1-4号 存储的 是 SPI 共享中断的 使能情况  

GPX1_1  的SPI 端口是什么???

通过 中断源表 查询可得  GPX1_1 --- EINT[9] ---- ID(57) --- SPI中断端口号 25

57号 中断ID  在 ICDISER_CPU 的1号寄存器 的  [25]bit 

若中断ID为 x  问 ICDISER_CPU 配置 第几号 的 多少bit位  

寄存器号 = 中断ID / 32 的商    对应bit = 中断ID % 32

ICDIPR  端口优先级配置寄存器

ICDIPR: 端口优先级配置寄存器  每个端口 8bit 宽度 优先级范围 0-255

0-39 共40个寄存器 

0-7  用于配置 SGI 和 PPI 

8-38 配置  SPI 

实际管脚对应的 寄存器号 = 中断ID / 4 的商    对应bit位域 = 中断ID % 4

余0  [7:0]   余1 [15:8]   余2[23:16]   余3 [31:24]

ICDIPTR 中断源 送哪个cpu

ICDIPTR_CPU: 用于配置 中断源 送哪个或哪些CPU处理 

寄存器分步格局 与ICDIPR 完全一样 

8bit 每个bit 分别表示要送哪个CPU  

3.2.3 GIC 面向cpu

ICCICR 中断响应使能寄存器

ICCICR   CPU中断响应使能寄存器   = 1 使能   =0 不使能

ICCPMR  优先级过滤寄存器  

ICCPMR   优先级过滤寄存器        

3.3 写程序

1. 配置必要寄存器 

2. 打开 do_irq 的 注释 

3. 实现 do_irq 函数 

注意

打开start.S文件修改,如下

ICCIAR 获取中断号

访问CPU0.ICCIAR寄存器得到中断号

EXT_INT41_PEND 清除中断 源头的挂起 

ICDICPR 在清除GIC分配器层中断挂起

ICCEOIR 清除cpu中断挂起

key_exit-----> main


#include"exynos_4412.h"
#include"uart.h"

void key_exit_init(){
    //GPIO     key2    GPX1_1
    //1. 配置引脚为外部中断输入模式
    GPX1.CON = (GPX1.CON & ~(0xf<<4)) | (0xf << 4);
    
    //2. 配置中断触发模式 这里配置为上升沿触发 [6:4] = 0x3
    EXT_INT41_CON = (EXT_INT41_CON & ~(0x7<<4)) | (0x3 << 4);
    
    //3. 配置中断掩码屏蔽寄存器
    //配置为使能中断 [1] = 0
    EXT_INT41_MASK &= ~(1<<1);


    // gic 面向中断源
    //4. 打开总中断
    //[0] = 1
    ICDDCR = 1; 

    //5. 配置端口中断使能寄存器 
    //就是配置哪些中断源能够使能,哪些不能
    //找到中断源的 中断号(57)寄存器编号1=57/32(即ICDISER1)  端口号(25=57%32)
    //配置为 1  使能
    ICDISER.ICDISER1 |= 1<<25;

    //6. 端口优先级设置寄存器 ,这里我们设置优先级为  5
    //14 = 57/4  1=57%4  ---->[15:8]
    ICDIPR.ICDIPR14 = (ICDIPR.ICDIPR14 & ~(0xff<<8)) | (5 << 8);

    //7. 配置中断源送去哪个cpu处理   0x1表示直送cpu0
    //寄存器分步格局 与ICDIPR 完全一样 
    ICDIPTR.ICDIPTR14 = (ICDIPTR.ICDIPTR14 & ~(0xff<<8)) | (0x1 << 8);


    //GIC 面向cpu
    //8. cpu响应中断使能  =1 使能  =0 不使能
    CPU0.ICCICR = 1;

    //9. cpu优先级过滤寄存器 这里我们设置为255 只有比255优先级还低的会被过滤,因为255至最低的优先级
    //我们前面设置的优先级为5 优先级高于255 所以不会被过滤
    CPU0.ICCPMR = 255;
}

//中断响应,c语言入口函数,在汇编汇总调用,当irq异常触发时
void do_irq(){
    // 1.获取中断号,用于区分哪个中断源触发的中断
    int irq_id = 0;
    //访问CPU0.ICCIAR寄存器得到中断号 说明CPU0.ICCIAR的[9:0]存储了中断号
    irq_id = CPU0.ICCIAR;
    printf("irq_id = %d\n",irq_id);
 
    //2.根据中断id来处理对应的事件
    switch(irq_id)
    {
        case 57:
            printf("key2 clicl!!!\n");
            //清除中断挂起 
            //先清除中断 源头的挂起 //GPX1_1  写1清除
            EXT_INT41_PEND |= 1<<1;
            
            //在清除GIC分配器层中断挂起 与ICDISER_CPU 结构一样 id:57
            //置为1  清除
            ICDICPR.ICDICPR1 |= 1<<25;
            break;
    }
    //最后清除cpu中断挂起
    //写入中断id清除对应中断挂起
    CPU0.ICCEOIR = irq_id;
    
}


int main()
{
    int a = 100;
    //初始化串口
    uart_init();

    //初始化中断
    key_exit_init();

    printf("hello!a=%d\r\n",a);
    while(1);
    return 0;
}


4. 使用中断 按下k2 控制led3亮灭

key2_led3---main.c


#include"exynos_4412.h"
#include"uart.h"
//中断的使用

//初始化led3
void led3_init(){
    //配置引脚模式
    GPX1.CON = (GPX1.CON & ~(0xf<<0)) | (0x1 << 0);
    //配置数据寄存器
    // GPX1.DAT |= 1;
    GPX1.DAT &= ~1;
    //配置上下拉寄存器
    GPX1.PUD &= ~(0x3<<0);
}



//初始化中断
void key_exit_init(){
    //GPIO     key2    GPX1_1
    //1. 配置引脚为外部中断输入模式
    GPX1.CON = (GPX1.CON & ~(0xf<<4)) | (0xf << 4);
    
    //2. 配置中断触发模式 这里配置为上升沿触发 [6:4] = 0x3
    EXT_INT41_CON = (EXT_INT41_CON & ~(0x7<<4)) | (0x3 << 4);
    
    //3. 配置中断掩码屏蔽寄存器
    //配置为使能中断 [1] = 0
    EXT_INT41_MASK &= ~(1<<1);


    // gic 面向中断源
    //4. 打开总中断
    //[0] = 1
    ICDDCR = 1; 

    //5. 配置端口中断使能寄存器 
    //就是配置哪些中断源能够使能,哪些不能
    //找到中断源的 中断号(57)寄存器编号1=57/32(即ICDISER1)  端口号(25=57%32)
    //配置为 1  使能
    ICDISER.ICDISER1 |= 1<<25;

    //6. 端口优先级设置寄存器 ,这里我们设置优先级为  5
    //14 = 57/4  1=57%4  ---->[15:8]
    ICDIPR.ICDIPR14 = (ICDIPR.ICDIPR14 & ~(0xff<<8)) | (5 << 8);

    //7. 配置中断源送去哪个cpu处理   0x1表示直送cpu0
    //寄存器分步格局 与ICDIPR 完全一样 
    ICDIPTR.ICDIPTR14 = (ICDIPTR.ICDIPTR14 & ~(0xff<<8)) | (0x1 << 8);


    //GIC 面向cpu
    //8. cpu响应中断使能  =1 使能  =0 不使能
    CPU0.ICCICR = 1;

    //9. cpu优先级过滤寄存器 这里我们设置为255 只有比255优先级还低的会被过滤,因为255至最低的优先级
    //我们前面设置的优先级为5 优先级高于255 所以不会被过滤
    CPU0.ICCPMR = 255;
}

//中断响应,c语言入口函数,在汇编汇总调用,当irq异常触发时
void do_irq(){
    // 1.获取中断号,用于区分哪个中断源触发的中断
    int irq_id = 0;
    //访问CPU0.ICCIAR寄存器得到中断号 说明CPU0.ICCIAR的[9:0]存储了中断号
    irq_id = CPU0.ICCIAR;
    printf("irq_id = %d\n",irq_id);
 
    //2.根据中断id来处理对应的事件
    switch(irq_id)
    {
        case 57:
            printf("key2 clicl!!!\n");

            GPX1.DAT ^= (1<<0); 


            //清除中断挂起 
            //先清除中断 源头的挂起 //GPX1_1 写1清除
            EXT_INT41_PEND |= 1<<1;

            //在清除GIC分配器层中断挂起 与ICDISER_CPU 结构一样 id:57
            //置为1  清除
            ICDICPR.ICDICPR1 |= 1<<25;
            break;
    }
    //最后清除cpu中断挂起
    //写入中断id清除对应中断挂起
    CPU0.ICCEOIR = irq_id;
    
}


int main()
{
    int a = 100;
    //初始化串口
    uart_init();

    led3_init();
    
    //初始化中断
    key_exit_init();

    printf("hello!a=%d\r\n",a);

    while(1);
    return 0;
}



5. 实现 K2  K3  K4的 中断程序 

key_led---main.c


#include"exynos_4412.h"
#include"uart.h"

//实现 K2  K3  K4的 中断程序
//延时函数
void delay(int m)
{
	int i;
	while(m--)
	  for(i=0; i < 10000 ; i++);
}

//中断初始化
void key_exti_init()
{
	//gpio
	//K2 --- GPX1-1 ----- ID 57
	//K3 --- GPX1-2 ----- ID 58
	//K4 --- GPX3-2 ----- ID 64
	//GPX1CON[1]  [7:4] = 0xF = WAKEUP_INT1[1]   外部中断输入模式 
	GPX1.CON |= 0xf<<4;
	GPX1.CON |= 0xf<<8;
	GPX3.CON |= 0xf<<8;

	//EXT_INT41_CON [1]: [6:4] = 0x3  上升沿触发
	EXT_INT41_CON = (EXT_INT41_CON & ~ ( 0x7<< 4)) | ( 0x3 << 4); 
	EXT_INT41_CON = (EXT_INT41_CON & ~ ( 0x7<< 8)) | ( 0x3 << 8); 
	EXT_INT43_CON = (EXT_INT43_CON & ~ ( 0x7<< 8)) | ( 0x3 << 8); 

	//GPIO中断屏蔽寄存器 [1] = 0  使能中断  
	EXT_INT41_MASK &= ~(1<<1); 
	EXT_INT41_MASK &= ~(1<<2); 
	EXT_INT43_MASK &= ~(1<<2); 

	//gic 面向中断源
	//ICDDCR  总中断使能寄存器  = 1使能 
	ICDDCR = 1;

	//ICDISER_CPU: 中断使能, GIC端口使能 中断ID 57
	//寄存器编号1 = 57/32 商  25 = 57 % 32
	ICDISER.ICDISER1 |= 1<<25;  // 57
	ICDISER.ICDISER1 |= 1<<26;  // 58
	ICDISER.ICDISER2 |= 1<<0 ;  // 64
	
	//端口优先级设置寄存器  14 == 57 /4商 
	ICDIPR.ICDIPR14 = (ICDIPR.ICDIPR14 & ~(0xff << 8))|(5  << 8);
	ICDIPR.ICDIPR14 = (ICDIPR.ICDIPR14 & ~(0xff << 16))|(5 << 16);
	ICDIPR.ICDIPR16 = (ICDIPR.ICDIPR16 & ~(0xff << 0))|(5  << 0);

	//ICDIPTR_CPU: 用于配置 中断源 送哪个或哪些CPU处理  1表示只送CPU0
	ICDIPTR.ICDIPTR14 = (ICDIPTR.ICDIPTR14 & ~(0xff <<8)) | ( 0x1<<8);
	ICDIPTR.ICDIPTR14 = (ICDIPTR.ICDIPTR14 & ~(0xff <<16)) | ( 0x1<<16);
	ICDIPTR.ICDIPTR16 = (ICDIPTR.ICDIPTR16 & ~(0xff <<0)) | ( 0x1<<0);

	//GIC 面向CPU
	//使能CPU0相应中断
	CPU0.ICCICR = 1;
	//优先级过滤寄存器 只有当优先级高于该值的中断源才可以送该CPU
	CPU0.ICCPMR = 255;
}
//中断相应 C语言入口函数 在汇编中被调用,当irq异常触发时
void do_irq(  )
{
	//如何区分哪个中断源触发的中断
	//1. 获得中断号 
	int irq_id = 0;
	irq_id = CPU0.ICCIAR;
	printf("irq_id=%d\r\n",irq_id); //打印调试  /r可以使超级终端光标回到起始位置
	//2. 根据中断id来处理对应的事件
	switch(irq_id)
	{
		case 57: // K2 id=57
			delay(1);//延时消抖
            //因为前面已经延时了一段时间,这里再判断一下管脚的电平,几乎可以确认到这里的是松开手的高电平了
			if(GPX1.DAT & (1<<1))//这里读取一下管脚的电平只有松开手时,触发高电平才可以进入if
			{
				printf("K2 click!\r\n");
			}
            // printf("K2 click!\n");
			//3. 中断处理完后 清楚中断挂起 
			//先清楚中断源头的挂起
			EXT_INT41_PEND |= 1<<1; //GPX1_1
			//在清楚GIC分配器层中断挂起 与 ICDISER_CPU 结果一样 id:57
			ICDICPR.ICDICPR1 |= 1<<25; 
			break;
		case 58:
            delay(1);//延时消抖
            if((GPX1.DAT & (1<<2))){
			    printf("K3 click!\n");
            }
			//先清楚中断源头的挂起
			EXT_INT41_PEND |= 1<<2; //GPX1_2
			//在清楚GIC分配器层中断挂起 与 ICDISER_CPU 结果一样 id:57
			ICDICPR.ICDICPR1 |= 1<<26; // id 58 
			break;
		case 64:
            delay(1);//延时消抖
            if((GPX3.DAT & (1<<2))){
                printf("K4 click!\n");
            }
			//先清楚中断源头的挂起
			EXT_INT43_PEND |= 1<<2; //GPX3_2
			//在清楚GIC分配器层中断挂起 与 ICDISER_CPU 结果一样 id:57
			ICDICPR.ICDICPR2 |= 1<<0; // id 64 
			break;
	//case .... 
	
	}
	//最后清除CPU中断挂起 
	//写入中断id 清除对应中断挂起
	CPU0.ICCEOIR = irq_id;
}

int main()
{
    int a = 100;
    uart_init();
	key_exti_init();

    printf("hello!a=%d\r\n",a);
    while(1);
    return 0;
}


  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值