1. 程式人生 > >[嵌入式]異常與中斷(下)

[嵌入式]異常與中斷(下)

異常與中斷

6.5  S5PV210的中斷程式設計

中斷跳轉流程

ARM處理器響應中斷的時候,總是從固定的中斷異常向量取地址開始的,而在高階語言環境下開發中斷服務程式時,無法控制從固定地址處開始至中斷服務程式的跳轉流程。為了使得上層應用程式與硬體中斷跳轉聯絡起來,需要編寫一段中間的服務程式來進行連線。這樣的服務程式常被稱作中斷解析程式。

    每個異常向量對應一個4位元組的空間,正好放置一條跳轉指令或者向PC暫存器賦值的資料訪問指令。具體中斷跳轉流程如圖。

 

中斷程式設計步驟

·建立系統中斷向量表,設定微處理器核心的程式狀態暫存器CPSR中的F位和I位。一般

情況下中斷均需使用資料棧,因此還需建立使用者資料棧。這一部分內容對應的程式指令,

通常編寫在系統載入程式中。

·設定各中斷源的中斷向量。通常需要利用向量地址暫存器來計算,若中斷號還對應有子中

斷,需求出子中斷地址偏移。

·中斷控制初始化。主要是初始化微處理器內部的中斷控制的暫存器。針對某個具體的中斷

源,設定其中斷控制模式、中斷是否遮蔽、中斷優先順序等。

·完成I/O埠或部件具體操作功能的中斷服務程式。中斷服務程式中,在返回之前必須清

除現場,返回中斷前的狀態。

中斷示例硬體電路

S5PV210 共支援 93 箇中斷源,這裡將使能其中的八個外部中斷。該示例通過中斷XEINT16-XEINT27連線八隻獨立按鍵,響應按鍵動作,驅動蜂鳴器鳴響和相應的LED燈亮。其中一隻按鍵電路和蜂鳴器電路如圖。

start.S 彙編啟動程式碼

.text

.global _start

.global irq_handler

_start:

breset

ldrpc,_undefined_instruction

ldrpc,_software_interrupt

ldrpc,_prefetch_abort

ldrpc,_data_abort

ldrpc,_not_used

ldrpc,_irq

ldrpc,_fiq

_undefined_instruction: .word  _undefined_instruction

_software_interrupt:       .word  _software_interrupt

_prefetch_abort:    .word  _prefetch_abort

_data_abort:.word  _data_abort

_not_used:.word  _not_used

_irq:.word  _irq

_fiq:.word  _fiq

reset:

mrsr0,cpsr

bicr0,r0,#0x1f

orrr0,r0,#0xd3

msrcpsr,r0

init_stack:

ldrr0,stacktop

/********svc mode stack********/

movsp,r0

subr0,#128*4          //

/****irq mode stack**/

msrcpsr,#0xd2

movsp,r0

subr0,#128*4

/***fiq mode stack***/

msrcpsr,#0xd1

movsp,r0

subr0,#0

/***abort mode stack***/

msrcpsr,#0xd7

movsp,r0

subr0,#0

/***undefine mode stack***/

msrcpsr,#0xdb

movsp,r0

subr0,#0

/*** sys mode and usr mode stack ***/

msrcpsr,#0x10

movsp,r0 bmain

.align5

irq_handler:

sub  lr,lr,#4

stmfd sp!,{r0-r12,lr}

bldo_irq

ldmfd sp!,{r0-r12,pc}^

stacktop:    .word stack+4*512

.data

Stack:.space4*512

main.c 原始碼

#include "lib\stdio.h"

#include "int.h"

#define    GPH2CON (*(volatile unsigned long *) 0xE0200C40)

#define   GPH2DAT(*(volatile unsigned long *) 0xE0200C44)

#define   GPH2_0_EINT16 (0xf<<(0*4))

#define   GPH2_1_EINT17 (0xf<<(1*4))

#define   GPH2_2_EINT18 (0xf<<(2*4))

#define   GPH2_3_EINT19 (0xf<<(3*4))

#defineEXT_INT_0_CON  ( *((volatile unsigned long *)0xE0200E00) )

#defineEXT_INT_1_CON  ( *((volatile unsigned long *)0xE0200E04) )

#defineEXT_INT_2_CON  ( *((volatile unsigned long *)0xE0200E08) )

#defineEXT_INT_3_CON  ( *((volatile unsigned long *)0xE0200E0C) )

#defineEXT_INT_0_MASK   ( *((volatile unsigned long *)0xE0200F00) )

#defineEXT_INT_1_MASK   ( *((volatile unsigned long *)0xE0200F04) )

