Linux-Ubuntu之按键中断实验

发布于:2025-02-11 ⋅ 阅读:(52) ⋅ 点赞:(0)

一, 汇编对中断进行设置

列出对中断向量表,主要用的是IRQ中断和复位中断服务函数,复位中断函数主要用于设置一些寄存器,再跳转到主函数,IRQ中断就是跳转到按键触发的中断,主要有中断ID号配置。



.global _start



_start:

/* 中断向量表*/

    ldr pc,=Reset_Handler

    ldr pc,=Undefined_Handler

    ldr pc,=SVC_Handler

    ldr pc,=PreAbort_Handler

    ldr pc,=DataAbort_Handler

    ldr pc,=NotUsed_Handler

    ldr pc,=IRQ_Handler

    ldr pc,=FIQ_Handler

/*复位中断服务函数 */

Reset_Handler:

    cpsid i  /*关闭IRQ */

    MRC p15,0,r0,c1,c0,0 //读取ARM的SCTLR寄存器内容

    bic r0,r0,#(1<<12)

    bic r0,r0,#(1<<11)

    bic r0,r0,#(1<<2)

    bic r0,r0,#(1<<1)

    bic r0,r0,#(1<<0)

    MCR p15,0,r0,c1,c0,0//写入SCTLR寄存器

   

    @ ldr r0,=0x87800000

    @ dsb

    @ isb



/*设置为IRQ模式 */

    mrs r0,cpsr /*读取cpsr现在状态 */

    bic r0,r0,#0x1f

    orr r0,r0,#0x12

    msr cpsr,r0

    ldr sp,=0x80600000 /*SP堆栈指针 */



/*设置为SYS模式 */

    mrs r0,cpsr /*读取cpsr现在状态 */

    bic r0,r0,#0x1f

    orr r0,r0,#0x1f

    msr cpsr,r0

    ldr sp,=0x80400000 /*SP堆栈指针 */



/*设置为SVC模式 */

    mrs r0,cpsr /*读取cpsr现在状态 */

    bic r0,r0,#0x1f

    orr r0,r0,#0x13

    msr cpsr,r0

    ldr sp,=0x80200000 /*SP堆栈指针 */ 

    cpsie i /*打开IRQ*/

    b main









/*未定义中断服务函数 */

Undefined_Handler:

    ldr r0,=Undefined_Handler

    bx r0

/*SVC中断服务函数 */

SVC_Handler:

    ldr r0,=SVC_Handler

    bx r0   

/*预取终止中断服务函数 */

PreAbort_Handler:

    ldr r0,=PreAbort_Handler

    bx r0 

/*数据终止中断服务函数 */

DataAbort_Handler:

    ldr r0,=DataAbort_Handler

    bx r0 

/*未使用中断服务函数 */

NotUsed_Handler:

    ldr r0,=NotUsed_Handler

    bx r0 

/*IRQ中断服务函数 */

