FriendlyARM Tiny210开发板硬件基础 第三篇--裸机中断

要想写按键中断程序,首先我们要了解一下什么是中断,它的执行过程是怎么样的。

在ARM中中断源分为两类:外部中断和内部中断。内部中断大多是有CPU内部自己产生的,外部中断就是由各种外部硬件产生的:比如按键。


中断能否成功处理,有两部分,1是触发中断信号,2是ARM能否识别中断。
  • 触发中断信号

    触发中断信号,很简单,有高电平,低电平,上升沿,下降沿触发四种,至于用那种需要程序员来设置。可是一个设备能否具有触发中断信号的能力,还要看它是否是中断源,还有它是否处于中断模式中,这个可以查看手册中相关的介绍就能够知道。

  • CPU识别中断信号

    中断并不是一发过来CPU就会自动处理的。一个中断信号传到给CPU前,还得经过一个或者多个中断控制器的处理。中断控制器说白了就是用来过滤中断的。对于ARM中的所有中断源,在中断控制器里会有对应寄存器来控制,包括状态标志和中断开关。
    一个中断源要想到达CPU,它所对应的寄存器应该都是允许该中断的,就拿按键IRQ中断举例:
    首先:设置按键所对应的寄存器为中断模式、配置中断触发方式
    然后:开中断,在裸机中除了要打开中断控制器中的位之外,CPSR寄存器的中断控制位也一定要打开。这个是中断的总开关,如果不打开一切都白费了。

  • 中断的处理过程
    当CPU接收到中断信号的时候,它会打断当前正在执行的程序,保存当前的寄存器(即保存PC和其他寄存器),查找中断向量表,跳到相应的地址去执行中断程序(即把中断向量表中的对应值赋值给PC),执行完之后跳回刚才中断的地方继续执行(即把原来保存的PC恢复回来)。

    好吧,简单写一个按键中断程序参考一下:
    首先先用汇编写一段引导代码,主要的功能就是,设置堆栈指针,设置中断向量表,打开CPSR的中断位。代码如下:

.global _start                  /* 声明一个全局的标号 */  
.global key_isr  
_start:  

    ldr r0, =0xE2700000         
    mov r1, #0
    str r1, [r0]

    /* 设置栈,以调用c函数 */  
    ldr sp, =0x40000000     

    /* 开总中断,CPSR寄存器 */  
    mov r0, #0x53           
    msr CPSR_cxsf, r0   
    bl clock_init   /*设置中断向量表*/
    bl main         /* 跳转到C函数去执行 */  

key_isr:  
    ldr sp, =0xD0037F80
    /* 计算返回地址:PC的值等于当前执行的地址+8,当CPU正要执行某条指令时(还未执行),被中断, 
    ** 这是这条刚要执行的指令的地址刚好=PC-4 */  
    stmfd sp!, {r0-r12, lr}     /* 保护现场 */  
    bl do_irq  
    /* 恢复现场 */  
    ldmfd sp!, {r0-r12, pc}^    /* ^表示把spsr恢复到cpsr */ 
    subs pc, lr, #4 
halt:  
    b halt  

中断向量表的设置代码:

#define _Exception_Vector       0xD0037400 //Yiny210 在程序开始时就跳到了这里、而不是0地址
#define pExceptionRESET ( *((volatile unsigned long *)(_Exception_Vector + 0x0)) )
#define pExceptionUNDEF ( *((volatile unsigned long *)(_Exception_Vector + 0x4)) )
#define pExceptionSWI   ( *((volatile unsigned long *)(_Exception_Vector + 0x8)) )
#define pExceptionPABORT    ( *((volatile unsigned long *)(_Exception_Vector + 0xc)) )
#define pExceptionDABORT    ( *((volatile unsigned long *)(_Exception_Vector + 0x10)) )
#define pExceptionRESERVED  ( *((volatile unsigned long *)(_Exception_Vector + 0x14)) )
#define pExceptionIRQ   ( *((volatile unsigned long *)(_Exception_Vector + 0x18)) )
#define pExceptionFIQ   ( *((volatile unsigned long *)(_Exception_Vector + 0x1c)) )

