嵌入式-ARM-学习总结(7):按键与中断

  • 一、按键
    • 1.S5PV210的按键
    • 2.按键的2中相应方法:轮询方式和中断方式
    • 3.按键对应的GPIO模式设置
    • 4.轮询方式处理按键的程序流程
    • 5.代码编写
    • 6.按键消抖
  • 二、中断
    • 1.什么是中断
    • 2.为什么需要中断
    • 3.异常向量表
    • 4.异常和中断的区别和联系
    • 5.为什么中断处理要先在汇编中进行
    • 6.中断的基本过程
    • 7.S5PV210中断处理的主要寄存器
  • 三、中断代码
    • (1)绑定中断异常向量表
    • (2)初始化中断控制器的基本寄存器
    • (3)绑定我们写的isr到VICnVECTADDR寄存器
    • (4)使能中断
    • (5)中断处理函数
    • (6)start.S文件
  • 四、外部中断
    • 1.什么是外部中断
    • 2.电平触发和边沿触发
    • 3.配置按键为外部中断触发模式
    • 4.中断发生处理

一、按键

1.S5PV210的按键

对于LED理解为输出,按键理解为输入。
串口是既可以输入又可以输出。

通过原理图搜索EINT2,得

同理,找到5个按键对应的GPIO口
所以:SW5对应GPH0_2,SW6:GPH0_3,SW7:GPH2_0
SW8:GPH2_1,SW9:GPH2_2,SW10:GPH2_3
原理图上可以看出:按下时是低电平,弹起时是高电平

2.按键的2中相应方法:轮询方式和中断方式

(1)轮询方式:就是SoC主动的每隔一段时间去读取(按键所对应的)GPIO的电平高低,以此获得按键信息;缺点在于CPU要一直注意按键事件,会影响CPU做其他事情。
(2)中断方式:就是SoC事先设定好GPIO触发的中断所对应的中断处理程序ISR,当外部按键按下或弹开时会自动触发GPIO对应的外部中断,导致ISR执行,从而自动处理按键信息。

3.按键对应的GPIO模式设置

(1)按键接到GPIO上,按键按下还是弹起,决定外部电路的接通与否,从而决定这个GPIO引脚的电压是高还是低;这个电压可以作为这个GPIO引脚的输入信号,此时GPIO配置为输入模式,即可从SoC内部读取该引脚的电平为1还是0(1对应高电平,0对应低电平)。
(2)GPH0CON(0xE0200C00) GPH2DAT(0xE0200C04) GPH2CON(0xE0200C40) GPH2DAT(0xE0200C44)
(3)应该在CON寄存器中将GPIO设置为input模式,然后去读取DAT寄存器(读取到的相应位的值为1表示外部是高电平(对应按键弹起),读取到的位的值为0表明外部是低电平(按键按下))

4.轮询方式处理按键的程序流程

第一步:先初始化GPIO模式为input;
第二步:循环读取GPIO的电平值,然后判断有无按键按下

5.代码编写

查看数据手册中的GPIO部分

对于每一位,被按下,就置位为1.

// 定义操作寄存器的宏
#define GPH0CON     0xE0200C00
#define GPH0DAT     0xE0200C04#define GPH2CON       0xE0200C40
#define GPH2DAT     0xE0200C44#define rGPH0CON  (*(volatile unsigned int *)GPH0CON)
#define rGPH0DAT    (*(volatile unsigned int *)GPH0DAT)
#define rGPH2CON    (*(volatile unsigned int *)GPH2CON)
#define rGPH2DAT    (*(volatile unsigned int *)GPH2DAT)// 初始化按键
void key_init(void)
{// 设置GPHxCON寄存器,设置为输入模式// GPH0CON的bit8~15全部设置为0,即可rGPH0CON &= ~(0xFF<<8);// GPH2CON的bit0~15全部设置为0,即可rGPH2CON &= ~(0xFFFF<<0);
}void key_polling(void)
{// 依次,挨个去读出每个GPIO的值,判断其值为1还是0.如果为1则按键按下,为0则弹起// 轮询的意思就是反复循环判断有无按键,隔很短时间while (1){// 对应开发板上标着LEFT的那个按键if (rGPH0DAT & (1<<2)){// 为1,说明没有按键led_off();}else{// 为0,说明有按键led1();}// 对应开发板上标着DOWN的那个按键if (rGPH0DAT & (1<<3)){// 为1,说明没有按键led_off();}else{// 为0,说明有按键led2();}// 对应开发板上标着UP的那个按键if (rGPH2DAT & (1<<0)){// 为1,说明没有按键led_off();}else{// 为0,说明有按键led3();}}
}

