1. 程式人生 > >嵌入式學習-uboot-lesson11-按鍵中斷

嵌入式學習-uboot-lesson11-按鍵中斷

一、中斷處理流程分析

1.中斷概念

CPU在工作的過程中,經常需要與外設進行互動,互動的方式包括”輪詢方式”,”中斷方式”。
輪詢方式
CPU不斷地查詢裝置的狀態。該方式實現比較簡單,但CPU利用率很低,不適合多工的系統。
中斷方式
CPU在告知硬體開始一項工作後,就去做別的事去了,當硬體完成了該項工作後,向CPU傳送一個訊號,告知CPU它已經完成了這項工作。

2.中斷生命週期

這裡寫圖片描述

事件會送到中斷控制器裡面,中斷控制器對其進行過濾,然後將其送到CPU核
這裡寫圖片描述

3.中斷源

在中斷的生命週期中,中斷源的作用是負責產生中斷訊號。
S3C2440支援60箇中斷源;
S3C6410支援64箇中斷源;
S5PV210支援93箇中斷源;
6410的中斷:
6410一共有個兩個中斷組,為VIC0和VIC1,每個VIC支援32箇中斷源,一共64箇中斷源,如下圖所示
這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述
從上面三幅圖可以看到6410的64箇中斷源,同時可以看到有的中斷源例如 INT_EINT0 包含了4箇中斷,主要是為了節約中斷,就用這一個中斷源,當 這四個中斷某一個發生時,中斷源INT_EINT0 就會產生中斷,然後會查詢相應的暫存器來判斷是發生了那個中斷。

4.中斷處理方式

4.1非向量方式(2440)

這裡寫圖片描述
如上圖,非向量中斷,當中斷產生的時候,會跳轉到一個統一的入口,然後在這個入口函式中判斷是那個中斷髮生了,然後再去執行相應的中斷處理程式處去

4.2向量方式(6410/210)

這裡寫圖片描述
向量中斷,就是當中斷髮生的時候,硬體自動判斷到是那個中斷產生,然後就會跳轉到相應的中斷暫存器裡面,中斷暫存器裡面存放著這個中斷的處理程式的地址,然後根據這個地址,就是我們的中斷處理程式了。

二、6410中斷程式設計分析

其總體流程如下所示:
這裡寫圖片描述

1.初始化按鍵

這裡寫圖片描述

這裡寫圖片描述
從上面兩幅圖可以看到,ok6410的6個按鍵對應的GPIO分別為GPN0 ~ GPN5
這裡寫圖片描述
根據上圖,對按鍵1和按鍵6進行操作,需要將控制暫存器設為外部中斷模式,因此要設定為0b10

#define GPNCON (volatile unsigned long*)0x7f008830
void button_init()
{
    *(GPNCON) = 0b10 | (0b10<<10);        //設定按鍵1與按鍵6 外部中斷
}

2.初始化中斷控制器

2.1配置為下降沿觸發

這裡寫圖片描述

#define
EXT_INT_0_CON *((volatile unsigned int *)0x7f008900)

這裡寫圖片描述
將EINT0 和 EINT5 設定為下降沿觸發
EXT_INT_0_CON = (0b010)|(0b010<<8); /* 配置按鍵1和按鍵6為下降沿觸發 */

2.2取消中斷遮蔽

這裡寫圖片描述

#define EXT_INT_0_MASK      *((volatile unsigned int *)0x7f008920) 

這裡寫圖片描述
使能中斷,則使暫存器的值為0即可

EXT_INT_0_MASK = 0;                               /* 取消遮蔽外部中斷 */ 

2.3使能中斷向量控制器

這裡寫圖片描述

#define VIC0INTENABLE       *((volatile unsigned int *)0x71200010)   

這裡寫圖片描述

這裡寫圖片描述
根據上面兩幅圖,使能中斷暫存器需要設定其值為1,同時要設定EINT0 EINT5,則需要使能中斷源INT_EINT0 INT_EINT1

VIC0INTENABLE |= (0b1)|(0b10);                     /* 使能外部中斷*/  

