ARM学习随笔(10)中断的学习

来源:互联网 发布:windows清理助手怎么样 编辑:程序博客网 时间:2024/06/05 08:55

LPC2000系列的向量中断控制器(VIC)支持32个中断请求输入,也即是支持32个中断源。这32个中断按顺序称为VIC通道0,VIC通道1,…,VIC通道31(实际上只使用了18个其他的预留)

    每一个VIC通道都支持软件中断与硬件中断,即每个中断均可由软件或硬件中断产生,软件中断与对应通道上的硬件中断是逻辑“或”的关系。软件中断可通过置位VICSoftInt寄存器相应位来产生,也可通过置位VICSoftIntClear寄存器相应位来清除。

 

    LPC2000具有3类中断:FIQ、向量IRQ和非向量IRQ。LPC2000系列可通过对VICIntSelect和VICVectCntlx(x=0,1,…,15)这两类寄存器的设置,将以上的32个中断源设置为FIQ和IRQ中断的任何一种(向量IRQ和非向量IRQ和在一起)。其中,快速中断请求FIQ具有最高优先级。建议只分配一个中断请求给FIQ以减少中断处理程序的延迟。当然,VIC支持多个FIQ中断。

 

    向量IRQ具有中等优先级。该级别最多可分配32个请求中的16个。32个请求中的任何一个都可以分配到16个向量IRQslot中的任意一个。其中,slot0具有最高优先级,而slot15则为最低优先级。

非向量IRQ具有最低优先级。一般也只分配一个中断源。

 

VIC中断设置步骤(前两步是外部中断必需的,而内部中断如定时器中断则不用设置!!):

初始化程序中:

一、管脚功能选择

PINSEL0


二、外部中断寄存器的4个寄存器的设置(其中EXTINT标志寄存器因为是达到要求自动置位,所以初始不用设置。当一个管脚选择使用外部中断功能时,对应在 EXTPOLAR 和EXTMODE 寄存器中的
位选择的电平或边沿将置位EXTINT 寄存器的中断标志
。这样来向VIC 提出中断请求,如
果管脚中断使能,则产生中断。
通过向EXTINT 寄存器的位EINT0~位EINT3 写入1 来将其清零

往这个位置写1,该位置的值就变成0了,写0就保持不变,不管写前是1还是0,很多IC的寄存器是这么设置的,这是内部结构规定的。
。电平激活方式下,
该操作只有在管脚处于无效状态时才有效。

电平激活方式下,如果管脚的EINT0 功能被选用且管脚处于有效状态时,
该位置位;边沿激活方式下,如果管脚的EINT0 功能被选用且管脚上出
现所选边沿时,该位置位。
有2 个I/O 口可选择用作EINT0 功能 (见“管脚配置”中有关P0.1 和P0.16
的描述)。
该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外
(例如,如果选择EINT0 为低电平激活且低电平在相应的管脚上出现时,
该位不能被清除;只有该管脚上的信号变为高电平时该位才能被清除  写入1以后EINT0是清零了,但是管脚还为低电平,处于有效状态,EINT0又置位了。)
一旦EINT0~EINT3 中的一位被置位并开始执行相应的代码(处理唤醒和/或外部中
断),该位必须清零。否则EINT 管脚刚触发的事件以后将不会被识别。

重要:只要执行外部中断操作模式(也就是,激活电平/边沿)的变化(包括外部中断
的初始化),那么EXTINT 寄存器中相应的位必须被清零!详细内容请见3.5.4 节“外部中
断方式寄存器(EXTMODE – 0xE01F C148)”和3.5.5 节“外部中断极性寄存器(EXTPOLAR –
0xE01F C14C)”。
例如,如果外部中断0 管脚的低电平将系统从掉电模式唤醒,为了将来还能进入掉电模
式,唤醒后的程序必须将EINT0 位复位。如果EINT0 位仍保持置位状态,后来唤醒掉电模
式的任何操作都将失败。外部中断处理也不例外。)

INTWAKE=0X00;//不作为唤醒用,为1时处理器从掉电模式唤醒   

