S5PV210中断初始化大致流程(以按键中断为例)

我认为还要加上这几句:  

cpsr_init:                   @设置cpsr寄存器

    mrs r0,cpsr

    bic r0,r0,#0x53    //开启IrQ,进入管理模式

    msr cpsr_c,r0
参考:
(1)https://blog.csdn.net/kiny_anderson/article/details/46884893
(2)https://blog.csdn.net/guohengsheng3882/article/details/78448050
(3)深入剖析ARM Cortex-A8 (378页)
****************************************************************************
流程图:
1.当发生中断————>>
2.程序会跳到相应的中断向量表,找相应的中断向量地址————>>
3.进入IRQ_handle,保存现场————>>
4.跳转到irq_handler,irq_handler分析哪一个VICN ADDR有值,把相应的处理函数地址给(*isr)()————>>
5.进入相应的中断处理函数,这三个(isr_eint2,isr_eint3,isr_eint16171819)真正的中断处理函数,
   执行相应的内容,清除中断挂起{rEXT_INT_0_PEND |= (1<<3); intc_clearvectaddr();}————>>
6.恢复现场回到IRQ_handle,恢复现场————>>
7.回到主函数
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
main:  ,中断处理步骤:
1.Uart.c的 串口初始化程序void uart_init(void)
2.以中断方式来处理按键的初始化 ,key_init_interrupt();
    {
        1. 外部中断对应的GPIO模式设置
        2. 中断触发模式设置
        3. 中断允许
        4. 清挂起,清除是写1,不是写0    
    }
3.中断初始化来初步初始化中断控制器,system_init_exception();
    {
        1.第一阶段处理,绑定异常向量表
        2.初始化中断控制器的基本寄存器,void intc_init(void)
        {
            1.禁止所有中断
            VIC0INTENCLEAR = 0xffffffff;
            VIC1INTENCLEAR = 0xffffffff;
            VIC2INTENCLEAR = 0xffffffff;
            VIC3INTENCLEAR = 0xffffffff;

            2.选择中断类型为IRQ
            VIC0INTSELECT = 0x0;
            VIC1INTSELECT = 0x0;
            VIC2INTSELECT = 0x0;
            VIC3INTSELECT = 0x0;
        }
        3.清VICxADDR,清除需要处理的中断的中断处理函数的地址,void intc_clearvectaddr(void)
        {
                              // VICxADDR:当前正在处理的中断的中断处理函数的地址
            VIC0ADDR = 0;    //存放当前活动的ISR地址,复位值为0x0000_0000
            VIC1ADDR = 0;
            VIC2ADDR = 0;
            VIC3ADDR = 0;
        }
    }
4.绑定isr到中断控制器硬件
    {
        intc_setvectaddr(KEY_EINT2, isr_eint2);    //KEY_EINT2 = 2
        intc_setvectaddr(KEY_EINT3, isr_eint3);    //KEY_EINT3 = 3
        intc_setvectaddr(KEY_EINT16_19, isr_eint16171819);    //KEY_EINT16_19 = 16    
    }
5.使能中断
    {
        intc_enable(KEY_EINT2);  //通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
        intc_enable(KEY_EINT3);  //通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
        intc_enable(KEY_EINT16_19);  //VIC0INTENABLE
    }
################################################################
异常向量表在irom_application中可以找到,直接搜0xD003_7400,然后有下面得到地址
#define exception_vector_table_base        0xD0037400
#define exception_reset                    (exception_vector_table_base + 0x00)
#define exception_undef                    (exception_vector_table_base + 0x04)
#define exception_sotf_int                (exception_vector_table_base + 0x08)
#define exception_prefetch                (exception_vector_table_base + 0x0C)
#define exception_data                    (exception_vector_table_base + 0x10)
#define exception_irq                    (exception_vector_table_base + 0x18)
#define exception_fiq                    (exception_vector_table_base + 0x1C)
################################################################
    bl main
    
    b .

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}^
    
######################################################################
// int.c的程序    通过读取VICnIRQSTATUS寄存器,判断其中哪个有一位为1,来得知哪个VIC发生中断了
unsigned long intc_getvicirqstatus(unsigned long ucontroller)
{
    if(ucontroller == 0)           //他这里因为只有
        return    VIC0IRQSTATUS;    //中断状态寄存器,只读
    else if(ucontroller == 1)
        return     VIC1IRQSTATUS;
    else if(ucontroller == 2)
        return     VIC2IRQSTATUS;
    else if(ucontroller == 3)
        return     VIC3IRQSTATUS;
    else
    {}
    return 0;
}


