×
单片机 > 单片机程序设计 > 详情

谈谈S5PV210的按键和中断部分

发布时间:2020-06-03 发布时间:
|

按键和中断部分

以按键触发中断为例,谈谈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寄存器上找到中断执行程序的地址。(自己补充:经过推理分析我觉着该位在中断响应后该位自动由硬件清零,因为处理中断的过程中我们并么有清零该位,只是查询的时候用了,并且软件没有清零该位并不影响程序的运行,但是一定要清零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 }


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
浅谈AVR中定时器几种工作模式