ARM之S5pv210的按键和中断部分

一、按键和中断分

    要使用中断,首先要做好两个部分的工作:CPU中断的初始化和相应器件的中断的初始化。

    CPU中断初始化:就是要设置号CPU有关中断的东西。

    相关器件的中断初始化:例如按键,就要设置好按键,就触发中

    (1)、先看看按键的原理图

        

        从上图我们可以得知:按键是接在EINT2和EINT3处,还有KP_COL0-3,一共是6个按

    (2)、接下来看SoC接口处

            

            从上图EINT2和EINT3接在了GPH0_2,3处,KP_COL0-3接在了GPH2_0-3处。

        (3)、最后看看有关的寄存器(GPH0和GPH2,全部将其设置为外部中断模式(EXT_INT),也就是1111),

            KP_COL模式是用来做矩阵键盘的。

            

        设置好寄存器GPH0和GPH2之后,我们下面设置和外部中断相关的寄存器:

        EXT_INT_0_CON,EXT_INT_2_CON,EXT_INT_0_MASK,EXT_INT_2_MASK,EXT_INT_0_PEND,EXT_INT_2_PEND。

        总结:也就是说按键这边,一个按键需要设置好四个寄存器就行了。

        GPH0CON:选择外部中断模式

        EXT_INT_0_CON:选择怎样就触发中断(是高电平就触发中断,还是低电平,上升沿,下降沿,上升/下降触发中断)

        EXT_INT_0_MASK:向该寄存器写0来使能中断

        EXT_INT_0_PEND:我们初始化的时候可以通过写1来进行清除中断,中断处理完之后,我们也要向这个寄存器写1来

        清除中断。

    设置好上面这些寄存器,我们按键部分的中断初始化就设置好了       

// 以中断方式来处理按键的初始化
void key_init_interrupt(void)
{
    // 1. 外部中断对应的GPIO模式设置
    rGPH0CON |= 0xFF<<8;        // GPH0_2 GPH0_3设置为外部中断模式
    
    // 2. 中断触发模式设置
    rEXT_INT_0_CON &= ~(0xFF<<8);    // bit8~bit15全部清零
    rEXT_INT_0_CON |= ((2<<8)|(2<<12));        // EXT_INT2和EXT_INT3设置为下降沿触发    
    
    // 3. 中断允许
    rEXT_INT_0_MASK &= ~(3<<2);            // 外部中断允许
    
    // 4. 清挂起,清除是写1,不是写0
    rEXT_INT_0_PEND |= (3<<2);
}

二、设置CPU的中断模式

    1、中断产生CPU所做的工作的大概过程

        

    需要注意的是:

        (1)、外部中断发生,EXT_INT_O_PEND寄存器(按键那边的寄存器)置1,中断挂起,这

    就相当于告诉了CPU中断发生了,然后CPU就发生了上图所对应的这些响应。

        (2)、中断发生后,cpsr寄存器中的IRQ中断位就置1了,所以,CPU进来处理中断后,其他硬

    件在这段时间内发生中断的话,CPU是不理的。

    根据上图:我们需要完成的东西就是

        (1)、把我们的异常处理入口的地址放到我们异常向量表所对应的内存处:0x00000018

    代码实现为:  

IRQ_handle:
    // 设置IRQ模式下的栈
    ldr sp, =IRQ_STACK
    // 保存LR
    // 因为ARM有流水线,所以PC的值会比真正执行的代码+8,
    sub lr, lr, #4
    // 保存r0-r12和lr到irq模式下的栈上面
    stmfd sp!, {r0-r12, lr}
    // 在此调用真正的isr来处理中断
    bl irq_handler
    // 处理完成开始恢复现场,其实就是做中断返回,关键是将r0-r12,pc,cpsr一起回复
    ldmfd sp!, {r0-r12, pc}^ 

三、设置中断相关的寄存器,让CPU找到相应的执行程序,然后执行它(这是中断的目的:执行中断处理程序)

    

    相关的寄存器有:VICnADDR   VICnINTENCLEAR  VICnINTSELECT  VICnIRQSTATUS

                                VIC0VECTADDRn     VICnINTENABLE

     1、VICnADDR:一共有四个寄存器,VIC(0-3)一人一个,用来存放我们想要执行的中断处理程序(isr)的地址,

        它里面的地址是从VIC0VECTADDRn这个寄存器里面来的,当中断发生之后,VIC0VECTADDRn里面的地址就会被

    硬件自动刷到这个寄存器上。(中断处理完之后,我们要清除这个寄存器)

// 清除需要处理的中断的中断处理函数的地址
void intc_clearvectaddr(void)
{
    // VICxADDR:当前正在处理的中断的中断处理函数的地址
    VIC0ADDR = 0;
    VIC1ADDR = 0;
    VIC2ADDR = 0;
    VIC3ADDR = 0;
}

    2、VINCnINTENCLEAR:清中断寄存器,也就是中断处理完成之后,我们要往这个寄存器里面写1,把中断清理掉。

        我们可以通过相应的中断号,来禁止那个中断。

