s3c2440裸机-异常中断(四. irq之外部中断)

发布时间:2023-08-09  

中断前:

中断产生后:

问题案例:
我们想实现一个按键点灯程序,我们知道有以下两种方案:

1.轮询方案:轮询检测按键的电平状态,当检测到被按下后,对应的gpio会拉低,点亮对应的led;(略)

2.中断方案:将按键配置成外部中断源,当有按键按下,触发中断,在中断服务程序(isr)中去完成点灯。


下面开始写代码:

一.中断初始化

1)中断源设置

我们用按键作为外部中断源,我们把按键对应的gpio配置成中断引脚,当按键按下,相应的gpio产生了电平跳变,就会触发外部中断。


我们想达到按下按键灯亮,松开按键灯灭这种效果(配成双边沿触发,按下的时候产生下降沿中断,进行点亮,松开产生上升沿中断,进行熄灭)。当然也可做成按一下点亮,再按一下熄灭的效果(设成单边沿触发,每来一次中断,对led电平进行一次取反)。


查看原理图如下:


我们从按键的原理图中得知,当按键没有按下时,接上拉电阻,那么按键为高电平状态。当按键按下时,电位被拉低,按键处于低电平状态。s2-s5分别对应GPF0,GPF2,GPG3,GPG11; D10-D12这3盏led所对应的gpio分别是GPF4,GPF5,GPF6.

那么我们让s2,s3,s4分别控制D10,D11,D12;s5对D10-D12同时控制(按下s5,同时点亮3个led)。

我们需要配置D10-D12的gpio为输出模式,s2-s4的gpio为外部中断模式。

打开芯片手册找到第九章 IO ports,找到对应的gpio控制寄存器,将对应的gpio配置成中断模式。

代码如下:

  1. 配置GPIO为中断引脚:

    GPG的一样,我就不放图片了。

 GPFCON &= ~((3<<0) | (3<<4)); //先把eint0和eint2这两个引脚清零

 GPFCON |= ((2<<0) | (2<<4));   //S2,S3被配置为中断引脚

 

 GPGCON &= ~((3<<6) | (3<<22));

 GPGCON |= ((2<<6) | (2<<22));   //S4,S5被配置为中断引脚


2.设置中断触发方式:

当电平从高变低时,此时表示按键按下,当电平由低变高,表示松开按键。不妨设置中断方式为双边沿触发,按下按键,触发下降沿中断,中断服务程序就可以去点亮led,反之,松开触发上升沿中断,就可以去熄灭led。


    EXTINT0 |= (7<<0) | (7<<8);     /* S2,S3 */

    EXTINT1 |= (7<<12);             /* S4 */

    EXTINT2 |= (7<<12);             /* S5 */


3.设置外部中断屏蔽寄存器EINTMASK:

从上图我们知道外部中断0-3是直接连接到中断控制器,而外部中断4-7、外部中断8-23还要经过EINTMASK,那么我们需要配置EINTMASK来打开中断的通道:

EINTMASK &= ~((1<<11) | (1<<19));    //打开外部中断通道


4.外部中断挂起寄存器EINTPEND:

当一个外部中断(EINT4-EINT23)发生后,那么相应的位会被置1, 所以中断结束后需要清除对应位。这个寄存器可以用来区分外部中断4-23的哪一个中断源。

2)中断控制器设置

我们先来看下中断控制器的总框图:

1.首先是SRCPND:用来表示哪个中断源发出了中断请求。

我们先看下中断源:

从上图我们发现外部中断有24个外部中断,除了外部中断EINT,还有定时器中断,ADC中断,UART中断等…。


我们来认识下SRCPND寄存器:(用来表示哪个(哪些)中断源已产生中断请求,中断结束后要清中断)

从上图中我们发现EINT4-7共用1bit,EINT8-23共用1bit,那么肯定有其他寄存器来区分它们,那就是EINTPEND寄存器(后面5会讲)。


2.然后到达INTMSK:(中断屏蔽寄存器)

我们需要把INTMSK寄存器配置成非屏蔽状态,默认是中断源时屏蔽的,见下图:

3.INTMOD(中断模式,是fiq还是irq)

4.Priroty:

5.INTPND:
INTPND 用来显示当前优先级最高的、正在发生的中断, 需要清除对应位。

