第五部分:S5PV210_按键和CPU中断部分_1

按键和中断部分

  以按键触发中断为例,谈谈S5PV210的中断体系

  要使用中断,首先要做好两个部分的工作:CPU中断的初始化和相应器件的中断的初始化。CPU中断初始化:就是要设置好CPU有关中断的东西。相关器件的中断初始化:譬如我们的按键,就要设置好按键,使其一按下按键,就触发中断。

(1)我们先来说说按键的相关的中断设置

先看看按键的硬件接法:我们可以看到,按键是接在EINT2和EINT3处,还有KP_COL0-3,一共是6个按键,

然后我们看SOC的接口处,可以看到EINT2和EINT3接在了GPH0_2,3处,KP_COL0-3接在了GPH2_0-3处

然后,我们就查找GPH0和GPH2的数据手册,全部将其设置为外部中断模式(EXT_INT)模式,也就是1111,KP_COL模式是用来做矩阵键盘的。

设置好寄存器GPH0和GPH2之后,我们下面设置和外部中断相关的寄存器:EXT_INT_0_CON,EXT_INT_2_CON,EXT_INT_0_MASK,EXT_INT_2_MASK,EXT_INT_0_PEND,EXT_INT_2_PEND.

总结:也就是说按键这边,一个按键的话只需要设置好四个寄存器就可以工作了。

GPH0CON选择外部中断模式,

EXT_INT_0_CON选择怎样就触发中断(是高电平就触发中断,还是低电平,上升沿,下降沿,上升/下降沿触发中断),

EXT_INT_0_MASK:向该寄存器写0来使能中断;

EXT_INT_O_PEND:我们初始化的时候可以通过写1来进行清除中断,中断处理完之后,我们也要向这个寄存器写1来清除中断。

设置好上面这些寄存器,我们按键部分的中断初始化就设置好了。

 1 // 以中断方式来处理按键的初始化
 2 void key_init_interrupt(void)
 3 {
 4     // 1. 外部中断对应的GPIO模式设置
 5     rGPH0CON |= 0xFF<<8;        // GPH0_2 GPH0_3设置为外部中断模式
 6
 7     // 2. 中断触发模式设置
 8     rEXT_INT_0_CON &= ~(0xFF<<8);    // bit8~bit15全部清零
 9     rEXT_INT_0_CON |= ((2<<8)|(2<<12));        // EXT_INT2和EXT_INT3设置为下降沿触发
10
11     // 3. 中断允许
12     rEXT_INT_0_MASK &= ~(3<<2);            // 外部中断允许
13
14     // 4. 清挂起,清除是写1,不是写0
15     rEXT_INT_0_PEND |= (3<<2);
16 }

(2)设置CPU的中断模式

1.中断产生的时候,CPU所做的工作的大致过程

  

图片来源:http://blog.csdn.net/mr_raptor/article/details/6556195

需要注意的是:1.外部中断发生,EXT_INT_O_PEND寄存器(按键那边的寄存器)置为1,中断挂起,这就相当于告诉了CPU中断发生了,然后CPU就发生了上面图所对应的这些响应

2.中断发生后,cpsr寄存器中的IRQ中断位就置1了,所以,CPU进来处理中断后,其他硬件在这段时间内发生了中断的话,CPU是一概不理的。

根据上图:我们要完成的的东西就是:

  1.把我们的异常处理入口地址放到我们的异常向量表所对应的内存处:0x00000018

  2.上面的异常处理入口那张图所对应的过程,具体实现的代码如下。

 1 IRQ_handle:
 2     // 设置IRQ模式下的栈
 3     ldr sp, =IRQ_STACK
 4     // 保存LR
 5     // 因为ARM有流水线,所以PC的值会比真正执行的代码+8,
 6     sub lr, lr, #4
 7     // 保存r0-r12和lr到irq模式下的栈上面
 8     stmfd sp!, {r0-r12, lr}
 9     // 在此调用真正的isr来处理中断
10     bl irq_handler
11     // 处理完成开始恢复现场,其实就是做中断返回,关键是将r0-r12,pc,cpsr一起回复
12     ldmfd sp!, {r0-r12, pc}^

2.设置中断相关的寄存器,让CPU找到相应的执行程序,然后执行它(这是中断的目的:执行中断处理程序)

图片来源:http://www.docin.com/p-961251144.html

相关的寄存器有:VICnADDR  VICnINTENCLEAR  VICnINTSELECT  VICnIRQSTATUS VIC0VECTADDRn VICnINTENABLE