// int.c的程序   真正的中断处理程序。意思就是说这里只考虑中断处理,不考虑保护/恢复现场
void irq_handler(void)
{
    //printf("irq_handler.\n");
    // SoC支持很多个(在低端CPU例如2440中有30多个,在210中有100多个)中断
    // 这么多中断irq在第一个阶段走的是一条路,都会进入到irq_handler来
    // 我们在irq_handler中要去区分究竟是哪个中断发生了,然后再去调用该中断
    // 对应的isr。
    
    
    // 虽然硬件已经自动帮我们把isr放入了VICnADDR中,但是因为有4个,所以我们必须
    // 先去软件的检查出来到底哪个VIC中断了,也就是说isr到底在哪个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)();        // 通过函数指针来调用函数
}
#########################################################################
Main.c的 int main(void)
{
——————————————————————————————————————————————————————————
    uart_init();
    //Uart.c的 串口初始化程序
    void uart_init(void)
    {
        // 初始化Tx Rx对应的GPIO引脚
        rGPA0CON &= ~(0xff<<0);            // 把寄存器的bit0~7全部清零
        rGPA0CON |= 0x00000022;            // 0b0010, Rx Tx
        
        // 几个关键寄存器的设置
        rULCON0 = 0x3;
        rUCON0 = 0x5;
        rUMCON0 = 0;
        rUFCON0 = 0;
        
        // 波特率设置    DIV_VAL = (PCLK / (bps x 16))-1
        // PCLK_PSYS用66MHz算        余数0.8
        //rUBRDIV0 = 34;    
        //rUDIVSLOT0 = 0xdfdd;
        
        // PCLK_PSYS用66.7MHz算        余数0.18
        // DIV_VAL = (66700000/(115200*16)-1) = 35.18
        rUBRDIV0 = 35;
        // (rUDIVSLOT中的1的个数)/16=上一步计算的余数=0.18
        // (rUDIVSLOT中的1的个数 = 16*0.18= 2.88 = 3
        rUDIVSLOT0 = 0x0888;        // 3个1,查官方推荐表得到这个数字
    }
—————————————————————————————————————————————————————————————————————————————————————
    //key_init(); 以中断方式来处理按键的初始化
    key_init_interrupt();
    //Key.c的程序
    void key_init_interrupt(void)
    {
        // 1. 外部中断对应的GPIO模式设置
        rGPH0CON |= 0xFF<<8;        // GPH0_2 GPH0_3设置为外部中断模式
        rGPH2CON |= 0xFFFF<<0;        // GPH2_0123共4个引脚设置为外部中断模式
                                   //这里是通过核心板,找相应的GPIO口的
        // 2. 中断触发模式设置
        rEXT_INT_0_CON &= ~(0xFF<<8);    // bit8~bit15全部清零
        rEXT_INT_0_CON |= ((2<<8)|(2<<12));        // EXT_INT2和EXT_INT3设置为下降沿触发
        rEXT_INT_2_CON &= ~(0xFFFF<<0);
        rEXT_INT_2_CON |= ((2<<0)|(2<<4)|(2<<8)|(2<<12));    
        
        // 3. 中断允许
        rEXT_INT_0_MASK &= ~(3<<2);            // 外部中断允许
        rEXT_INT_2_MASK &= ~(0x0f<<0);     //注意是取反,是让它变为0
        
        // 4. 清挂起,清除是写1,不是写0
        rEXT_INT_0_PEND |= (3<<2);
        rEXT_INT_2_PEND |= (0x0F<<0);
    }
————————————————————————————————————————————————————————————————————————————————————————
    // 如果程序中要使用中断,就要调用中断初始化来初步初始化中断控制器
        system_init_exception();
    // 主要功能:绑定第一阶段异常向量表;禁止所有中断;选择所有中断类型为IRQ;
    //int.c的程序   清除VICnADDR为0
    void system_init_exception(void)
    {
        // 第一阶段处理,绑定异常向量表
        r_exception_reset = (unsigned int)reset_exception;   //向量地址都是函数地址
        r_exception_undef = (unsigned int)undef_exception;
        r_exception_sotf_int = (unsigned int)sotf_int_exception;
        r_exception_prefetch = (unsigned int)prefetch_exception;
        r_exception_data = (unsigned int)data_exception;
        r_exception_irq = (unsigned int)IRQ_handle;
        r_exception_fiq = (unsigned int)IRQ_handle;   //我自己认为是他把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
        // 清除需要处理的中断的中断处理函数的地址
        void intc_clearvectaddr(void)
        {
                // VICxADDR:当前正在处理的中断的中断处理函数的地址
                VIC0ADDR = 0;    //存放当前活动的ISR地址,复位值为0x0000_0000
                VIC1ADDR = 0;
                VIC2ADDR = 0;
                VIC3ADDR = 0;
        }
    }
——————————————————————————————————————————————————————————————————————————————
    printf("-------------key interrypt test--------------");

——————————————————————————————————————————————————————————————————————————————    
    // 绑定isr到中断控制器硬件
    intc_setvectaddr(KEY_EINT2, isr_eint2);    //KEY_EINT2 = 2
    intc_setvectaddr(KEY_EINT3, isr_eint3);    //KEY_EINT3 = 3
    intc_setvectaddr(KEY_EINT16_19, isr_eint16171819);    //KEY_EINT16_19 = 16    
// 绑定我们写的isr到VICnVECTADDR寄存器
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
// 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
// 参数:intnum是int.h定义的****物理中断号#####,handler是函数指针,就是我们写的isr

// VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个
// 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候
// 只需要首地址+偏移量即可。        arm总共有  32 * 4 = 128个VIC0VECTADDR寄存器
Int.c的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;
}