// 禁止中断
// 通过传参的intnum来禁止某个具体的中断源,中断号在int.h中定义,是物理中断号
void intc_disable(unsigned long intnum)
{
    unsigned long temp;

    if(intnum<32)
    {
        temp = VIC0INTENCLEAR;
        temp |= (1<<intnum);
        VIC0INTENCLEAR = temp;
    }
    else if(intnum<64)
    {
        temp = VIC1INTENCLEAR;
        temp |= (1<<(intnum-32));
        VIC1INTENCLEAR = temp;
    }
    else if(intnum<96)
    {
        temp = VIC2INTENCLEAR;
        temp |= (1<<(intnum-64));
        VIC2INTENCLEAR = temp;
    }
    else if(intnum<NUM_ALL)
    {
        temp = VIC3INTENCLEAR;
        temp |= (1<<(intnum-96));
        VIC3INTENCLEAR = temp;
    }
    // NUM_ALL : disable all interrupt
    else
    {
        VIC0INTENCLEAR = 0xFFFFFFFF;
        VIC1INTENCLEAR = 0xFFFFFFFF;
        VIC2INTENCLEAR = 0xFFFFFFFF;
        VIC3INTENCLEAR = 0xFFFFFFFF;
    }

    return;
}

   3.VICnINTSELECT  :中断选择寄存器,我们在这里选择FIQ模式还是IRQ模式 

// 初始化中断控制器
void intc_init(void)
{
    // 禁止所有中断
    // 为什么在中断初始化之初要禁止所有中断?
    // 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr
    // 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码
    // 则程序很可能跑飞,所以不用的中断一定要关掉。
    // 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须
    // 给这个中断提供相应的isr并绑定好。
    VIC0INTENCLEAR = 0xffffffff;
    VIC1INTENCLEAR = 0xffffffff;
    VIC2INTENCLEAR = 0xffffffff;
    VIC3INTENCLEAR = 0xffffffff;

    // 选择中断类型为IRQ
    VIC0INTSELECT = 0x0;
    VIC1INTSELECT = 0x0;
    VIC2INTSELECT = 0x0;
    VIC3INTSELECT = 0x0;

    // 清VICxADDR
    intc_clearvectaddr();
}
    4.VIC0VECTADDRn  :一共有128个这样的寄存器,一个中断号对应一个这样的寄存器,我们可以通过中断号和VICnADDR这个基地址来计算VIC0VECTADDRn这个寄存器的地址,然后把我们的执行程序放到这个寄存器中,这样我们就不需要定义太多的宏了。
// 绑定我们写的isr到VICnVECTADDR寄存器
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
// 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
// 参数:intnum是int.h定义的物理中断号,handler是函数指针,就是我们写的isr

// VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个
// 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候
// 只需要首地址+偏移量即可。
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
{
    //VIC0
    if(intnum<32)
    {
        *( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;
    }
    //VIC1
    else if(intnum<64)
    {
        *( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;
    }
    //VIC2
    else if(intnum<96)
    {
        *( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) = (unsigned)handler;
    }
    //VIC3
    else
    {
        *( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) = (unsigned)handler;
    }
    return;
}
    5.VICnIRQSTATUS  :IRQ模式下的中断状态寄存器(一共有4个),中断发生后,这个寄存器就会自动置1了,然后我们是通过判断这4个寄存器中哪个寄存器写了1,然后得知我们的中断执行程序的地址是放到了哪个VICnADDR寄存器上,最后在相应的VICADDR寄存器上找到中断执行程序的地址。
unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};
    int i=0;
    void (*isr)(void) = NULL;

    for(i=0; i<4; i++)
    {
        // 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0
        if(intc_getvicirqstatus(i) != 0)
        {
            isr = (void (*)(void)) vicaddr[i];
            break;
        }
    }
    (*isr)();        // 通过函数指针来调用函数
 6.VICnINTENABLE  :中断使能寄存器,通过中断号,来向中断使能寄存器的相应位写1,就可以使能相应的中断了
// 使能中断
// 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
void intc_enable(unsigned long intnum)
{
    unsigned long temp;
    // 确定intnum在哪个寄存器的哪一位
    // <32就是0~31,必然在VIC0
    if(intnum<32)
    {
        temp = VIC0INTENABLE;
        temp |= (1<<intnum);        // 如果是第一种设计则必须位操作,第二种设计可以
                                    // 直接写。
        VIC0INTENABLE = temp;
    }
    else if(intnum<64)
    {
        temp = VIC1INTENABLE;
        temp |= (1<<(intnum-32));
        VIC1INTENABLE = temp;
    }
    else if(intnum<96)
    {
        temp = VIC2INTENABLE;
        temp |= (1<<(intnum-64));
        VIC2INTENABLE = temp;
    }
    else if(intnum<NUM_ALL)
    {
        temp = VIC3INTENABLE;
        temp |= (1<<(intnum-96));
        VIC3INTENABLE = temp;
    }
    // NUM_ALL : enable all interrupt
    else
    {
        VIC0INTENABLE = 0xFFFFFFFF;
        VIC1INTENABLE = 0xFFFFFFFF;
        VIC2INTENABLE = 0xFFFFFFFF;
        VIC3INTENABLE = 0xFFFFFFFF;
    }

}

    接下来是主函数:

int main(void)
{
    //串口初始化
    uart_init();
    //按键的中断初始化
    key_init_interrupt();
    
    // 如果程序中要使用中断,就要调用中断初始化来初步初始化中断控制器
    system_init_exception();
    
    // 绑定isr到中断控制器硬件
    intc_setvectaddr(KEY_EINT2, isr_eint2);
    
    // 使能中断
    intc_enable(KEY_EINT2);

    return 0;
}
欢迎各位指出不足之处


猜你喜欢

转载自blog.csdn.net/qq_41003024/article/details/80317218