一, 汇编对中断进行设置
列出对中断向量表,主要用的是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进行配置,最后具体说明实现这个中断会执行什么样操作。模块实现起来比较多,原理相对前面来说复杂一些。