1.VICnADDR  :一共有四个寄存器,VIC(0-3)一人一个,用来存放我们想要的执行的中断处理程序(isr)的地址,它里面的地址是从VIC0VECTADDRn这个寄存器里面来的,当中断发生之后,VIC0VECTADDRn里面的地址就会硬件自动刷到这个寄存器上。(中断处理完成之后,我们要清除这个寄存器)

1 // 清除需要处理的中断的中断处理函数的地址
2 void intc_clearvectaddr(void)
3 {
4     // VICxADDR:当前正在处理的中断的中断处理函数的地址
5     VIC0ADDR = 0;
6     VIC1ADDR = 0;
7     VIC2ADDR = 0;
8     VIC3ADDR = 0;
9 }

2.VICnINTENCLEAR  :清中断寄存器,也就是中断处理完成之后,我们要往这个寄存器里面写1,把中断清理掉。我们也可以通过相应的中断号,来禁止那个中断。

 1 // 禁止中断
 2 // 通过传参的intnum来禁止某个具体的中断源,中断号在int.h中定义,是物理中断号
 3 void intc_disable(unsigned long intnum)
 4 {
 5     unsigned long temp;
 6
 7     if(intnum<32)
 8     {
 9         temp = VIC0INTENCLEAR;
10         temp |= (1<<intnum);
11         VIC0INTENCLEAR = temp;
12     }
13     else if(intnum<64)
14     {
15         temp = VIC1INTENCLEAR;
16         temp |= (1<<(intnum-32));
17         VIC1INTENCLEAR = temp;
18     }
19     else if(intnum<96)
20     {
21         temp = VIC2INTENCLEAR;
22         temp |= (1<<(intnum-64));
23         VIC2INTENCLEAR = temp;
24     }
25     else if(intnum<NUM_ALL)
26     {
27         temp = VIC3INTENCLEAR;
28         temp |= (1<<(intnum-96));
29         VIC3INTENCLEAR = temp;
30     }
31     // NUM_ALL : disable all interrupt
32     else
33     {
34         VIC0INTENCLEAR = 0xFFFFFFFF;
35         VIC1INTENCLEAR = 0xFFFFFFFF;
36         VIC2INTENCLEAR = 0xFFFFFFFF;
37         VIC3INTENCLEAR = 0xFFFFFFFF;
38     }
39
40     return;
41 }

3.VICnINTSELECT  :中断选择寄存器,我们在这里选择FIQ模式还是IRQ模式

 1 // 初始化中断控制器
 2 void intc_init(void)
 3 {
 4     // 禁止所有中断
 5     // 为什么在中断初始化之初要禁止所有中断?
 6     // 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr
 7     // 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码
 8     // 则程序很可能跑飞,所以不用的中断一定要关掉。
 9     // 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须
10     // 给这个中断提供相应的isr并绑定好。
11     VIC0INTENCLEAR = 0xffffffff;
12     VIC1INTENCLEAR = 0xffffffff;
13     VIC2INTENCLEAR = 0xffffffff;
14     VIC3INTENCLEAR = 0xffffffff;
15
16     // 选择中断类型为IRQ
17     VIC0INTSELECT = 0x0;
18     VIC1INTSELECT = 0x0;
19     VIC2INTSELECT = 0x0;
20     VIC3INTSELECT = 0x0;
21
22     // 清VICxADDR
23     intc_clearvectaddr();
24 }

4.VIC0VECTADDRn  :一共有128个这样的寄存器,一个中断号对应一个这样的寄存器,我们可以通过中断号和VICnADDR这个基地址来计算VIC0VECTADDRn这个寄存器的地址,然后把我们的执行程序放到这个寄存器中,这样我们就不需要定义太多的宏了。

 1 // 绑定我们写的isr到VICnVECTADDR寄存器
 2 // 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
 3 // 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
 4 // 参数:intnum是int.h定义的物理中断号,handler是函数指针,就是我们写的isr
 5
 6 // VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个
 7 // 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候
 8 // 只需要首地址+偏移量即可。
 9 void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