#defineEXT_INT_2_MASK  ( *((volatile unsigned long *)0xE0200F08) )

#defineEXT_INT_3_MASK   ( *((volatile unsigned long *)0xE0200F0C) )

#defineEXT_INT_0_PEND   ( *((volatile unsigned long *)0xE0200F40) )

……(以下暫存器地址宣告略)

void uart_init();

// 延時函式

void delay(unsigned long count)

{

volatile unsigned long i = count;   

while (i--);    

}

void isr_key(void)

{

printf("we get company:EINT16_31\r\n");

intc_clearvectaddr(); // clear VIC0ADDR

EXT_INT_2_PEND |= 1<<0;// clear pending bit

}

int main(void)

{

int c = 0;

uart_init(); // 初始化串列埠

system_initexception(); // 中斷相關初始化printf("**************Int test *************** \r\n");

// 外部中斷相關的設定

GPH2CON |= 0xF; // 1111 = EXT_INT[16] EXT_INT_2_CON |= 1<<1;// 010 = Falling edge triggered EXT_INT_2_MASK &= ~(1<<0); // unmasked

// 設定中斷EINT16_31的處理函式

intc_setvectaddr(NUM_EINT16_31, isr_key);

// 使能中斷EINT16_31

intc_enable(NUM_EINT16_31);

while (1)

{   printf("%d\r\n",c++);

delay(0x100000);

}

}

int.c 原始碼

#include "int.h"

#include "lib\stdio.h"

//// Interrupt

#define VIC0_BASE(0xF2000000)

#define VIC1_BASE(0xF2100000)

#define VIC2_BASE(0xF2200000)

#define VIC3_BASE(0xF2300000)

// VIC0

#defineVIC0IRQSTATUS(*((volatile unsigned long *)(VIC0_BASE + 0x00)) )

……(以下暫存器地址宣告略)

void exceptionundef(void)

{printf("undefined instruction exception.\n");

while(1)  ;

}

……(以下未定義具體操作的異常處理程式略)

// 中斷相關初始化

void system_initexception( void)

{    // 設定中斷向量表

pExceptionUNDEF  =(unsigned long)exceptionundef;

pExceptionSWI  =(unsigned long)exceptionswi;

pExceptionPABORT  =(unsigned long)exceptionpabort;

pExceptionDABORT  =(unsigned long)exceptiondabort;

pExceptionIRQ  =(unsigned long)irq_handler;

pExceptionFIQ  =(unsigned long)irq_handler;

// 初始化中斷控制器

intc_init();

}

// 初始化中斷控制器

void intc_init(void)

{// 禁止所有中斷

VIC0INTENCLEAR = 0xffffffff;VIC1INTENCLEAR = 0xffffffff;

VIC2INTENCLEAR = 0xffffffff;VIC3INTENCLEAR = 0xffffffff;

// 選擇中斷型別為IRQ

VIC0INTSELECT = 0x0;VIC1INTSELECT = 0x0;

VIC2INTSELECT = 0x0;VIC3INTSELECT = 0x0;

// 清VICxADDR

intc_clearvectaddr();

}

// 儲存需要處理的中斷的中斷處理函式的地址

void intc_setvectaddr(unsigned long intnum, void (*handler)(void))

{//VIC0

if(intnum<32)

{

*( (volatile unsigned long *)(VIC0VECTADDR + 4*intnum) ) = (unsigned)handler;

}

                  ……其他向量控制器程式略。

return;

}

// 清除需要處理的中斷的中斷處理函式的地址

void intc_clearvectaddr(void)

{// VICxADDR:當前正在處理的中斷的中斷處理函式的地址

VIC0ADDR = 0;

VIC1ADDR = 0;

VIC2ADDR = 0;

VIC3ADDR = 0;

}

// 使能中斷

void intc_enable(unsigned long intnum)

{……具體程式略。}

// 禁止中斷

void intc_disable(unsigned long intnum)

{……具體程式略。

// 讀中斷狀態

unsigned long intc_getvicirqstatus(unsigned long ucontroller)

{if(ucontroller == 0)

returnVIC0IRQSTATUS;

else if(ucontroller == 1)

returnVIC1IRQSTATUS;

else if(ucontroller == 2)

returnVIC2IRQSTATUS;

else if(ucontroller == 3)

returnVIC3IRQSTATUS;

else    {}

return 0;

}

// 通用中斷處理函式

void do_irq(void)

{

unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};

int i=0;

void (*isr)(void) = NULL;

for(; i<4; i++)

 {

if(intc_getvicirqstatus(i) != 0)

 {

isr = (void (*)(void)) vicaddr[i];

break;

  }

 }

  (*isr)();

}