6.按键消抖

(1)按键这种物理器件本身会有抖动信号抖动信号指的是在电平由高到低(也就是按键按下时)或者电平由低到高(也就是按键弹起时)过程中,电平的变化不是立刻变化,而是经过了一段时间的不稳定期才完成变化,在这个不稳定期间电平可能会时高时低反复变化,这个不稳定期就叫抖动(抖动期内获取按键信息是不可靠的,要想办法消抖)。
(2)什么叫消抖消抖就是用硬件或者软件方法来尽量减小抖动期对按键获取的影响。消抖常用2种思路:第一是硬件消抖,消抖思路就是尽量减小抖动时间,方法是通过硬件添加电容等元件来减小抖动;第二是软件消抖,消抖思路是发现一次按键按下/弹起事件后,不立即处理按键,而是延时一段时间(一般10~20ms,这就是消抖时间)后再次获取按键键值,如果此次获取和上次一样是按下/弹起,那就认为真的按下/弹起了。
(3)一般比较精密需要的时候,需要硬件消抖软件消抖一起配合。

二、中断

1.什么是中断

中断的发明是用来解决宏观上的并行需要的。宏观就是从整体上来看,并行就是多件事情都完成了。微观上的并行,就是指的正在的并行,就是精确到每一秒甚至每一刻,多少事情都是在同时进行的。宏观上面的并行并不等于微观的并行,有时候宏观上是并行的,微观上是串行的。
例子:
一个人在看电影,快递来了暂停电影跑去收快递,收完快递继续回来看电影,这个例子就是宏观上的并行和微观上的串行。例子中一个人等同于SoC中1个CPU(也就是单核CPU),这个CPU看电影就不能收快递,收快递就不能看电影(也就是说不能真正的并行)。单核心CPU在微观角度是串行的,但是因为CPU很快,所以在宏观看来可以并行。

2.为什么需要中断

因为单核CPU实际无法并行的,但是通过中断机制,可以实现假并行(宏观上的并行,微观上实际还是串行的)。

3.异常向量表

异常向量表是SoC对中断的实现机制。
异常向量表是CPU中某些特定地址的特定定义。当中断发生的时候,中断要想办法通知CPU去处理中断,怎么做到?这就要靠异常向量表
在CPU设计时,就事先定义了CPU中一些特定地址作为特定异常的入口地址。例如定义0x00000000地址为复位异常向量地址,则发生复位异常时CPU会自动跳转到0x00000000地址去执行指令。
以上讲的是CPU硬件设计时对异常向量表的支持,但是需要软件的支持。硬件已经决定了发生什么异常CPU自动跳转PC到哪个地址去执行,软件需要做的就是把处理这个异常的代码的首地址填入这个异常向量地址

4.异常和中断的区别和联系

针对SoC来说,发生复位、软中断、中断、快速中断、取指令异常、数据异常等,我们都统一叫异常。所以说:中断其实是异常的一种。

异常的定义就是突发事件,打断了CPU的正常常规业务,CPU不得不跳转到异常向量表中去执行异常处理程序;中断是异常的一种,一般特指SoC内的内部外设产生的打断SoC常规业务,或者外部中断(SoC的GPIO引脚传回来的中断)。

5.为什么中断处理要先在汇编中进行

