歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

exynos4412中斷編程

中斷概述

我們知道,ARM核能處理的異常有7種,但僅僅區分異常的種類顯然不能夠滿足需求。拿手機來說,觸摸屏幕和按下音量鍵可能都是irq異常,但是ARM並不能將他們區分開,而事實的情況是針對這兩種中斷,我們的處理方式顯然不同,為此就需要在Soc中集成中斷控制器(Generic Interupt Controller),它的核心功能就是進行中斷的調度和管理。

SGI VS PPI VS SPI

exynos4412中每種中斷都有自己的ID用以標識,exynos4412支持三類中斷:Software Generated Interrupt (SGI),Private Peripheral Interrupt (PPI)和Shared Peripheral Interrupt (SPI)。所有的中斷可以按照優先級可以劃分為0~255級,和大多數芯片一樣,數字越小,中斷的優先級越高,同時發生的時候優先級更高的中斷會被優先處理,當然,Soc對每個中斷都有一個默認的優先級。

SGI表示軟件生成中斷,即上圖中CPU和GIC通過一個寄存器進行交互,讓GIC給CPU(們)發送一個中斷(和ARM匯編中的swi不是一回事!)這個過程並沒有真正的外設來觸發中斷。SGI中斷具有邊沿觸發的屬性,在多核Soc中,一個CPU可以通過觸發SGI的方式和其他CPU交互。交互的形式有兩種:1-N,N-N。前者表示只有一個CPU響應這個SGI,此時需要提供機制來確定到底的哪個CPU接收並響應這個SGI。後者表示所有的CPU都將接收到SGI,當一個CPU收到這個SGI之後,這個SGI在這個CPU的pending狀態就被清除了,但是在其他CPU的pending狀態還會繼續維持。exynos4412支持最多16個SGI

PPI表示該中斷只會送給某一個CPU來處理,這是一個外設中斷,有邊沿觸發和平台觸發兩種觸發方式。exynos4412支持最多8個PPI中斷

SPI和PPI一樣是外設中斷,不同的是這個中斷可以被GIC調度給任何CPU來處理。exynos4412支持最多128個SPI中斷

中斷的狀態

GIC負責維護每個中斷的狀態標志,exynos4412的中斷有四個狀態:inactive,pending,active,active and pending。

Inactive即該中斷既不是pending狀態也不是active狀態。

Pending表示一個從中斷源傳送到GIC的外設中斷或者一個SIG中斷正在等待被GIC傳送到目標CPU.

Active表示一個中斷正在被CPU 響應。

Active and Pending表示CPU正在響應一個中斷,而GIC還用一個來自同一個中斷源的中斷在排隊.

編程模型

源碼
參考上面的中斷響應結構圖,為了響應一個中斷,首先應該在外設層進行設置,諸如中斷觸發條件,中斷連接管腳等,然後需要在GIC層次進行設置,比如中斷的調度方法,最後在CPU層次進行設置,比如哪些中斷可以進入CPU,哪些不行。

編程使用的原理圖如下,程序的目的是按下一個按鍵,就有一個相應的LED閃爍一下。需要參考的資料有原理圖、exynos4412芯片手冊:

#include "exynos_4412.h"
#include "debug.h"
#define LED_ON_TIMER 0xfffff
void delay(unsigned int x){
    unsigned int timer=x;
    while(timer--);
}
/* 57號中斷(即EINT[9],按鍵K2,參考原理圖和芯片手冊中斷號表)的中斷處理*/
void irq57_handler(void){   
    deprint("irq57\n");
    GPX1.CON=GPX1.CON&(~(0xf<<0))|(0x1<<0); //配置LED3的相應管腳(GPX1_1)為輸出
    GPX1.DAT|=(0x1<<0);                     //GPX1_1輸出高電平
    delay(LED_ON_TIMER);                    //延時
    GPX1.DAT&=~(0x1<<0);                    //GPX1_1輸出低電平
}
void irq58_handler(void){
    deprint("irq58\n");
    GPF3.CON=GPX2.CON&(~(0xf)<<16)|(0x1<<16);
    GPF3.DAT|=(0x1<<4);
    delay(LED_ON_TIMER);
    GPF3.DAT&=~(0x1<<4);
}
void do_irq(void){      //irq入口函數
    //ICCIAR的低10位是當前觸發的中斷號
    volatile unsigned int IRQ_ID=CPU0.ICCIAR & 0x3ff;  
    switch (IRQ_ID){
    case 57:
        irq57_handler();

        //處理完中斷應該將相應的中斷標志清除,
        //否則GIC會認為中斷沒有響應,繼續給CPU發送這個中斷
        //  EXT_INT41_PEND=EXT_INT41_PEND|(0x1<<1);
        //  BUG!!!,寫一置零,如果低位本來是1,會將其一起清零!!!
        EXT_INT41_PEND=0x2; 
        break;
    case 58:
        irq58_handler();
        EXT_INT41_PEND=0x4;
        break;
    default:
        break;
    }
    deprint("switch over\n");
    //將中斷號告訴GIC,表示中斷處理完畢,可以將下一個pending的中斷送入
    CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3ff))|IRQ_ID;    
}


int main()
{
    //外設配置
    //1. 將GPX1_1 GPX1_2 設置為中斷功能GPX1CON
    GPX1.CON=GPX1.CON|(0xff<<4);
    detobin(GPX1.CON);
        
    //2. 設置GPX1_1 GPX1_2的觸發方式為下降沿觸發
    EXT_INT41_CON=EXT_INT41_CON|(0x22);
    detobin(EXT_INT41_CON);

    //3. 使能GPX1_1 GPX1_2中斷
    EXT_INT41_MASK=EXT_INT41_MASK&(~(0x3<<1));
    detobin(EXT_INT41_MASK);

    //GIC配置
    //4. 全局使能GIC使其可以監控外設的中斷信號並轉發到CPU接口
    ICDDCR=1;
    detobin(ICDDCR);
    //5. 在中斷管理器中使能57,58號中斷,ICDISER1[25]
    ICDISER.ICDISER1=ICDISER.ICDISER1|(0x3<<25);
    detobin(ICDISER.ICDISER1);  

    //6. 給57 58號中斷選擇一個目標CPU。這裡選CPU0 ICDIPTR14[23:16][15:8]
    ICDIPTR.ICDIPTR14=ICDIPTR.ICDIPTR14|(0x1<<16)|(0x1<<8); 
    detobin(ICDIPTR.ICDIPTR14);

    //CPU配置
    //7. 使能全局中斷信號開關,使中斷能夠通過相應接口到達處理器,這裡是CPU0 
    CPU0.ICCICR=1;  
    detobin(CPU0.ICCICR);

    //8. 優先級低於ICCPMR的中斷才能被送入cpu,
    //這裡使所有的中斷都能夠經過CPU0接口到達處理器,給255。
    CPU0.ICCPMR=0xff;
    detobin(CPU0.ICCPMR);
    while(1);

    return 0;
}

Copyright © Linux教程網 All Rights Reserved