1. 程式人生 > >s5pv210——中斷實戰

s5pv210——中斷實戰

一、s5pv210的中斷步驟

(1)建立異常向量表;

(2)中斷初始化;

(3)使能(如外部中斷,寫中斷處理函式);

(4)建立中斷號與中斷處理函式的聯絡,使能。

當中斷髮生時,中斷處理函式會自動處理中斷;

流程如下圖:


二、程式碼

1、建立異常向量表

/*
 *	s5pv210 裸機
 *
 *  異常向量表初始化
 *
 */
#define VECTOR_TABLE_BASE			0xD0037400
#define Reset_offset				0x0
#define Undef_offset				0x4
#define SVC_offset					0x8
#define Prectch_offset				0xC
#define Data_Abort_offset			0x10
#define IRQ_offset					0x18
#define FIQ_offset					0x1C

#define _PFUNC_Reset		(*(unsigned int*)(VECTOR_TABLE_BASE+Reset_offset))
#define _PFUNC_Undef		(*(unsigned int*)(VECTOR_TABLE_BASE+Undef_offset))
#define _PFUNC_SVC			(*(unsigned int*)(VECTOR_TABLE_BASE+SVC_offset))
#define _PFUNC_Prectch		(*(unsigned int*)(VECTOR_TABLE_BASE+Prectch_offset))
#define _PFUNC_Data_Abort	(*(unsigned int*)(VECTOR_TABLE_BASE+Data_Abort_offset))
#define _PFUNC_IRQ			(*(unsigned int*)(VECTOR_TABLE_BASE+IRQ_offset))
#define _PFUNC_FIQ			(*(unsigned int*)(VECTOR_TABLE_BASE+FIQ_offset))
extern void IRQ_handle(void);

void Reset_handle(void)
{
	
}
void Undef_handle(void)
{
	
}
void SVC_handle(void)
{
	
}
void Prectch_handle(void)
{
	
}
void Data_Abort_handle(void)
{
	
}
 
 

void vector_table_init(void)
{	
	_PFUNC_Reset		=   (unsigned int)Reset_handle;
	_PFUNC_Undef		=   (unsigned int)Undef_handle;
	_PFUNC_SVC		=   (unsigned int)SVC_handle;
	_PFUNC_Prectch		=     (unsigned int)Prectch_handle;
	_PFUNC_Data_Abort	=     (unsigned int)Data_Abort_handle;
	_PFUNC_IRQ		=   (unsigned int)IRQ_handle;//但凡是中斷,都會進入到此模式
	_PFUNC_FIQ		=   (unsigned int)IRQ_handle;//FIQ、IRQ都是採用IRQ中斷
	
}

其中,IRQ_handle要寫在彙編IRQ_handle.S中(因為要設定棧,以及儲存現場 ),如下
 #define IRQ_STACK        0xD0037F80 
    
    .global IRQ_handle
IRQ_handle:

    //設定IRQ的棧
    ldr sp, =IRQ_STACK

    //由於三級流水線的存在,pc為此時的程式語句+8,儲存的時候要把下一句儲存到lr中
    sub lr, lr, #4
    
    //儲存現場
    stmd sp! {r0-r12, lr}
    
    //跳轉到中斷處理函式
    bl isr_handler
    
    //恢復現場
    ldmfd sp! {r0-r8, pc}^//這裡為什麼不是恢復r0~r12

ARM儲存中斷時為什麼使用 sub lr, lr, #4?

(1)首先要談流水線,在arm執行過程中一般分為取指,譯碼,執行階段;

  • 假設當前第一條指令在執行階段,第二條指令在譯碼階段,第三條指令在取指階段;
  • 若當前正在執行的指令地址為pc-8,那第二條就為pc-4,而pc指向取址。

(2)一般pc在發生中斷時lr儲存的是當前的pc值,這裡pc值是多少呢?

  • 當發生中斷時,肯定儲存的pc是第三條指令,而我們從中斷返回肯定不是執行第三條指令,而是緊接著的第二條指令,所以應該儲存的 lr = pc - 4,(但是當執行到此位置時pc值已經改變,肯定不對,還好發生中斷時 mov lr,pc ,)所以這裡就可以直接使用 sub lr,lr,#4,即lr=pc-4。