(1)中断处理要注意保护现场(中断从SVC模式来,则保存SVC模式下的必要寄存器的值)和恢复现场(中断处理完成后,准备放回SVC模式前,要将保存的SVC模式下的必要寄存器的值恢复回去,不然到了SVC模式后寄存器的值乱了,SVC模式下原来正在进行的常规任务就被搞坏了)
(2)保存现场包括:第一:设置IRQ栈;第二,保存LR;第三,保存R0~R12
(3)为什么要保存LR寄存器?要考虑中断返回的问题。中断ISR执行完后如何返回SVC模式下去接着执行原来的代码。中断返回其实取决于我们进入中断时如何保存现场。中断返回时关键的2个寄存器就是PC和CPSR。所以我们在进入IRQ模式时,应该将SVC模式下的下一句指令的地址(中断返回地址)和CPSR保存起来,将来恢复时才可以将中断返回地址给PC,将保存的CPSR给CPSR。
(4)中断返回地址就保存在LR中,而CPSR(自动)保存在(IRQ模式下的)SPSR中

汇编保存现场和恢复现场
保护现场关键是保存:中断处理程序的返回地址,r0-r12(cpsr是自动保存的)
恢复现场主要是恢复:r0-r12,pc,cpsr

6.中断的基本过程

中断处理一般分为两个阶段。

阶段一:异常向量表阶段
第一个阶段之所以能够进行,主要依赖于CPU设计时提供的异常向量表机制。第一个阶段的主要任务是从异常发生到响应异常并且保存/恢复现场、跳转到真正的异常处理程序处。
阶段二:处理中断阶段
第二个阶段的目的是识别多个中断源中究竟哪一个发生了中断,然后调用相应的中断处理程序来处理这个中断。
小阶段1:查询中断状态寄存器来确定在哪里发生了中断。
**小阶段2:**通过ISR函数指针来执行对应的中断响应。

7.S5PV210中断处理的主要寄存器

(1)VICnINTENABLE和VICnINTENCLEAR
VICnINTENABLE 对应interrupt enable,负责相应的中断的使能,意思就是启用这个中断,当硬件产生中断时CPU能接收的到,某个中断时,只要在这个中断编号对应的VICnINTENABLE的相应bit位写1即可(注意这个位写1其他位写0对其他位没有影响)

INTENCLEAR对应interrupt enable clear,负责相应的中断的禁止,如果我们想禁止某个中断源时,只要向VICnINTENCLEAR中相应的位写1即可。

(2)VICINTSELECT

设置各个中断的模式为irq还是fiq。一般都设置成irq
IRQ和FIQ究竟有何区别。210中支持2种中断,irq和fiq。irq是普通中断,fiq是快速中断。快速中断提供一种更快响应处理的中断通道,用于对实时性要求很高的中断源。fiq在CPU设计时预先提供了一些机制保证fiq可以被快速处理,从而保证实时性。fiq的限制就是只能有一个中断源被设置为fiq,其他都是irq。

CPU如何保证fiq比irq快?
有2个原因:第一,fiq模式有专用的r8~r12,因此在fiq的isr中可以直接使用r8-r12而不用保存,这就能节省时间;第二,异常向量表中fiq是最后一个异常向量入口。因此fiq的isr不需要跳转,可以直接写在原地,这样就比其他异常少跳转一次,省了些时间。(可以看3.异常向量表中的图)

(3)VICIRQSTATUSVICFIQSTATUS

中断状态寄存器,是只读的。当发生了中断时,硬件会自动将该寄存器的对应位置为1,表示中断发生了。软件在处理中断第二阶段的第一阶段,就是靠查询这个寄存器来得到中断编号的。

(4)VICnVECTPRIORITY0~VICnVECTPRIORITY31
中断优先级设置寄存器,设置多个中断同时发生时先处理谁后处理谁的问题。一般来说高优先级的中断可以打断低优先级的中断,从而嵌套处理中断。当然了有些硬件/软件可以设置不支持中断嵌套。

(5)VICnVECTADDR0~VICnVECTADDR31、VICnADDR


这几个寄存器和210中断处理第二阶段的第二阶段有关。
VICnVECTADDR0到31这32个寄存器分别用来存放真正的各个中断对应的isr的函数地址。相当于每一个中断源都有一个VECTADDR寄存器,程序员在设置中断的时候,把这个中断的isr地址直接放入这个中断对应的VECTADDR寄存器即可。
S5PV210中因为支持的中断源很多,所以直接设计了4个中断寄存器(也就是VICVECTADDR),每个32位,每位对应一个中断源。(理论上210最多支持128个中断,实际支持不足128个,有些位是空的);210没有子中断寄存器,每个中断源都是并列的。当中断发生时,在irq_handler中依次去查询4个中断源寄存器,看哪一个的哪一位被置1,则这个位对应的寄存器就发生了中断,即找到了中断编号。

