s5p6818裸机-irq中断

前言

本次学习的目的是通过按按键触发按键中断,调用相关的中断服务函数,实现蜂鸣器鸣响。通过裸机学习能使自己对SoC的运行环境,开发环境有更好的了解。

软件实现流程

1)在start.S启动汇编中实现中断向量表,及irq处理函数
2)实现irq中断结构体数组,irq通用中断服务函数注册函数(中断号和中断服务函数的联系)
3)实现gic中断控制器初始化函数
4)实现gpio初始化函数,复用-中断配置

流程具体

1、异常向量表和IRQ_Handler一般都在start.S里面编写,因为需要做一些中断触发时保护现场和恢复现场的工作。

_start:
    /*DDR范围:0x40000000~0x80000000 1GB*/

    /*异常向量表 */
    ldr pc, =Reset_Handler      /* 复位中断 					*/	
    ldr pc, =Undefined_Handelr  /* 未定义中断 					*/
    ldr pc, =SVC_Handelr        /* SVC(Supervisor)中断 		*/
    ldr pc, =PrefAbort_Handler  /* 预取终止中断 					*/
    ldr pc, =DataAbort_Handler  /* 数据终止中断 					*/
    ldr pc, =NotUsed_Handler    /* 未使用中断					*/
    ldr pc, =IRQ_Handler        /* IRQ中断 					*/
    ldr pc, =FIQ_Handler        /* FIQ(快速中断)未定义中断 			*/
IRQ_Handler:
    ldr sp,=0x7FC00000 /*设置irq模式的sp指针 */
    /*保护现场 */
    push {lr}
    push {r0-r3,r12}   
    mrs r0, spsr   /*访问SPSR寄存器,保存cpsr */
    push {r0}