EXTMODE =0x00;// 为0用电平激活,为1时是边沿激活
EXTPOLAR=0x00;//为低电平或下降沿有效
三、VIC的设置

1、设置VICIntSelect

    通过VICIntSelect中断选择寄存器将32个中断请求分配为FIQ或IRQ(包括向量IRQ与非向量IRQ)

相应位为1是FIQ,为0是IRQ。

例如:VICIntSelect = 0x00000000,设置所有中断都是IRQ

 

2、设置VICVectCntlx(仅对于IRQ,FIQ不需要此设置)  选择VIC通道slot并使能slot

    通过VICVectCntlx(x=0,1,…,15)来选择32个中断请求中的某个为向量IRQ并设定此中断请求为IRQ slotx(x对应于VICVectCntlx中的x)。若某个中断源被设定为IRQ,但却未通过VICVectCntlx使能,则该中断源将被默认为非向量IRQ。

例如:VICVectCntl0 = 0x20(0x20是16进制数) | 14(14由于前面没有0x就是一个十进制数,他们俩可以或),设置EINT0为向量中断,使用Slot0。其中0x20是因为VICVectCntlx寄存器第5位是使能(由表知),14是通过下表得到的EINT0的VIC通道号(便于MCU识别是谁的中断请求)。

 


3、设置VICVectAddrx(仅对于IRQ,FIQ不需要此设置)  选择跳转中断服务程序地址(其中VICVectAddr=0;表示中断结束)

当有IRQ中断产生时,VIC将会根据中断源设置VICVectAddr寄存器为相应中断服务程序的地址,切换处理器工作模式为IRQ模式,并跳转到IRQ中断入口0x00000018处;