中断发生后,SRCPND中会有bit置1,可能好几个(因为同时可能发生几个中断),这些中断会由优先级仲裁器选出一个最紧迫的,然后把INTPND中相应位置1。所以只有INTPND置1,CPU才会处理。

我们知道有可能同时出现多个中断请求,那么INTPND就挑选出当前优先级最高的、正在发生的中断。


当产生irq后,要去分辨是哪个中断源,根据不同的中断源去中断服务程序isr中做不同的事情,那么如何得知当前产生的中断是哪一个外部中断源产生的呢?那么就可以访问这个INTPND寄存器。


可是我们要去手工去解析INTPND里面的位,才能知道是哪个中断源产生了中断请求。那么有没有什么比较快捷的方式自动帮我们解析INTPND呢,直接返回中断号给我们?


当然有啦,有一个INTOFFSET寄存器的值就是代表哪个中断请求产生了,如果INTOFFSET=0表示EINT0产生了中断请求,INTOFFSET=2表示EINT2产生了中断请求。具体见下图:

我们从上图看到ENIT4-7共用一个offset, EINT8-23也共用一个offset,那么要通过访问EINTPEND寄存器来区分它们。

中断控制器设置代码入下

 GPFCON &= ~((3<<0) | (3<<4)); //先把eint0和eint2这两个引脚清零

 GPFCON |= ((2<<0) | (2<<4));   //S2,S3被配置为中断引脚

 

 GPGCON &= ~((3<<6) | (3<<22));

 GPGCON |= ((2<<6) | (2<<22));   //S4,S5被配置为中断引脚


3)中断总开关

CPSR有I位,是irq的总开关,我们需要把CPSR寄存器 bit7给清零,这是中断的总开关,如果bit7设置为1,CPU无法响应任何中断。

    /* 把bit7这一位清零 */

bic r0, r0, #(1<<7)  /* 清除I位, 使能中断 */

msr cpsr, r0


二. 中断服务程序设计

到这里中断前的初始化工作知识点就已经讲完了,当然要提前准备好led初始化工作(就是将led对应的gpio配置成输出模式,这个不讲解)。

那么中断产生后,我们之前讲过,会跳转到0x18异常向量,执行跳转指令ldr pc, =_irq,和之前的swi异常,und异常框架一样。
代码框架如下:

展开代码


1.我们在start.s中用汇编代码设置cpsr的I位,开启中断开关;

2.在main函数中初始化中断源key_eint_init,初始化中断控制器interrupt_init;

3.然后继续执行main主函数。

4.当中断产生,触发irq异常,进入0x18异常向量,执行do_irq。

do_irq实现如下(和之前的do_und, do_swi类似):


handle_irq_c函数实现如下:


void key_eint_irq(int irq)

{

unsigned int val = EINTPEND;

unsigned int val1 = GPFDAT;

unsigned int val2 = GPGDAT;

if (irq == 0) /* eint0 : s2 控制 D12 */

{

if (val1 & (1<<0)) /* s2 --> gpf6 */

{

/* 松开 */

GPFDAT |= (1<<6);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<6);

}

}

else if (irq == 2) /* eint2 : s3 控制 D11 */

{

if (val1 & (1<<2)) /* s3 --> gpf5 */

{

/* 松开 */

GPFDAT |= (1<<5);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<5);

}

}

else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */

{

if (val & (1<<11)) /* eint11 */

{

if (val2 & (1<<3)) /* s4 --> gpf4 */

{

/* 松开 */

GPFDAT |= (1<<4);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<4);

}

}

else if (val & (1<<19)) /* eint19 */

{

if (val2 & (1<<11))

{

/* 松开 */

/* 熄灭所有LED */

GPFDAT |= ((1<<4) | (1<<5) | (1<<6));

}

else

{

/* 按下: 点亮所有LED */

GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));

}

}

}


EINTPEND = val; /* 清中断 : 源头*/

}


/*INTOFFSET中哪一位被设置成1,就表示哪一个 中断源*/

void handle_irq_c(void)

{

/* 分辨中断源 */

int bit = INTOFFSET;


/* 调用对应的处理函数 */

if (bit == 0 || bit == 2 || bit == 5)  /* eint0,2,bit==5还需细分eint8_23 */

{

key_eint_irq(bit); /* 处理中断, 清中断源EINTPEND(eint11,2 eint11, eint11) */

}


/* 清中断 : 从源头开始清 */

SRCPND = (1< INTPND = (1<}


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>