VICnADDR这个寄存器是只需要读的,它里面的内容是由硬件自动设置的。当发生了相应中断时,硬件会自动识别中断编号,并且会自动找到这个中断的VECTADDR寄存器,然后将其读出复制到VICnADDR中,供我们使用 。 这样的设计避免了软件查找中断源和isr,节省了时间,提高了210的中断响应速度。
210开拓了一种全新的寻找ISR的机制:210提供了很多寄存器来解决每个中断源对应ISR的寻找问题,当发生相应中断时,硬件会自动的将相应ISR推入一定的寄存器中(也就是VICADDR),我们软件只要去这个寄存器中执行函数就行了。

三、中断代码

(1)绑定中断异常向量表

void system_init_exception(void)
{// 第一阶段处理,绑定异常向量表r_exception_reset = (unsigned int)reset_exception;r_exception_undef = (unsigned int)undef_exception;r_exception_sotf_int = (unsigned int)sotf_int_exception;r_exception_prefetch = (unsigned int)prefetch_exception;r_exception_data = (unsigned int)data_exception;r_exception_irq = (unsigned int)IRQ_handle;r_exception_fiq = (unsigned int)IRQ_handle;// 初始化中断控制器的基本寄存器intc_init();
}

(2)初始化中断控制器的基本寄存器

// 初始化中断控制器
void intc_init(void)
{// 禁止所有中断// 为什么在中断初始化之初要禁止所有中断?// 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr// 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码// 则程序很可能跑飞,所以不用的中断一定要关掉。// 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须// 给这个中断提供相应的isr并绑定好。VIC0INTENCLEAR = 0xffffffff;VIC1INTENCLEAR = 0xffffffff;VIC2INTENCLEAR = 0xffffffff;VIC3INTENCLEAR = 0xffffffff;// 选择中断类型为IRQVIC0INTSELECT = 0x0;VIC1INTSELECT = 0x0;VIC2INTSELECT = 0x0;VIC3INTSELECT = 0x0;// 清VICxADDRintc_clearvectaddr();
}

(3)绑定我们写的isr到VICnVECTADDR寄存器

// 绑定我们写的isr到VICnVECTADDR寄存器
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
// 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
// 参数:intnum是int.h定义的物理中断号,handler是函数指针,就是我们写的isr// VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个
// 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候
// 只需要首地址+偏移量即可。
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
{//VIC0if(intnum<32){*( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;}//VIC1else if(intnum<64){*( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;}//VIC2else if(intnum<96){*( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) = (unsigned)handler;}//VIC3else{*( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) = (unsigned)handler;}return;
}

(4)使能中断