-----------------------------------
这是三个(isr_eint2,isr_eint3,isr_eint16171819)函数
-----------------------------------
    // EINT2通道对应的按键,就是GPH0_2引脚对应的按键,就是开发板上标了LEFT的那个按键
    void isr_eint2(void)
    {
        // 真正的isr应该做2件事情。
        // 第一,中断处理代码,就是真正干活的代码
        printf("isr_eint2_LEFT.\n");
        // 第二,清除中断挂起
        rEXT_INT_0_PEND |= (1<<2);         //GPIO的PEND寄存器
        void intc_clearvectaddr(void)
        {
            // VICxADDR:当前正在处理的中断的中断处理函数的地址
            VIC0ADDR = 0;    //存放当前活动的ISR地址,复位值为0x0000_0000
            VIC1ADDR = 0;
            VIC2ADDR = 0;
            VIC3ADDR = 0;
        }
    }

    void isr_eint3(void)
    {
        // 真正的isr应该做2件事情。
        // 第一,中断处理代码,就是真正干活的代码
        printf("isr_eint3_DOWN.\n");
        // 第二,清除中断挂起
        rEXT_INT_0_PEND |= (1<<3);   //GPIO的PEND寄存器
        intc_clearvectaddr();
    }

    void isr_eint16171819(void)
    {
        // 真正的isr应该做2件事情。
        // 第一,中断处理代码,就是真正干活的代码
        // 因为EINT16~31是共享中断,所以要在这里再次去区分具体是哪个子中断
        if (rEXT_INT_2_PEND & (1<<0))       //0——没有产生中断  1——产生中断
        {
            printf("eint16\n");
        }
        if (rEXT_INT_2_PEND & (1<<1))
        {
            printf("eint17\n");
        }
        if (rEXT_INT_2_PEND & (1<<2))
        {
            printf("eint18\n");
        }
        if (rEXT_INT_2_PEND & (1<<3))
        {
            printf("eint19\n");
        }

        // 第二,清除中断挂起
        rEXT_INT_2_PEND |= (0x0f<<0);      //GPIO的PEND寄存器
        intc_clearvectaddr();
    }
————————————————————————————————————————————————————————————————————————————————————

    // 使能中断
    intc_enable(KEY_EINT2);
    intc_enable(KEY_EINT3);
    intc_enable(KEY_EINT16_19);
// 使能中断
//Int.c的程序 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
//前面只是把使能清除寄存器关闭              VIC0INTENCLEAR = 0xffffffff;
    void intc_enable(unsigned long intnum)
    {
        unsigned long temp;
        // 确定intnum在哪个寄存器的哪一位
        // <32就是0~31,必然在VIC0
        if(intnum<32)
        {
            temp = VIC0INTENABLE;
            temp |= (1<<intnum);        // 如果是第一种设计则必须位操作,第二种设计可以
                                        // 直接写。
            VIC0INTENABLE = temp;    //0——无效  1——允许中断
        }
        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;
        }

    }
————————————————————————————————————————————————————————————————————————————————————-
    // 在这里加个心跳
    while (1)
    {
        printf("A ");
        delay(10000);
    }

    return 0;
}
 

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/81067477