谈谈S5PV210的按键和中断部分

按键和中断部分
以按键触发中断为例 , 谈谈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所做的工作的大致过程
图片:https://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找到相应的执行程序 , 然后执行它(这是中断的目的:执行中断处理程序)
图片:https://www.docin.com/p-961251144.html
相关的寄存器有:VICnADDR VICnINTENCLEARVICnINTSELECTVICnIRQSTATUS VIC0VECTADDRn VICnINTENABLE
1.VICnADDR :一共有四个寄存器 , VIC(0-3)一人一个 , 用来存放我们想要的执行的中断处理程序(isr)的地址 , 它里面的地址是从VIC0VECTADDRn这个寄存器里面来的 , 当中断发生之后 , VIC0VECTADDRn里面的地址就会硬件自动刷到这个寄存器上 。(中断处理完成之后 , 我们要清除这个寄存器)
1 // 清除需要处理的中断的中断处理函数的地址
【谈谈S5PV210的按键和中断部分】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寄存器上找到中断执行程序的地址 。(自己补充:经过推理分析我觉着该位在中断响应后该位自动由硬件清零 , 因为处理中断的过程中我们并么有清零该位 , 只是查询的时候用了 , 并且软件没有清零该位并不影响程序的运行 , 但是一定要清零PEND相应和4个VICnADDR寄存器 , 至于为什么要清零VICnADDR寄存器 , 还没有搞清楚 , 如果不清除这几个寄存器 , 中断会响应 , 然后就不再响应了)
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 }

    推荐阅读