2.4 設定CPSR狀態暫存器,開啟總中斷

    __asm__( 
    "mrs r0,cpsr\n"               /*讀出值*/
    "bic r0, r0, #0x80\n"
    "msr cpsr_c, r0\n"           /*寫入*/ 
    : 
    : 

2.5使能向量中斷

向量中斷的方式,當中斷產生的時候,並沒有統一的入口,6410的64箇中斷有64個相對應的暫存器,每個暫存器裡面存放著相對應的中斷處理程式的地址,當中斷產生的時候,硬體自己判斷髮生了那個中斷,cpu就直接從發生中斷對應的暫存器中取出處理程式的地址,從而處理中斷。

這裡寫圖片描述

這裡寫圖片描述


#define EINT0_VECTADDR      *((volatile unsigned int *)0x71200100)  
#define EINT5_VECTADDR      *((volatile unsigned int *)0x71200104)  

    EINT0_VECTADDR = (int)key1_handle;         
    EINT5_VECTADDR = (int)key6_handle;

使能向量中斷,6410也可向下相容2440非向量中斷方式,因此需要設定其為向量中斷方式

   __asm__( 

    "mrc p15,0,r0,c1,c0,0\n"    /*使能向量中斷*/
    "orr r0,r0,#(1<<24)\n"        
    "mcr p15,0,r0,c1,c0,0\n"
    : 
    : 
  );

3.中斷處理

3.1儲存環境

儲存環境變數到棧上

    __asm__( 

    "sub lr, lr, #4\n"  
    "stmfd sp!, {r0-r12, lr}\n"       
    : 
    : 
   );

3.2中斷處理

led_on(); //點亮led

3.3清除中斷

這裡寫圖片描述
此暫存器的功能是當發生中斷後,就會產生計數,從0變為1
這裡寫圖片描述
當處理完中斷之後,需要清除掉這次中斷,使其恢復到原始的狀態。

#define EXT_INT_0_PEND      *((volatile unsigned int *)0x7f008924) 
EXT_INT_0_PEND = ~0x0;  

當產生中斷的時候,中斷暫存器裡面的地址也會放在VIC0ADDRESS 裡面,因此在處理完中斷的時候,也需要將其清零。
這裡寫圖片描述

#define VIC0ADDRESS         *((volatile unsigned int *)0x71200f00)   
#define VIC1ADDRESS         *((volatile unsigned int *)0x71300f00)
    VIC0ADDRESS = 0; 
    VIC1ADDRESS = 0; 

3.4恢復環境

    __asm__( 
    "ldmfd sp!, {r0-r12, pc}^ \n"       
    : 
    : 
  );

4.棧的處理

中斷環境下的棧和SVC模式下的棧有所不同,因此需要設定中斷模式下的棧

init_stack:
    msr cpsr_c, #0xd2    @進入中斷模式
    ldr sp, =0x53000000  @初始化r13_irq

    msr cpsr_c, #0xd3    @進入svc模式
    ldr sp,=0x54000000   @初始化r13_svc  50000000 + 64 MB

    mov pc,lr

貼上這次改動過的程式碼:
button.c

/********************************************
*file name: button.c
*author   : stone
*date     : 2016.7.3
*function : 按鍵中斷進行相關的操作
*********************************************/

#define GPNCON (volatile unsigned long*)0x7f008830

void button_init()
{
    *(GPNCON) = 0b10 | (0b10<<10);        //設定按鍵1與按鍵6 為外部中斷模式
}

interrupt.c

/********************************************
*file name: interrupt.c
*author   : stone
*date     : 2016.7.3
*function : 中斷處理
*********************************************/
#define EXT_INT_0_CON       *((volatile unsigned int *)0x7f008900) //外部中斷配置暫存器
#define EXT_INT_0_MASK      *((volatile unsigned int *)0x7f008920) //外部中斷遮蔽暫存器
#define EXT_INT_0_PEND      *((volatile unsigned int *)0x7f008924) //外部中斷0懸掛暫存器

#define VIC0INTENABLE       *((volatile unsigned int *)0x71200010)  //中斷使能暫存器

#define EINT0_VECTADDR      *((volatile unsigned int *)0x71200100)  //向量地址0暫存器
#define EINT5_VECTADDR      *((volatile unsigned int *)0x71200104)  //向量地址1暫存器
#define VIC0ADDRESS         *((volatile unsigned int *)0x71200f00)  //向量地址暫存器
#define VIC1ADDRESS         *((volatile unsigned int *)0x71300f00)

//按鍵1處理程式
void key1_handle()
{
    __asm__(                  /*3.1儲存環境*/

    "sub lr, lr, #4\n"  
    "stmfd sp!, {r0-r12, lr}\n"       
    : 
    : 
   );

    //3.2中斷處理程式
    led_on();

    //3.3清除中斷 
    EXT_INT_0_PEND = ~0x0;  
    VIC0ADDRESS = 0; 
    VIC1ADDRESS = 0; 

   //3.4恢復環境
    __asm__( 
    "ldmfd sp!, {r0-r12, pc}^ \n"       
    : 
    : 
  );

}

//按鍵6處理程式
void key6_handle()
{
    __asm__( 

    "sub lr, lr, #4\n"  
    "stmfd sp!, {r0-r12, lr}\n"       
    : 
    : 
  );
    led_off();

    //清除中斷 
    EXT_INT_0_PEND = ~0x0; 
    VIC0ADDRESS = 0; 
    VIC1ADDRESS = 0;   

    __asm__( 
    "ldmfd sp!, {r0-r12, pc}^ \n"       
    : 
    : 
  ); 
}

//中斷初始化
void init_irq()
{ 
    EXT_INT_0_CON = (0b010)|(0b010<<8);         /* 2.1 配置按鍵1和按鍵6為下降沿觸發 */  

    EXT_INT_0_MASK = 0;                         /* 2.2取消遮蔽外部中斷 */  

    VIC0INTENABLE |= (0b1)|(0b10);              /* 2.3使能外部中斷*/  

    EINT0_VECTADDR = (int)key1_handle;          /* 2.5使用者按下key時,CPU就會自動的將VIC0VECTADDR0的值賦給VIC0ADDRESS並跳轉到這個地址去執 */  
    EINT5_VECTADDR = (int)key6_handle;


    __asm__( 

    "mrc p15,0,r0,c1,c0,0\n"                   /*2.5使能向量中斷*/
    "orr r0,r0,#(1<<24)\n"
    "mcr p15,0,r0,c1,c0,0\n"

    "mrs r0,cpsr\n"                            /*2.4設定cpsr狀態暫存器*/
    "bic r0, r0, #0x80\n"
    "msr cpsr_c, r0\n"            
    : 
    : 
  );
}

main.c

/********************************************
*file name: main.c
*author   : stone
*date     : 2016.7.3
*function : 總程式
*********************************************/

int gboot_main()
{

    /*mmu 初始化,暫時不用*/
    #ifdef MMU_ON
      mmu_init();  
    #endif

    /*led初始化*/
        led_init();

    /*中斷初始化*/
    init_irq();

    /*按鍵初始化*/
    button_init();

    //led_off();

    while(1);

    return 0;    
}

mmu.c

/********************************************
*file name: mmu.c
*author   : stone
*date     : 2016.7.3
*function : mmu的初始化
*********************************************/
#define MMU_FULL_ACCESS     (3 << 10)   /* 訪問許可權 [11:10]*/
#define MMU_DOMAIN          (0 << 5)    /* 屬於哪個域 [8:5]*/
#define MMU_SPECIAL         (1 << 4)    /* 必須是1 [4]*/
#define MMU_CACHEABLE       (1 << 3)    /* cacheable  [3]*/
#define MMU_BUFFERABLE      (1 << 2)    /* bufferable [2]*/
#define MMU_SECTION         (2)         /* 表示這是段描述符  [1:0]*/
#define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)
#define MMU_SECDESC_WB      (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)

void creat_page_table()
{
    unsigned long *ttb = (unsigned long *)0x50000000; //表在記憶體的基地址處
    unsigned long vaddr; //虛擬地址
    unsigned long paddr; //實體地址

    vaddr = 0xa0000000; //虛擬地址
    paddr = 0x7F000000;
    *(ttb + (vaddr >> 20)) = (paddr&0xfff00000) | MMU_SECDESC;
    //*(ttb + (vaddr >> 20))  為表項的位置 
    //(paddr&0xfff00000) 獲取高12位資料
    //MMU_SECDESC  訪問led的gpio很簡單,就不需要cache和buffer

    //對映記憶體
    vaddr = 0x50000000;
    paddr = 0x50000000;  //其虛擬地址和實體地址是一致的
    while (vaddr < 0x54000000)  //對映64mb
    {
        *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;
        vaddr += 0x100000;
        paddr += 0x100000;
    }   

}

void mmu_enable()
{
    __asm__(

    /*設定TTB*/
    "ldr    r0, =0x50000000\n"     /* 頁表的基地址   */         
    "mcr    p15, 0, r0, c2, c0, 0\n"    

    /*不進行許可權檢查*//*cp15 c3 domain 控制權限*/
    "mvn    r0, #0\n"                   
    "mcr    p15, 0, r0, c3, c0, 0\n"    

   /*使能MMU*/
    "mrc    p15, 0, r0, c1, c0, 0\n"    
    "orr    r0, r0, #0x0001\n"          
    "mcr    p15, 0, r0, c1, c0, 0\n"    
    : 
    : 
  );
}
void mmu_init()
{
    create_page_table();
    mmu_enable();
}

菜鳥一枚,如有錯誤,多多指教。。。