×
嵌入式 > 嵌入式开发 > 详情

第5课:ARM的中断

发布时间:2020-07-09 发布时间:
|
中断分为硬件中断和软件中断SWI

而硬件中断有内部中断,即中断控制器中所列出的那些中断。而外部中断在中断控制器里列的是EINT。

总共有60个中断源,而在中断控制器中列出32个,还有一些子中断在子中断控制器里列出。这些控制器我们都是通过寄存器的某一位来控制。

中断发生之后的流程:

1把下一条指令的地址放入LR,不过PC是5级流水线,所以LR的值要减去4才是正真的下一条指令的地址。

2把cpsr复制到中断模式的spsr

3pc设置到0x18的位置,这个位置是中断向量表,他指引程序跳转到中断处理程序。而中断处理程序的作用是1:保存上下文2:继续跳转到中断服务程序。

4退出的时候要恢复上下文,最重要的是把中断的spsr复制到cpsr

以下介绍下用到的寄存器:

1 SRCPND 它只要发送一次中断,该相应位就变成1。即使那个中断源被屏蔽了,这个位还是置1.

2 SUBSRCPND 子中断源未决寄存器。

3 INTMSK 中断源屏蔽

4 INTSUBMSK 子中断源屏蔽

5 INTOFFSET 读这个寄存器 方便读出是哪个中断,用10进制表示。

以下来做个例子:

在主程序中小灯一直闪,然后串口接受到数据,产生中断,然后回显数据。

这个crt0.s 是文件的入口,该程序放在0x0的位置,所以一开始进入的是中断向量表

.extern main
.extern EINT_Handle
.externinit_irq
.text
.global _start
_start:
bReset
HandleUndef:
bHandleUndef
HandleSWI:
bHandleSWI
HandlePrefetchAbort:
bHandlePrefetchAbort
HandleDataAbort:
bHandleDataAbort
HandleNotUsed:
bHandleNotUsed

bHandleIRQ@0x18

HandleFIQ:
bHandleFIQ

Reset:
ldr r0, =0x53000000 @ WATCHDOG close
mov r1, #0x0
str r1, [r0]
msr cpsr_c, #0xd2 @进入中断模式
ldr sp, =3072 @设置中断模式的sp
msr cpsr_c, #0xd3 @进入svc模式
ldr sp, =4096
bl init_irq
msr cpsr_c, #0x53 @把svc的i位置0

ldr lr, =halt_loop @设置返回地址
ldr pc, =main @ 进入主程序

HandleIRQ:@中断处理程序
sub lr, lr, #4
stmdb sp!, { r0-r12,lr } @ 保存环境
ldr lr, =int_return

ldr pc, =ISR_Handle @进入中断服务程序
int_return:
ldmia sp!, { r0-r12,pc }^ @恢复环境
halt_loop:
bhalt_loop
这个是程序的主框架。

以下是中断服务程序int.c

#include "addr.h"

void init_irq()
{
INTMSK = ~(1<<28);
INTMOD = 0;
INTSUBMSK = 0xfffffffe;
}

void EINT_Handle()
{
unsigned long oft = INTOFFSET;
if(oft == 28){
if(SUBSRCPND & 1){
UTXH0 = URXH0;//这里RX里面的数据一定要读走,不然中断有问题。不然会根据RX interpute type 选择模式的不同有不同的效果。
SUBSRCPND |= 1;//这里清中断,要置1,清中断有一定顺序。从src子到src再到int
SRCPND |= 1< INTPND |= 1<}
}

}

以下是main主程序:主要初始化灯和串口,执行小灯闪

#include"addr.h"
#define UART_CLK 50000000
#define UART_BAUD_RATE 115200
#define UART_BRD (int)(UART_CLK/(UART_BAUD_RATE *16))-1
void wait(int time)
{
do{
time--;
}
while(time>0);
}

void init_uart()
{
GPHCON |=0;
GPHUP = 0x0c;
ULCON0 = 0x3;
UCON0 = 0x5+ (1<<8);
UFCON0 = 0;
UMCON0 = 0;
UBRDIV0 = UART_BRD;
}

void init_led()
{
GPECON = GPE12_out|GPE13_out;
}

void uart_write(char *data)
{
while (*data != \0) {
while (!(UTRSTAT0 & 0x4));
UTXH0 = *data;
data++;
}
}

int main()
{
init_uart();
init_led();
uart_write("This is in main ");
while(1){
GPEDAT = 0;
wait(300000);
GPEDAT = (3<<12);
wait(300000);
}
return 0;
}

这里省略了addr.h makefile int.lds文件。

以下再也一个外部中断的例子,即用到EINT这几位。我们通过开发板上的6个按键 上下左右 ab 按下,通过串口输出key up key down。

crt0.s还是那个文件

但中断服务程序 和 中断初始化的东西不同了

#include "addr.h"

void init_irq()
{
INTMOD = 0;
GPFCON = 0xaa;//初始化6个按键的引脚到 INT模式
GPGCON = 0xa;
EXTINT0 = 0x4444;//设为上跳沿,触发中断
EXTINT1 = 0x44;
INTMSK = ~(0x2f); //设置中断
EINTMASK = ~(0x30f);//设置子中断

}

void EINT_Handle()
{
unsigned long oft = INTOFFSET;//有4个按键用EINT0 EINT1 EINT2 EINT3
unsigned long val = EINTPEND; //有2个按键是共用EINT5,所以在这个寄存器里能区分它们
switch (oft)
{
case 0:
uart_write("key b\r\n");break;
case 1:
uart_write("key a\r\n");break;
case 2:
uart_write("key down\r\n");break;
case 3:
uart_write("key up\r\n");break;
case 5:
if(val == 0x100){
uart_write("key right\r\n");break;
}
if(val == 0x200){
uart_write("key left\r\n");break;
}
default:
uart_write("Unknown interrupt!\r\n");
break;
}

if((oft >=0) && (oft<=3))
EINTPEND |= 1< else
EINTPEND |= val;
SRCPND |= (1< INTPND |= (1<

}

其他都一样。



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

热门文章 更多
Recogni:将高端AI芯片推向自动驾驶边缘