/****************************************************** */

    mrc p15,1,r1,c15,c3,0 /*读取GIC的CPU接口端基地址 GICC_CTLR*/
    ldr r0, [r1,#0xC]   /*基地址偏移0xC,访问GICC_IAR寄存器 */
                        /*GICC_IAR能够获取到中断ID号 */
    push {r0,r1}
    cps #0x3           /*进入svc模式 */
    push {lr}       /*保存svc的lr寄存器 */

    //ldr r2, =system_irqhandler 
    blx system_irqhandler            /*跳转到c程序的中断处理函数中,带上一个参数r0(中断ID)*/

    pop {lr}   /*lr出栈 */
    cps #0x2   /*进入irq模式 */
    pop {r0,r1}
    str r0, [r1,#0x10]  /*中断执行完成,把中断ID写入EOIR寄存器 */

    /*恢复现场 */
    pop {r0}
    msr spsr, r0  /*恢复cpsr */
    pop {r0-r3,r12}
    pop {lr}
/********************************** */
    subs pc, lr, #4 /*返回触发中断时,下一条指令的位置 */

其中恢复现场的subs pc, lr, #4 是因为cpu是三级流水线工作的,取址,译址,执行。比如当前第一条指令是执行,第二条指令是译址,第三条指令是取址,那么当中断触发时,pc所指向的是取址,而恢复现场则需要从第二条指令那里开始恢复即pc-4的位置。

2、实现irq中断结构体数组,(中断号和中断服务函数的联系)

/*中断服务函数的形式*/
typedef void (*system_irq_handler_t)(unsigned int giccIar,void *param);

/*中断服务函数结构体*/
typedef struct _sys_irq_handle
{
    system_irq_handler_t irqhandler;    /*中断服务函数*/
    void *userParam;                    /*中断服务函数参数*/
}sys_irq_handle_t;

static sys_irq_handle_t irqTable[160]; /*数组大小为160,每一个元素的下标对应一个中断ID*/

有了这个结构体数组,就实现了中断号和中断服务函数的联系了,下一步就是把中断服务函数往里面注册就可以了。

3、irq通用中断服务函数注册函数

/*给指定的中断号注册中断服务函数 */
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam)
{
    irqTable[irq].irqhandler = handler;
    irqTable[irq].userParam = userParam;
}

这个就是往结构体里面注册中断服务函数了,自己编写好最终的中断服务函数,作为参数handler往里面存。

4、上面start.S的IRQ_Handler会跳进这个函数,根据中断ID执行最终的中断服务函数

/*具体的中断服务函数*/
void system_irqhandler(unsigned int giccIar)
{
    unsigned int intNum = giccIar & 0x3FF;  /*GICC_IAR的bit[9:0]为中断ID*/
    if(intNum < NUMBER_OF_INT_VECTORS)
    {
        irqNesting++;
        irqTable[intNum].irqhandler(intNum, irqTable[intNum].userParam);
        irqNesting--;
    }
    else
        return;
}

5、gic的初始化
参考数据手册,得到需要用到的寄存器GICD_TYPER、GICD_ISCENABLERn、GICC_PMR、GICC_BPR、GICD_CTRL、GICC_CTRL、GICD_ISENABLERn、GICD_IPRIORITYRn,寄存器的具体作用我在下面代码里面也作了注释,或者去查看手册。

/*GIC初始化*/
void GIC_init(void)
{
    unsigned int i;
    unsigned int irqRegs;

    /*读取bit[0:4]为N,支持的中断ID最大值:(32(N+1)-1=159)*/
    irqRegs = (GIC->D_TYPER & 0x1F) + 1; 

    /*关闭所有SGI,PPI,SPI中断*/
    for(i=0; i<irqRegs; i++) 
        GIC->D_ISCENABLERn[i] |= 0xFFFFFFFF; 

    /*设置支持32个优先级,即bit[7:0]=11111000*/
    GIC->C_PMR |= (0xFF << 3);  
    //GIC->C_PMR |= 0xFF;

    /*设置5级抢占优先级bit[2:0]=2,即所有中断优先级位都为抢占优先级*/
    GIC->C_BPR |= (2<<0);

    /*使能组0的中断分发控制*/
    GIC->D_CTRL |= (0x1<<0);

    /*使能组0的中断发送到CPU Core的中断请求信号*/
    GIC->C_CTRL |= (0x1<<0);
}

/*使能指定中断,并设置优先级*/
void GIC_EnableIRQ(IRQn_Type IRQn, unsigned int pri)
{
    /*IRQn/32: 取到0~4寄存器里对应的寄存器*/
    /*IRQn%32: 取到对应寄存器里相应的bit*/
    GIC->D_ISENABLERn[IRQn/32] |= (1<<(IRQn%32));
    GIC->D_IPRIORITYRn[IRQn/4] &= ~(0xFF<<((IRQn%4)*8));
    GIC->D_IPRIORITYRn[IRQn/4] |= (pri<<((IRQn%4)*8));      /*设置指定中断的优先级*/
    //GIC->D_ITARGETSRn[21] &= (~(0xFF<<16));
    //GIC->D_ITARGETSRn[21] |= (0x1<<16);    /*设置指定中断的CPU接口*/
}

6、gpio的中断设置
通过查看数据手册得到需要使用的寄存器有GPIOXINTENB、GPIOXDETENB、GPIOXDETMODE0、GPIOXDETMODE1、GPIOXDETMODEEX、GPIOXDET,具体功能也是在下面代码的注释里面有说明

/*使能GPIO口的中断功能*/
void gpio_intEnable(GPIO_Type *base, int pin)
{
    base->GPIO_INTENB |= (1<<pin);  /*中断使能*/
    base->GPIO_DETENB |= (1<<pin);  /*检测使能*/
}

/*设置gpio的中断配置功能*/
/*GPIOXDETMODE0或者GPIOXDETMODE1需要跟GPIOXDETMODEEX配合一起使用,
因为配置中断检测方式即高/低电平触发,上升/下降沿触发等需要用到3bit数来配置,
其中高1bit由GPIOXDETMODEEX提供,低2bit由GPIOXDETMODE0或者GPIOXDETMODE1提供*/

void gpio_intconfig(GPIO_Type *base, int pin, GPIO_Interrupt_Mode_t intmode)
{
    volatile unsigned int *DetMode;
    unsigned int pin_t;

    pin_t = pin;

    if (pin<16)
    {
        DetMode = &(base->GPIO_DETMODE0); /*0~15引脚*/
    }
    else
    {
        DetMode = &(base->GPIO_DETMODE1); /*16-31引脚*/
        pin_t -= 16;
    }
    switch(intmode)
    {
        case GPIO_IntLowLevel:
            base->GPIO_DETMODEEX &= ~(1<<pin);    /*000的高(1-bit)*/
            *DetMode &= ~(3<<(2 * pin_t));        /*000的低(2-bit)*/
            break;
        case GPIO_IntHighLevel:
            base->GPIO_DETMODEEX &= ~(1<<pin);                                      /*001的高(1-bit)*/
            *DetMode = (*DetMode & (~(3<<(2 * pin_t)))) | (1<<(2 * pin_t));         /*001的低(2-bit)*/             
            break;
         case GPIO_IntFallingEdge:
            base->GPIO_DETMODEEX &= ~(1<<pin);                                      /*010的高(1-bit)*/
            *DetMode = (*DetMode & (~(3<<(2 * pin_t)))) | (2<<(2 * pin_t));         /*010的低(2-bit)*/       
            break;
        case GPIO_IntRisingEdge:
            base->GPIO_DETMODEEX &= ~(1<<pin);                                      /*011的高(1-bit)*/
            *DetMode = (*DetMode & (~(3<<(2 * pin_t)))) | (3<<(2 * pin_t));         /*011的低(2-bit)*/
            break;
         case GPIO_IntFallingorRisingEdge:
            base->GPIO_DETMODEEX |= (1<<pin);   /*100的高(1-bit)*/
            *DetMode &= ~(3<<2 * pin_t);        /*100的低(2-bit)*/
            break;   
        default:
			break;

    }
}

/*清除中断标注位*/
void gpio_clearintflags(GPIO_Type *base, int pin)
{
    base->GPIO_DET |= (1<<pin);	/*往里面写1即可清除标志位*/
}

小结

总体来说整个中断的执行顺序大概是:按下按键 -> 触发irq中断信号 -> gic控制器 -> CPU -> 异常向量表 -> IRQ异常处理 -> 执行最终的中断服务程序

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值