异常中断向量表中0x00000018处使用“LDR PC, [PC, #-0xFF0]”,使得程序跳转到(0x00000018+8-0x00000FF0=0xFFFFF030)存储器处保存的地址。0xFFFFF030是VICVectAddr寄存器地址。也即是说:通过该指令,程序跳转到VICVectAddr寄存器所指向的中断服务程序的地址。

例如:VICVectAddr0 = (uint32)EINT1_Exception,设置EINT1中断地址。EINT1一般是中断服务程序函数名即服务中断服务程序的首地址,前面要用(uint32)强制类型转换。

 

4、设置VICDefVectAddr(当都设置为向量IRQ时可以没有这个)

若是非向量IRQ中断,VIC提供默认服务程序地址VICDefVectAddr,IRQ中断入口程序可通过读取VIC的向量地址寄存器VICVectAddr来取得该地址,然后跳转到相应服务程序继续执行。该默认服务程序由所有非向量IRQ公用,默认服务程序可读取IRQ状态寄存器以确定哪个IRQ被激活。

例如:VICDefVectAddr = (uint32)Default_Entry,设置非向量中断地址。如果在管理向量中断的VICVectCntl0~15和VICVectAddr0~15中没有设置某一个中断EINTx则此EINTx中断发生时,要进入非向量中断处理程序Default_Entry。

 5、  EXTINT=0X01;//通过向 EXTINT 寄存器 写入 1 来将其清零  定时器也是写入1清零

6、设置VICIntEnable使能,使能IRQ或FRQ

例如:VICIntEnable = 0x00018000,使能EINT1和EINT2。因为EINT1和EINT2的VIC通道号分别为15和16,所以15和16两位置一将EINT1和EINT2使能。

 

中断服务程序中:

1、清除相应的中断标志,以响应下一次中断,并切换处理器工作模式。建议用__irq关键字定义中断服务程序void __irq IRQ_Eint0 (void),这样的话,该函数将自动切换处理器工作模式,但该函数不能返回参数或者数值。    EXTINT=0X01;//通过向 EXTINT 寄存器 写入 1 来将其清零。

2、中断处理程序。用户自己编写的实现某种功能的中断程序。

3、退出中断前,一定要对VICVectAddr寄存器写0,通知VIC中断结束  //中断程序结束时对向量地址寄存器执行 写 “0”操作

 

【在公共中断处理函数中
 __irq __arm void irq_handler (void)        //公共中断处理函数,检查 VICVectAddr 是否为空
{
    void (*interrupt_function)();
    unsigned int vector;

/***********当只有一个中断源时可以这样处理*************************/
    vector = VICVectAddr;     // Get interrupt vector.
//如果作为非向量中断,这里把 VICDefVectAddr 赋给 vector 和把 VICVectAddr 赋给 vector 都可以,
//因为当发生非向量中断时处理器自动把 VICDefVectAddr 赋给 VICVectAddr
    interrupt_function = (void(*)())vector;
    if(interrupt_function !=NULL){
       interrupt_function();  // Call vectored interrupt function.
    }else{
        VICVectAddr = 0;      // Clear interrupt in VIC.
    }

/*********当只有一个中断源时可以这样处理*************************/


// 当有多个中断时可以通过读 VICIRQStatus 寄存器来判断是哪个中断产生了,并跳转到相应的处理函数
}

这是在IAR环境下的代码,在keil中不需要

 

具体FIQ、向量IRQ和非向量IRQ的具体设置步骤不完全相同

1、快速中断FIQ

⑴在主程序中:

1、在VICIntSelect中将中断分配为FIQ中断;

2、在VICIntEnable中使能外设中断。

⑵中断服务程序中:

1、中断处理程序;

2、清除相应的中断标志,以响应下一次中断。

2、向量IRQ

⑴主程序

1、在VICIntSelect中将中断分配为IRQ中断;

2、在VICVectCntlx中分配中断通道(优先级);

3、在VICVectAddrx中设置中断服务程序的地址;

4、通过VICIntEnable使能外设中断。

⑵中断服务程序

1、中断处理;

2、清除相应的中断标志,以响应下一次中断;

3、对VICVectAddr寄存器执行写操作(通常为0x00),结束向量中断。

3、非向量IRQ

⑴主程序

1、在VICDefVectAddr中设置中断服务程序的地址;

2、通过VICIntEnable使能外设中断。

⑵中断服务程序

1、中断处理;

2、清除相应的中断标志,以响应下一次中断;

3、对VICVectAddr寄存器执行写操作(通常为0x00),结束向量中断。


完整代码:

#include<lpc213x.h>
#include<stdio.h>
#define EINT0 14
#define led 1<<16
#define uint unsigned int


void __irq KeyIntr (void)
{
uint i;

i = IO1PIN; /* 读取当前IO1管脚 */
if ((i & led) == 0)
IO1SET = led;
else
IO1CLR = led;

/* 等待外部中断信号恢复为高电平
若信号保持为低电平,中断标志会一直置位。*/
while ((EXTINT & 0x01) != 0)
{
EXTINT = 0x01;/* 清除EINT0中断标志*/
}

VICVectAddr = 0;/* 向量中断结束*/
}
void init()
{
/*****************第一步***********/
PINSEL1=0x00000001; //P0.16选择为ENT0  但ENT0的值不是P0.16,只不过P0.16选择了ENT0功能。在P0.16为有效状态是ENT0才置位。 


/*****************第二步***********/
INTWAKE=0; //不作为唤醒使用
EXTMODE=0; //电平方式触发
EXTPOLAR=0;//低电平触发


/*****************第三步***********/
VICIntSelect   = 0x00000000;/* 设置所有中断分配为IRQ中断*/
VICVectCntl0   = 0x20 | EINT0;/* 分配中断编号,使能IRQ*/
VICVectAddr0   = (uint)KeyIntr;/* 设置中断服务程序地址*/
EXTINT         = 0x01;/* 清除EINT0中断标志*/
VICIntEnable   = 1 << EINT0;
}  


int main()
{
PINSEL2 = 0x00000000;// 管脚连接GPIO
IO1DIR  = led;/* 设置led控制口为输出,其余输入。*/
IO1SET = led;


init();
// IRQEnable();/*IAR使能IRQ中断 在VICVectCntl0的第5位以控制使能,所以可以不写*/


while (1); /* 等待中断 */
}

0 0
原创粉丝点击