// 使能中断
// 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
void intc_enable(unsigned long intnum)
{unsigned long temp;// 确定intnum在哪个寄存器的哪一位// <32就是0~31,必然在VIC0if(intnum<32){temp = VIC0INTENABLE;temp |= (1<<intnum);        // 如果是第一种设计则必须位操作,第二种设计可以// 直接写。VIC0INTENABLE = temp;}else if(intnum<64){temp = VIC1INTENABLE;temp |= (1<<(intnum-32));VIC1INTENABLE = temp;}else if(intnum<96){temp = VIC2INTENABLE;temp |= (1<<(intnum-64));VIC2INTENABLE = temp;}else if(intnum<NUM_ALL){temp = VIC3INTENABLE;temp |= (1<<(intnum-96));VIC3INTENABLE = temp;}// NUM_ALL : enable all interruptelse{VIC0INTENABLE = 0xFFFFFFFF;VIC1INTENABLE = 0xFFFFFFFF;VIC2INTENABLE = 0xFFFFFFFF;VIC3INTENABLE = 0xFFFFFFFF;}}

(5)中断处理函数

// 真正的中断处理程序。意思就是说这里只考虑中断处理,不考虑保护/恢复现场
void irq_handler(void)
{printf("irq_handler.\n");// SoC支持很多个(在低端CPU例如2440中有30多个,在210中有100多个)中断// 这么多中断irq在第一个阶段走的是一条路,都会进入到irq_handler来// 我们在irq_handler中要去区分究竟是哪个中断发生了,然后再去调用该中断// 对应的isr。// 虽然硬件已经自动帮我们把isr放入了VICnADDR中,但是因为有4个,所以我们必须// 先去软件的检查出来到底哪个VIC中断了,也就是说isr到底在哪个VICADDR寄存器中unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};int i=0;void (*isr)(void) = NULL;for(i=0; i<4; i++){// 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0if(intc_getvicirqstatus(i) != 0){isr = (void (*)(void)) vicaddr[i];break;}}(*isr)();       // 通过函数指针来调用函数//isr();          // 正确的,和上面的一个效果//*isr();     // 错误
}

(6)start.S文件

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

整个中断的流程梳理:
整个中断的工作分为2部分:
第一部分是我们为中断响应而做的预备工作:
1. 初始化中断控制器
2. 绑定写好的isr到中断控制器
3. 相应中断的所有条件使能
第二部分是当硬件产生中断后如何自动执行isr:
1. 第一步,经过异常向量表跳转入IRQ/FIQ的入口
2. 第二步,做中断现场保护(在start.S中),然后跳入isr_handler
3. 第三步,在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去这个VIC的ADDR寄存器中取isr来执行即可。
4. 第四步,isr执行完,中断现场恢复,直接返回继续做常规任务。

四、外部中断

1.什么是外部中断

SoC支持的中断类型中有一类叫外部中断
内部中断就是指的中断源来自于SoC内部(一般是内部外设),譬如串口、定时器等部件产生的中断;
外部中断是SoC外部的设备,通过外部中断对应的GPIO引脚产生的中断。
按键在SoC中就使用外部中断来实现。具体实现方法是:将按键电路接在外部中断的GPIO上,然后将GPIO配置为外部中断模式。 此时人通过按按键改变按键电路的电压高低,这个电压高低会触发GPIO对应的外部中断,通过引脚传进去给CPU处理。

2.电平触发和边沿触发

(1)外部中断的触发模式主要有2种:电平触发边沿触发
(2)电平触发就是说GPIO上的电平只有满足条件,就会不停触发中断。电平触发分为高电平触发低电平触发
(3)边沿触发分为上升沿触发下降沿触发双边沿触发三种。边沿触发不关心电平常规状态,只关心电平变化的瞬间(边沿触发不关心电平本身是高还是低,只关心变化是从高到低还是从低到高的这个过程)。

分析按键的工作:如果我们关注的是按键按下和弹起这两个事件本身,那么应该用边沿触发来处理按键;如果我们关心的是按键按下/弹起的那一段时间,那么应该用电平触发

3.配置按键为外部中断触发模式

本实验采用S5PV210板卡上的按键来触发外部中断,选用板卡上的LEFT、DOWN、RIGHT按键作为触发源。通过调试可得,该3个按键对应的寄存器为GPH0_2、GPH0_3、GPH2_1。

// 以中断方式来处理按键的初始化
void key_init_interrupt(void)
{// 1. 外部中断对应的GPIO模式设置rGPH0CON |= 0xFF<<8;       // GPH0_2 GPH0_3设置为外部中断模式rGPH2CON |= 0xFFFF<<0;      // GPH2_0123共4个引脚设置为外部中断模式// 2. 中断触发模式设置rEXT_INT_0_CON &= ~(0xFF<<8);    // bit8~bit15全部清零rEXT_INT_0_CON |= ((2<<8)|(2<<12));       // EXT_INT2和EXT_INT3设置为下降沿触发rEXT_INT_2_CON &= ~(0xFFFF<<0);rEXT_INT_2_CON |= ((2<<0)|(2<<4)|(2<<8)|(2<<12));    // 3. 中断允许rEXT_INT_0_MASK &= ~(3<<2);            // 外部中断允许rEXT_INT_2_MASK &= ~(0x0f<<0);// 4. 清挂起,清除是写1,不是写0rEXT_INT_0_PEND |= (3<<2);rEXT_INT_2_PEND |= (0x0F<<0);
}

编写ISR程序:

// EINT2通道对应的按键,就是GPH0_2引脚对应的按键,就是开发板上标了LEFT的那个按键
void isr_eint2(void)
{// 真正的isr应该做2件事情。// 第一,中断处理代码,就是真正干活的代码printf("isr_eint2_LEFT.\n");// 第二,清除中断挂起rEXT_INT_0_PEND |= (1<<2);intc_clearvectaddr();
}
// 清除需要处理的中断的中断处理函数的地址
void intc_clearvectaddr(void)
{// VICxADDR:当前正在处理的中断的中断处理函数的地址VIC0ADDR = 0;VIC1ADDR = 0;VIC2ADDR = 0;VIC3ADDR = 0;
}

4.中断发生处理

第一步:产生中断
第二步:查找异常向量表
在CPU发生中断后,由于设置为了IRQ模式,因此CPU会自动执行IRQ模式下的一系列操作,包括设置栈,保存LR、R0-R12、CPSR(相当于保存当前执行进度)。

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

第三步:查找对应中断的VIC

// 使能中断
// 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
void intc_enable(unsigned long intnum)
{unsigned long temp;// 确定intnum在哪个寄存器的哪一位// <32就是0~31,必然在VIC0if(intnum<32){temp = VIC0INTENABLE;temp |= (1<<intnum);        // 如果是第一种设计则必须位操作,第二种设计可以// 直接写。VIC0INTENABLE = temp;}else if(intnum<64){temp = VIC1INTENABLE;temp |= (1<<(intnum-32));VIC1INTENABLE = temp;}else if(intnum<96){temp = VIC2INTENABLE;temp |= (1<<(intnum-64));VIC2INTENABLE = temp;}else if(intnum<NUM_ALL){temp = VIC3INTENABLE;temp |= (1<<(intnum-96));VIC3INTENABLE = temp;}// NUM_ALL : enable all interruptelse{VIC0INTENABLE = 0xFFFFFFFF;VIC1INTENABLE = 0xFFFFFFFF;VIC2INTENABLE = 0xFFFFFFFF;VIC3INTENABLE = 0xFFFFFFFF;}}
// 真正的中断处理程序。意思就是说这里只考虑中断处理,不考虑保护/恢复现场
void irq_handler(void)
{printf("irq_handler.\n");// SoC支持很多个(在低端CPU例如2440中有30多个,在210中有100多个)中断// 这么多中断irq在第一个阶段走的是一条路,都会进入到irq_handler来// 我们在irq_handler中要去区分究竟是哪个中断发生了,然后再去调用该中断// 对应的isr。// 虽然硬件已经自动帮我们把isr放入了VICnADDR中,但是因为有4个,所以我们必须// 先去软件的检查出来到底哪个VIC中断了,也就是说isr到底在哪个VICADDR寄存器中unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};int i=0;void (*isr)(void) = NULL;for(i=0; i<4; i++){// 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0if(intc_getvicirqstatus(i) != 0){isr = (void (*)(void)) vicaddr[i];break;}}(*isr)();       // 通过函数指针来调用函数
}
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
{//VIC0if(intnum<32){*( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;}//VIC1else if(intnum<64){*( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;}//VIC2else if(intnum<96){*( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) = (unsigned)handler;}//VIC3else{*( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) = (unsigned)handler;}return;
}

由于之前已经将中断位置与中断物理标号进行了绑定,因此CPU会根据中断的物理编号去判断是哪个VIC上发生了中断,并使能该VIC。由于开始已经将ISR函数的地址与对应的VERTADDR进行了绑定,当发生相应中断时,硬件会自动的将相应ISR推入到VICADDR寄存器中,我们软件只要去这个寄存器中执行函数就行了。

第四步:执行中断处理程序
bl irq_handler

通过VICADDR寄存器,CPU访问到了ISR函数,然后执行对应的中断处理程序。执行完程序之后,恢复现场,使得程序继续正常运行,并清理掉挂起,以防对下次发生中断时造成影响。

嵌入式-ARM-学习总结(7):按键与中断相关推荐

  1. ARM之S5pv210的按键和中断部分

    一.按键和中断分析 要使用中断,首先要做好两个部分的工作:CPU中断的初始化和相应器件的中断的初始化. CPU中断初始化:就是要设置号CPU有关中断的东西. 相关器件的中断初始化:例如按键,就要设置好 ...

  2. 嵌入式Arm学习笔记(3)

    ARM指令集 精简指令集设计理念(The RISC design philosophy) 设计原则(Design rules) 指令"短小精悍" 应用流水线技术设计 大量采用寄存器 ...

  3. 嵌入式arm学习总结(一)--电子基础知识

     2011年参加了linux-arm嵌入式培训班,虽然学费比较贵,但对于个人教育的投资,我感觉还是非常值的  .今天无意翻到了培训时候写的一些笔记,在此和大家分享一下,也作为自己的一个存档吧! ( ...

  4. linux 按键驱动中断 rockchip_嵌入式linux快速入门(转载)

    从上图可以知道: ① 组成:嵌入式Linux系统 = bootloader + linux内核 + 根文件系统(里面含有APP). ② bootloader:它的目的是启动内核,去哪读内核?读到哪里? ...

  5. ARM学习(12)基于arm架构的嵌入式操作系统理解

    ARM学习(12)基于arm架构的嵌入式操作系统理解 笔者来聊聊指令集的理解 这里写自定义目录标题 ARM学习(12)基于arm架构的嵌入式操作系统理解 symbol 符号表认识 symbol符号表的 ...

  6. 基于STM32的按键、中断学习

    问题咨询及项目源码下载请加群: 群名:IT项目交流群 群号:245022761 STM32单片机学习篇3:按键 外部中断 封装 ===================  条件运算符: (三目运算符) ...

  7. ARM嵌入式开发学习,粤嵌提出合理的学习方向

    学习ARM嵌入式有很多途径,一部分人通过自学,还有一部分人通过参加相关ARM嵌入式培训班学习.下面是粤嵌主要针对自学人群提出的关于学习ARM嵌入式开发的一点建议,希望对大家有所帮助! 学习ARM,从硬 ...

  8. 嵌入式STM32—第三天按键中断key实现控制led灯

    1.软件及工程环境搭建 就不介绍了,可以直接用现成的 链接:https://pan.baidu.com/s/1GaSYpNm6yh-lZZmqRdXxvQ 提取码:dy63 相关库文件 链接:http ...

  9. 嵌入式Linux学习入门

    大四毕业了,签了一份嵌入式开发的工作,现在准备入门了,搜集一些要学习的内容. 嵌入式开发学习路线 51单片机,arm,stm32在单片机上 在单片机上编程c语言和在嵌入式系统写c语言,有很大不同 gc ...

最新文章

  1. 《深入理解java虚拟机》学习笔记四/垃圾收集器GC学习/一
  2. 3亿Docker容器部署的挑战及应对方案
  3. 洛谷 P1469 找筷子
  4. 第七节:语法总结(1)(自动属性、out参数、对象初始化器、var和dynamic等)
  5. (转)实现AI中LOD优化技术
  6. 25 张图,一万字,拆解 Linux 网络包发送过程
  7. java 万年历接口
  8. 神舟战神调节风扇热键_神舟HASEE笔记本电脑开机进入BIOS的方法与bios设置图解(F2)...
  9. 不到100行代码制作各种证件照
  10. IPTV 行业机顶盒EPG关键点
  11. 猫、路由器、交换机的区别
  12. 关于在win8下面安装虚拟机出现的一些问题
  13. springboot前后端分离,邮箱/手机验证号激活和登录,手机注册和激活
  14. 基于Redis的分布式锁真的安全吗?
  15. python中jieba库的安装
  16. shutter截图编辑功能
  17. AI人工智能学习准备之路线图
  18. 复习一下,? extends T 和 ? super T
  19. 在.net里编写Windows Service
  20. MySQL中查询某年某月的数据

热门文章

  1. 修改mac终端的前缀
  2. 【算法设计与分析】-- 分治法
  3. 将map转换成json字符串
  4. Lammps压力控制之活塞控压—一个代码循环限域空间内的气体分子数
  5. php服务器环境配置
  6. 麻将牌技法--收藏慢慢学
  7. 【ZZULIOJ】1011: 圆柱体表面积
  8. 什么情况下会出现笛卡尔积
  9. 自动驾驶——模型预测控制(MPC)理解与实践
  10. MATLAB如何绘制每组数量不一致柱状图