10 {
11     //VIC0
12     if(intnum<32)
13     {
14         *( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;
15     }
16     //VIC1
17     else if(intnum<64)
18     {
19         *( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;
20     }
21     //VIC2
22     else if(intnum<96)
23     {
24         *( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) = (unsigned)handler;
25     }
26     //VIC3
27     else
28     {
29         *( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) = (unsigned)handler;
30     }
31     return;
32 }

5.VICnIRQSTATUS  :IRQ模式下的中断状态寄存器(一共有4个),中断发生后,这个寄存器就会自动置1了,然后我们是通过判断这4个寄存器中哪个寄存器写了1,然后得知我们的中断执行程序的地址是放到了哪个VICnADDR寄存器上,最后在相应的VICADDR寄存器上找到中断执行程序的地址。

 1     unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};
 2     int i=0;
 3     void (*isr)(void) = NULL;
 4
 5     for(i=0; i<4; i++)
 6     {
 7         // 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0
 8         if(intc_getvicirqstatus(i) != 0)
 9         {
10             isr = (void (*)(void)) vicaddr[i];
11             break;
12         }
13     }
14     (*isr)();        // 通过函数指针来调用函数

6.VICnINTENABLE  :中断使能寄存器,通过中断号,来向中断使能寄存器的相应位写1,就可以使能相应的中断了

 1 // 使能中断
 2 // 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
 3 void intc_enable(unsigned long intnum)
 4 {
 5     unsigned long temp;
 6     // 确定intnum在哪个寄存器的哪一位
 7     // <32就是0~31,必然在VIC0
 8     if(intnum<32)
 9     {
10         temp = VIC0INTENABLE;
11         temp |= (1<<intnum);        // 如果是第一种设计则必须位操作,第二种设计可以
12                                     // 直接写。
13         VIC0INTENABLE = temp;
14     }
15     else if(intnum<64)
16     {
17         temp = VIC1INTENABLE;
18         temp |= (1<<(intnum-32));
19         VIC1INTENABLE = temp;
20     }
21     else if(intnum<96)
22     {
23         temp = VIC2INTENABLE;
24         temp |= (1<<(intnum-64));
25         VIC2INTENABLE = temp;
26     }
27     else if(intnum<NUM_ALL)
28     {
29         temp = VIC3INTENABLE;
30         temp |= (1<<(intnum-96));
31         VIC3INTENABLE = temp;
32     }
33     // NUM_ALL : enable all interrupt
34     else
35     {
36         VIC0INTENABLE = 0xFFFFFFFF;
37         VIC1INTENABLE = 0xFFFFFFFF;
38         VIC2INTENABLE = 0xFFFFFFFF;
39         VIC3INTENABLE = 0xFFFFFFFF;
40     }
41
42 }

至此,全部准备工作处理完成,只需要在main函数那里调用相应的函数就可以了

 1 int main(void)
 2 {    //串口初始化    uart_init();
 3     //按键的中断初始化
 4     key_init_interrupt();
 5
 6     // 如果程序中要使用中断,就要调用中断初始化来初步初始化中断控制器
 7     system_init_exception();
 8
 9     // 绑定isr到中断控制器硬件
10     intc_setvectaddr(KEY_EINT2, isr_eint2);
11
12     // 使能中断
13     intc_enable(KEY_EINT2);
14
15     return 0;
16 }

终极目标:执行中断处理程序,只要我们一按下按键,就会在串口那里打印”isr_eint2_LEFT“这句话了

 1 // EINT2通道对应的按键,就是GPH0_2引脚对应的按键,就是开发板上标了LEFT的那个按键
 2 void isr_eint2(void)
 3 {
 4     // 真正的isr应该做2件事情。
 5     // 第一,中断处理代码,就是真正干活的代码
 6     printf("isr_eint2_LEFT.\n");
 7     // 第二,清除中断挂起
 8     rEXT_INT_0_PEND |= (1<<2);
 9     intc_clearvectaddr();
10 }

参考来源:朱老师物联网大教程

时间: 2024-10-12 15:57:45

第五部分:S5PV210_按键和CPU中断部分_1的相关文章

外部按键 控制 LED 中断 (参考 http://www.oschina.net/question/565065_115196?sort=time )

转帖: http://www.oschina.net/question/565065_115196?sort=time 实验目的: mini2440开发板上有6个按键,将其中的前4个按键设为外部中断方式,当按下K1时,LED1亮:当按下K2时,LED2亮:当按下K3时,LED3亮:当按下K4时,LED4亮. 首先我们先了解一下 mini2440 按键和LED接口:     GPBCON 地址: 0x56000010 (LED 灯可以参考流水灯的随笔) 按键接口电路如图2所示,当按键没有按下时,G

7.自己写中断方式按键驱动程序

request_irq()和free_irq()分析完毕后,接下来开始编写上升沿中断的按键驱动 如下图,需要设置4个按键的EINT0, EINT2, EINT11, EINT19的模式为双边沿,且设置按键引脚为中断引脚 这里我们只需要使用request_irq函数就行了, 在request_irq函数里会初始chip->set_type(设置引脚和中断模式) 1.首先添加头文件 #include <linux/irq.h> //要用到IRQ_EINT0和IRQT_RISING这些变量 2

6.6410和210的按键中断编程

6.6410和210的按键中断编程 首先是打开6410底板原理图: 可以看到OK6410有六个按键: 可以看到OK6410的六个按键对应的引脚是KEYINT1.KEYINT2.KEYINT3.KEYINT4.KEYINT5和KEYINT6.接着在核心板的原理图里搜索这个词: 可以看到按键中断与GPN系列寄存器的引脚是互用的,接着就是在芯片手册里查看有关GPN系列寄存器的信息,重点是关注GPN控制寄存器:GPNCON: 这里我们使用到的按键对应的中断,需要配置成中断的方式,对应的GPN位设置为10

ok6410按键中断驱动程序

#include <linux/module.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/io.h> #define GPNCON 0x7F008830//ioremap使用的地址,与具体硬件相关 irqreturn_t key_in

按键中断---那些年我们一起玩mini2440(arm9)裸机

ARM中断控制系统 按键驱动程序设计 一.Arm中断控制系统 1.查询方式 程序不断地查询设备的状态,并做出相应的反应.该方式实现比较简单,常用在比较单一的系统中,比如:一个温控系统中可以实用查询的方式不断检测温度变化. 特点:实现简单:但CPU利用率很低,不适合多任务的系统. 2.中断方式 当事件发生时,硬件会设置某个寄存器:CPU在每执行完一个指令时,查看这个寄存器,如果所关注的事件发生了,则中断当前程序,跳转到一个固定的地址处理这个事件,处理完后返回到被中断的程序中继续运行. 特点:实现相

2440按键中断编程

1.其底板按键原理图 将EINT1到核心板原理图去搜 可以看出它对应GPF1这个引脚, 到s3c2440的I/O引脚去找GPF这一组. a.配置这些按键引脚,即初始化这些中断源 b.初始化中断控制器 我们的按键没有子中断,故不需要设置SUBMASK寄存器,只需要去设置MASK寄存器,对于MODE和Priority保持默认值即可. 保证EINT0.1.2.4保持可以被中断,不能被屏蔽. 在这里对于第四位对应EINT4-7,设置第四位就是设置4-7位,那么要分开对他们怎么设置呢,这时候还有一个寄存器

Linux kernel中断子系统之(五):驱动申请中断API

一.前言 本文主要的议题是作为一个普通的驱动工程师,在撰写自己负责的驱动的时候,如何向Linux Kernel中的中断子系统注册中断处理函数?为了理解注册中断的接口,必须了解一些中断线程化(threaded interrupt handler)的基础知识,这些在第二章描述.第三章主要描述了驱动申请 interrupt line接口API request_threaded_irq的规格.第四章是进入request_threaded_irq的实现细节,分析整个代码的执行过程. 二.和中断相关的lin

(zigbee学习总结二)Z-stack按键机制

本文是自己学习zigbee时的知识梳理. 参考书:<ZigBee技术与实训教程--基于CC2530的无线传感网技术>----姜仲.刘丹 编著 Z-stack中提供了两种方式采集按键数据:轮询方式和中断方式.轮询方式:每隔一定时间,检测按键状态,进行相应处理:中断方式:按键引起按键中断,进行相应处理.Zstack在默认情况下,使用轮询方式进行处理. 一.按键的宏定义 在HAL/include/hal_key.h中对按键进行了基本的定义: /* 中断使能和禁用*/ #define HAL_KEY_

基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(1)

作者:彭东林 邮箱:[email protected] QQ:405728433 平台 tiny4412 ADK Linux-4.9 概述 前面几篇博文列举了在有设备树的时候,gpio中断的用法示例.下面我们尝试分析一下Linux内核是如何做到的,如果哪写的有问题,欢迎大家批评指正,谢谢. 还是以GPIO中断为例分析,对于tiny4412,gpio中断可以分为两种,外部中断和普通的GPIO中断 外部中断:按键中断分别使用了外部中断XEINT26.XEINT27.XEINT28以及XEINT29,