Linux-Ubuntu之按键中断实验
- 一, 汇编对中断进行设置
- 二,C语言模块
- 1.中断配置
- 2.GPIO口配置
- 3.按键配置
- 4.主函数
- 三,总结
一, 汇编对中断进行设置
列出对中断向量表,主要用的是IRQ中断和复位中断服务函数,复位中断函数主要用于设置一些寄存器,再跳转到主函数,IRQ中断就是跳转到按键触发的中断,主要有中断ID号配置。
.global _start_start:/* 中断向量表*/ldr pc,=Reset_Handlerldr pc,=Undefined_Handlerldr pc,=SVC_Handlerldr pc,=PreAbort_Handlerldr pc,=DataAbort_Handlerldr pc,=NotUsed_Handlerldr pc,=IRQ_Handlerldr 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,#0x1forr r0,r0,#0x12msr cpsr,r0ldr sp,=0x80600000 /*SP堆栈指针 *//*设置为SYS模式 */mrs r0,cpsr /*读取cpsr现在状态 */bic r0,r0,#0x1forr r0,r0,#0x1fmsr cpsr,r0ldr sp,=0x80400000 /*SP堆栈指针 *//*设置为SVC模式 */mrs r0,cpsr /*读取cpsr现在状态 */bic r0,r0,#0x1forr r0,r0,#0x13msr cpsr,r0ldr sp,=0x80200000 /*SP堆栈指针 */ cpsie i /*打开IRQ*/b main/*未定义中断服务函数 */Undefined_Handler:ldr r0,=Undefined_Handlerbx r0/*SVC中断服务函数 */SVC_Handler:ldr r0,=SVC_Handlerbx r0 /*预取终止中断服务函数 */PreAbort_Handler:ldr r0,=PreAbort_Handlerbx r0 /*数据终止中断服务函数 */DataAbort_Handler:ldr r0,=DataAbort_Handlerbx r0 /*未使用中断服务函数 */NotUsed_Handler:ldr r0,=NotUsed_Handlerbx r0 /*IRQ中断服务函数 */IRQ_Handler:push {lr}push {r0-r3,r12} //入栈mrs r0,spsr //读取spsrpush {r0}//入栈mrc p15,4,r1,c15,c0,0add r1,r1,#0x2000//接口端地址ldr r0,[r1,#0xc]//保存中断IDpush {r0,r1}cps #0x13 //SVCpush {lr}ldr r2,=system_irqhandler//加载c语言中断blx r2pop {lr}cps #0x12pop {r0,r1}str r0,[r1,#0x10]pop {r0}msr spsr_cxsf,r0pop {r0-r3,r12}pop {lr}subs pc,lr,#4/*FIQ中断服务函数 */FIQ_Handler:ldr r0,=FIQ_Handlerbx r0 /*设置为svr模式 */mrs r0,cpsr /*读取cpsr现在状态 */bic r0,r0,#0x1forr r0,r0,#0x13msr 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;//初始化时候 清0for(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;//中断号 intnumirqNesting++;//进入中断加一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 或者写1gpio_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)));//清0break;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进行配置,最后具体说明实现这个中断会执行什么样操作。模块实现起来比较多,原理相对前面来说复杂一些。