2、中斷初始化

//清除4箇中斷處理函式
void clean_vicaddress(void)
{
    _REG_VIC0ADDRESS = 0x0;
    _REG_VIC1ADDRESS = 0x0;
    _REG_VIC2ADDRESS = 0x0;
    _REG_VIC3ADDRESS = 0x0;
    
}


 
void interrupt_init(void)
{
    //第一步初始化中斷之前要關閉所有中斷
    _REG_VIC0INTENCLEAR = 0xFFFFFFFF;
    _REG_VIC1INTENCLEAR = 0xFFFFFFFF;
    _REG_VIC2INTENCLEAR = 0xFFFFFFFF;
    _REG_VIC3INTENCLEAR = 0xFFFFFFFF;
    
    //第三步:設定中斷為IRQ中斷
    _REG_VIC0INTSELECT = 0x0;
    _REG_VIC1INTSELECT = 0x0;
    _REG_VIC2INTSELECT = 0x0;
    _REG_VIC3INTSELECT = 0x0;
    
    //第三步:清中斷處理函式地址
    clean_vicaddress();
    
}

void int_disable(unsigned int num)
{
    if (num < 32) {
        _REG_VIC0INTENCLEAR = (0x1<<num);
    }
    else if (num < 64) {
        _REG_VIC1INTENCLEAR = (0x1<<(num-32));        
    }
    else if (num < 96) {
        _REG_VIC2INTENCLEAR = (0x1<<(num-64));
    }
    else if (num < 128) {
        _REG_VIC3INTENCLEAR = (0x1<<(num-96));
    }
    else {

    }
        
}
 
void int_enable(unsigned int num)
{
    if (num < 32) {
        _REG_VIC0INTENABLE = (0x1<<num);
    }
    else if (num < 64) {
        _REG_VIC1INTENABLE = (0x1<<(num-32));        
    }
    else if (num < 96) {
        _REG_VIC2INTENABLE = (0x1<<(num-64));
    }
    else if (num < 128) {
        _REG_VIC3INTENABLE = (0x1<<(num-96));
    }
    else {
        _REG_VIC0INTENABLE = 0xFFFFFFFF;
        _REG_VIC1INTENABLE = 0xFFFFFFFF;
        _REG_VIC2INTENABLE = 0xFFFFFFFF;
        _REG_VIC3INTENABLE = 0xFFFFFFFF;
    }
    
}
 
void creat_israddr(unsigned int num, void (*PIRQ_handler)(void))
{
    
    if (num < 32) {        
        //*( (void (*)(void))(VIC0VECTADDR + 4*num) )= PIRQ_handler;
        *( (volatile unsigned long *)(VIC0VECTADDR + 4*(num-0)) ) = (unsigned)PIRQ_handler;
        
    }
    else if (num < 64) {
        //(void (*)(void))(VIC1VECTADDR + 4*(num-32))= PIRQ_handler;
        *( (volatile unsigned long *)(VIC1VECTADDR + 4*(num-32)) ) = (unsigned)PIRQ_handler;
    }
    else if (num < 96) {
        //(void (*)(void))(VIC2VECTADDR + 4*(num-64))= PIRQ_handler;
        *( (volatile unsigned long *)(VIC2VECTADDR + 4*(num-64)) ) = (unsigned)PIRQ_handler;
    }
    else {
        //(void (*)(void))(VIC3VECTADDR + 4*(num-96))= PIRQ_handler;
        *( (volatile unsigned long *)(VIC3VECTADDR + 4*(num-96)) ) = (unsigned)PIRQ_handler;
    }
        
}
 
//判斷中斷在哪個address中

static int check_int_addr(void)
{
    if (_REG_VIC0IRQSTATUS) {
        return 0;
    }
    else if (_REG_VIC1IRQSTATUS) {
        return 1;
    }
    else if (_REG_VIC2IRQSTATUS) {
        return 2;
    }
    else if (_REG_VIC3IRQSTATUS) {
        return 3;
    }
    else {
        return -1;
    }
    
} 
 

