[嵌入式]異常與中斷(下)
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)();
}