void key_isr(void);

void exceptionundef(void)
{

    while(1);
}

void exceptionswi(void)
{
    while(1);
}

void exceptionpabort(void)
{
    while(1);
}

void exceptiondabort(void)
{

    while(1);
}

// 设置中断向量表
void system_initexception( void)
{
    // 给每个中断一个处理函数地址
    pExceptionUNDEF   = (unsigned long)exceptionundef;
    pExceptionSWI     = (unsigned long)exceptionswi;
    pExceptionPABORT  = (unsigned long)exceptionpabort;
    pExceptionDABORT  = (unsigned long)exceptiondabort;
    pExceptionIRQ     = (unsigned long)key_isr;
    pExceptionFIQ     = (unsigned long)key_isr;
}

main.c

#define LEDCON    (*(volatile unsigned long *)0xE0200280)
#define LEDDAT    (*(volatile unsigned long *)0xE0200284)
#define BUZCON    (*(volatile unsigned long *)0xE02000A0)
#define BUZDAT    (*(volatile unsigned long *)0xE02000A4)
#define KEY1CON   (*(volatile unsigned long *)0xE0200C40)
#define EXT_INT_2_CON  (*(volatile unsigned long *)0xE0200E08)
#define EXT_INT_2_MASK (*(volatile unsigned long *)0xE0200F08)
#define EXT_INT_2_PEND (*(volatile unsigned long *)0xE0200F48)

//保存中断处理程序地址
#define VIC0INTSELECT  (*(volatile unsigned long *)0xF200000C)
#define VIC0INTENABLE  (*(volatile unsigned long *)0xF2000010)
#define VIC0VECTADDR16 (*(volatile unsigned long *)0xF2000140)
#define VIC0ADDRESS    (*(volatile unsigned long *)0xF2000F00)

void int_key1();

void do_irq();
void delay(void)
{
    int i;
    for(i = 0x100000; i > 0; i --)
        ;
}
int main()
{
    //设置异常向量表
    system_initexception();
    //LED  init
    LEDCON &= ~((0xf << 0) | (0xf << 4) | (0xf << 8) | (0xf << 14));
    LEDCON |= (0x1 << 0) | (0x1 << 4) | (0x1 << 8) | (0x1 << 12);
    LEDDAT |= 0xf;

    //BUZ   init
    BUZCON &= ~((0xf << 0) | (0xf << 4) | (0xf << 8) | (0xf << 14));
    BUZCON |= (0x1 << 0) ;
    BUZDAT &= 0xe;

    //interrupt init
    KEY1CON |= 0xf;  //interrupt mode
    EXT_INT_2_CON |= 1<<1;          
    EXT_INT_2_MASK &= ~(1<<0);

    VIC0INTENABLE |= (0x1 << 16);
    VIC0VECTADDR16 = (unsigned int)int_key1;

    while(1)
    {
        LEDDAT &= 0x0;
        delay();
        LEDDAT |= 0xf;
        delay();
    }

    return 0;
}

void do_irq()
{
    //处理中断的时候  中断函数的地址会自动赋值给 VIC0ADDRESS 
    ( ( void(*)(void) )VIC0ADDRESS )() ;
}
void int_key1()
{
    //中断服务程序
    BUZDAT |= 0x1;
    delay();
    delay();
    BUZDAT &= 0xe;

    //清中断
    VIC0ADDRESS = 0;
    EXT_INT_2_PEND |= (1 << 0);
}

Makefile

uart.bin: start.o main.o clock.o int.o
    arm-linux-ld -Tint.lds -o uart.elf $^
    arm-linux-objcopy -O binary uart.elf uart.bin
    arm-linux-objdump -D uart.elf > uart_elf.dis

%.o : %.s
    arm-linux-gcc -o $@ $< -c -fno-builtin

%.o : %.c
    arm-linux-gcc -o $@ $< -c -fno-builtin

clean:
    rm *.o *.elf *.bin *.dis *.exe *~ -f
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值