void isr_handler(void)
{
    void (*p_isr)(void) = NULL;
    int i;
    
    i = check_int_addr();
    switch (i) {
        case 0 :
            p_isr = (void (*)(void))_REG_VIC0ADDRESS;
            break;
        case 1 :
            p_isr = (void (*)(void))_REG_VIC1ADDRESS;
            break;
        case 2 :
            p_isr = (void (*)(void))_REG_VIC2ADDRESS;
            break;
        case 3 :
            p_isr = (void (*)(void))_REG_VIC2ADDRESS;
            break;
        default :
            break;
    }
    
    p_isr();
    
}

3、使能外部中斷中斷處理函式

void int_led_blink(void)
{
   //中斷處理函式 
  led_blink();
  //清楚外部中斷掛起,注意寫1清掛起
    clean_int_pend();
   //清vicaddress
  clean_vicaddress(); 
}

4、建立中斷號與中斷函式聯絡,使能中斷

#include "interrupt.h"
#include "stdio.h"
extern void led_blink(void);
extern void led1_on(void);
extern void vector_table_init(void);
extern void key_init(void);
extern void uart_init(void);

int main(void)
{
    //按鍵初始化
    key_inter_init();
    
    //異常向量表初始化
    vector_table_init();
    
    //中斷初始化
    interrupt_init();
    
    
    //建立函式
    creat_israddr(NUM_EINT2, int_led_blink);
    creat_israddr(NUM_EINT3, int_led_blink);
    creat_israddr(NUM_EINT16_31, int_led_blink);
    
    
    //使能中斷
    int_enable(NUM_EINT2);
    int_enable(NUM_EINT3);
    int_enable(NUM_EINT16_31);
    
    
    while (1) {
        printf("a");
    }
    
}


小總結:

一、S5PV210中斷處理的程式設計實踐

2、中斷控制器初始化

  • 主要工作有:第一階段繫結異常向量表到異常處理程式;禁止所有中斷源;選擇所有中斷型別為IRQ;清理VICnADDR暫存器為0.

3、中斷的使能與禁止

  • 思路是先根據中斷號判斷這個中斷屬於VIC幾,然後在用中斷源減去這個VIC的偏移量,得到這個中斷號在本VIC中的偏移量,然後1<<x位,寫入相應的VIC的INTENABLE/INTENCLEAR暫存器即可。

4、繫結自己實現的isr到VICnVECTADDR

  • 搞清楚2個暫存器的區別:VICnVECTADDR和VICnADDR
  • VICVECTADDR暫存器一共有4×32個,每個中斷源都有一個VECTADDR暫存器,將自己為這個中斷源寫的isr地址丟到這個中斷源對應的VECTADDR暫存器中即可。

5、真正的中斷處理程式如何獲取isr

  • 發生中斷時,硬體自動把相應中斷源的isr地址從VICnVECTADDR暫存器推到VICnADDR暫存器中,所以只需要到相應的VICnADDR中去拿出isr地址,呼叫執行即可。
  • 第4步繫結isr地址到VICnVECTADDR,以及第5步中斷髮生時第二階段的第二階段如何獲取isr地址,這兩步是相關的。這兩個的結合技術,就是x210的硬體自動尋找isr的機制。

二、整個中斷的流程梳理:

整個中斷的工作分為2部分:

第一部分是我們為中斷響應而做的預備工作:

1. 初始化中斷控制器

2. 繫結寫好的isr到中斷控制器

3. 相應中斷的所有條件使能

第二部分是當硬體產生中斷後如何自動執行isr:

1. 第一步,經過異常向量表跳轉入IRQ/FIQ的入口

2. 第二步,做中斷現場保護(在start.S中),然後跳入isr_handler

3. 第三步,在isr_handler中先去搞清楚是哪個VIC中斷了,然後直接去這個VIC的ADDR暫存器中取isr來執行即可。

4. 第四步,isr執行完,中斷現場恢復,直接返回繼續做常規任務。