IRQ_Handler:

    push {lr}

    push {r0-r3,r12}  //入栈



    mrs r0,spsr //读取spsr

    push {r0}//入栈



    mrc p15,4,r1,c15,c0,0



    add r1,r1,#0x2000//接口端地址

    ldr r0,[r1,#0xc]//保存中断ID

    push {r0,r1}



    cps #0x13 //SVC

    push {lr}

    ldr r2,=system_irqhandler//加载c语言中断

    blx r2

    

    pop {lr}

    cps #0x12

    pop {r0,r1}

    str r0,[r1,#0x10]



    pop {r0}

    msr spsr_cxsf,r0



    pop {r0-r3,r12}

    pop {lr}

    subs pc,lr,#4



/*FIQ中断服务函数 */

FIQ_Handler:

    ldr r0,=FIQ_Handler

    bx r0 



/*设置为svr模式 */

    mrs r0,cpsr /*读取cpsr现在状态 */

    bic r0,r0,#0x1f

    orr r0,r0,#0x13

    msr cpsr,r0





    

二,C语言模块

1.中断配置

设置中断处理函数表,然后对这个中断处理函数表进行初始化配置,当实现中断时候,就是将自己设置的中断函数放到这个中断处理函数表中,这个方法用的是注册中断处理函数。

#include "dsp_int.h"

static int irqNesting;//记录中断嵌套个数
/*中断处理函数表*/
static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];

/*初始化中断函数表*/
void system_irqtable_int(void)
{
    unsigned int i=0;
    irqNesting=0;//初始化时候 清0
    for(i=0;i<160;i++)
    {
        irqTable[i].irqHandler = default_irqhandler;//中断处理函数初始化
        irqTable[i].userParam=NULL;//中断处理函数参数初始化
    }
}

/*默认中断处理函数初始化*/
void default_irqhandler(unsigned int gicciar,void *param)
{
    while(1)
    {

    }
}

/*注册中断处理函数,当执行按键操作,将这个中断向量表对应位置进行写入*/
void system_register_irqhandler(IRQn_Type irq,system_irq_handler_t handler,void *userParam)
{
    irqTable[irq].irqHandler = handler;
     irqTable[irq].userParam = userParam;
}

/*中断初始化*/
void int_init(void)
{
    GIC_Init();//gic的
    system_irqtable_int();//初始化中断处理表
    __set_VBAR(0x87800000); //中断向量偏移地址
}

/*真正执行的中断处理函数,IRQ_handler会调用此函数*/
void system_irqhandler(unsigned int gicciar)
{
    uint32_t intnum = gicciar&0x3ff;//中断号 intnum
    irqNesting++;//进入中断加一
    if(intnum>=160)
    return;//超过160,有问题
    irqTable[intnum].irqHandler(intnum,irqTable[intnum].userParam);
    irqNesting--;
}

2.GPIO口配置

包括中断使能,设置GPIO的中断触发方式,包括低电平、高电平、上升沿或下降沿触发方式。

#include "dsp_gpio.h"



void gpio_init(GPIO_Type *base,int wei,gpio_config_t *gpio_config_init)//相当于设置GDIR和判断DR,输入的话,读取DR值,输出,给DR值
{
    if(gpio_config_init->section==gpio_in)//设置为输入
    {
        base->GDIR &=~(1<<wei);//输入设置为0 
    }else
    {
        base->GDIR |=(1<<wei);//设置为输出,进行写0 或者写1
        gpio_write(base,wei,gpio_config_init->mode);
    } 
   gpio_intconfig(base,wei,gpio_config_init->interruptMode);//GPIO初始化中断函数
}
void gpio_write(GPIO_Type *base,int wei,int write_value)
{
    if(write_value==0)
    {
        base->DR &= ~(1<<wei);//写0
    }else 
    {
        base->DR |= (1<<wei);//写1
    }
}
unsigned int gpio_read(GPIO_Type *base,int wei)//记录读的是0还是1
{
    return (base->DR>>wei)&0x01;
}
void gpio_enable(GPIO_Type *base,unsigned int pin)//使能中断
{
    base->IMR |=(1<<pin);
}
void gpio_disable(GPIO_Type *base,unsigned int pin)//禁止使能中断
{
    base->IMR &= ~(1<<pin);
}
void gpio_clearintflags(GPIO_Type *base,unsigned int pin)//清除中断标志
{
    base->ISR |= (1<<pin);
}
void gpio_intconfig(GPIO_Type *base,unsigned int pin,gpio_interrupt_mode_t ping_int_mode)//GPIO初始化中断函数
{
    volatile  uint32_t *icr;
    uint32_t icrShift;
    icrShift=pin;
    base->EDGE_SEL &=~(1 << pin);
    if(pin << 16)//低16位,ICR1,即设置高低电平
    {
        icr = &(base -> ICR1);
    }else 
    {
        icr = &(base -> ICR2);
        icrShift = icrShift-16;
    }
    switch(ping_int_mode)
    {
        case kGPIO_IntLowLevel:
            *icr = *icr & (~(3<<(2*icrShift)));//清0
            break;
        case kGPIO_IntHighLevel:
            *icr = *icr & (~(3<<(2*icrShift))); 
            *icr = *icr | (1<<(2*icrShift));   
            break;
        case kGPIO_IntRisingEdge:
            *icr = *icr & (~(3<<(2*icrShift)));
            *icr = *icr | (2<<(2*icrShift));  
            break;
        case kGPIO_IntFallingEdge:
            *icr = *icr & (~(3<<(2*icrShift)));
            *icr = *icr | (3<<(2*icrShift));  
            break;
        case KGPIO_IntRisingOrFallingEdge:
            *icr = *icr &(~(3<<(2*icrShift)));
            base->EDGE_SEL=base->EDGE_SEL|(1<<pin);
            break;
        default:
            break;          
    }

}

3.按键配置

这个部分是实现中断的具体执行操作,前面对中断初始化,就是说怎么样去用中断,给对应中断ID相应的功能怎么样实现,这个按键配置就是具体往里面填函数,来真的实现。然后对按键的GPIO即GPIO1_IO18进行配置,使其使能并且下降沿触发。

#include "dsp_exti.h"
#include "dsp_gpio.h"
#include "dsp_int.h"
#include "dsp_delay.h"
#include "dsp_led.h"
#include "beep.h"

void exit_init(void)
{
    gpio_config_t key_config;
    /*中断对按键初始化*/
    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);		
	IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0Xf080);
    key_config.section= gpio_in;
    key_config.interruptMode=kGPIO_IntFallingEdge;
    key_config.mode=1;
    gpio_init(GPIO1,18,&key_config);

    /*打开16-31中断*/
    GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);
    system_register_irqhandler(GPIO1_Combined_16_31_IRQn,(system_irq_handler_t)gpio1_IO18_irqhandler,NULL);
    gpio_enable(GPIO1,18);
}

/*中断处理函数*/
void gpio1_IO18_irqhandler(unsigned int gocciar,void *param)
{
    static unsigned char state = 0;
     delay(15);
    if(gpio_read(GPIO1,18)==0)
    {
        beep_mode(state);
        state=!state;
    }
    /*清除中断标志位*/ 
    gpio_clearintflags(GPIO1,18);
}


4.主函数

这个主函数与其他不同点在于,调用中断初始化,即对中断向量表进行初始化,然后调用按键的初始化,往中断向量表对应的ID写入这个中断执行函数,当判断为低电平,触发执行中断,执行蜂鸣器的操作。

#include "main.h"

#include "dsp_clk.h"

#include "dsp_led.h"

#include "dsp_delay.h"

#include "beep.h"

#include "dsp_key.h"

#include "dsp_int.h"

#include "dsp_exti.h"



int main(void)

{

    // int key_status=1;

    int_init();//中断初始化

    key_init();

    clk_enable();

    beep_init();

    led_init();

    exit_init();

    while(1)

    {



    }

    return 0;    

}

三,总结

按键中断实验,先在汇编中进行说明,然后对中断初始化,GPIO进行配置,最后具体说明实现这个中断会执行什么样操作。模块实现起来比较多,原理相对前面来说复杂一些。