第一節(jié) 關(guān)于S5PV210的中斷體系結(jié)構(gòu)
S5PV210的中斷控制器是由4個(gè)向量中斷控制器(VIC)、ARM PrimeCell PL192 和 4個(gè) TrustZone Interrupt Controller (TZIC)共同組成。 S5PV210共支持93個(gè)中斷源,待會(huì)我們將使能其中的一個(gè)外部中斷,讓大家了解中斷處理的完整過(guò)程。
第二節(jié) 程序相關(guān)講解
完整代碼見(jiàn)目錄詳細(xì)代碼下載鏈接。
1. start.S
共4個(gè)步驟,其中第2、4步和中斷相關(guān):
第一步 清bss;
第二步 開(kāi)中斷,設(shè)置CPSR 寄存器,允許中斷發(fā)生,代碼如下:
mov r0, #0x53
msr CPSR_cxsf, r0
第三步 跳轉(zhuǎn)到main;
第四步 中斷處理;程序正常執(zhí)行時(shí),只會(huì)運(yùn)行到第三步就跳轉(zhuǎn)到main而不會(huì)執(zhí)行該部分代碼。當(dāng)有中斷發(fā)生時(shí),PC才會(huì)跳轉(zhuǎn)到該部分代碼,進(jìn)行中斷相關(guān)的處理。別急,后面會(huì)詳細(xì)解釋該段代碼。
2. main.c
共4個(gè)步驟,其中第2、3、4步和中斷相關(guān):
第一步 初始化串口;
第二步 中斷相關(guān)初始化,調(diào)用了system_initexception();
代碼如下:
void system_initexception( void)
{
// 設(shè)置中斷向量表
pExceptionUNDEF = (unsigned long)exceptionundef;
pExceptionSWI = (unsigned long)exceptionswi;
pExceptionPABORT = (unsigned long)exceptionpabort;
pExceptionDABORT = (unsigned long)exceptiondabort;
pExceptionIRQ = (unsigned long)IRQ_handle;
pExceptionFIQ = (unsigned long)IRQ_handle;
// 初始化中斷控制器
intc_init();
}
這段代碼主要做了下面兩件事:
1) 設(shè)置中斷向量表
當(dāng)發(fā)生各種異常時(shí),PC會(huì)自動(dòng)跳轉(zhuǎn)到相應(yīng)的異常向量;S5PV210的異常向量表的起始地址是0xD0037400,我們需要特別注意的是,當(dāng)發(fā)生IRQ中斷異常時(shí),對(duì)應(yīng)的處理函數(shù)是IRQ_handle(),即我們?cè)趕tart.S中第四步所設(shè)置的代碼如下:
IRQ_handle:
// 設(shè)置中斷模式的棧
ldr sp, =0xD0037F80
// 保存現(xiàn)場(chǎng)
sub lr, lr, #4
stmfd sp!, {r0-r12, lr}
// 跳轉(zhuǎn)到中斷處理函數(shù)
bl irq_handler
// 恢復(fù)現(xiàn)場(chǎng)
ldmfd sp!, {r0-r12, PC}
注釋已經(jīng)寫得很清楚了,當(dāng)發(fā)生IRQ中斷異常時(shí),會(huì)根據(jù)中斷向量表里的設(shè)置,跳轉(zhuǎn)到該部分代碼,然后設(shè)置中斷模式下的棧,保存現(xiàn)場(chǎng),再調(diào)用中斷處理函數(shù)irq_handler(),處理完中斷后再恢復(fù)現(xiàn)場(chǎng)。irq_handler()里會(huì)怎么做,后面再解釋。
2) 初始化中斷控制器
調(diào)用了函數(shù)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();
}
首先先禁止所有中斷,然后選擇中斷類型為IRQ,最后清寄存器VICxARRD,VICxADDr是用來(lái)保存當(dāng)前發(fā)生的中斷的處理函數(shù)的
第三步 設(shè)置外部中斷相關(guān)寄存器
代碼如下:
// 1111 = EXT_INT[16]
GPH2CON |= 0xF;
// 010 = Falling edge triggered
EXT_INT_2_CON |= 1<<1;
// unmasked
EXT_INT_2_MASK &= ~(1<<0);
首先配置GPH2_0引腳為中斷功能; 然后設(shè)置外部中斷EINT16_31為下降沿觸發(fā);
最后是不屏蔽該中斷;
第四步 設(shè)置VIC相關(guān)寄存器
代碼如下:
// 設(shè)置中斷EINT16_31的處理函數(shù)
intc_setvectaddr(NUM_EINT16_31, isr_key);
// 使能中斷EINT16_31
intc_enable(NUM_EINT16_31);
這里調(diào)用了兩個(gè)函數(shù):
1) intc_setvectaddr()
它的作用是設(shè)置VICVECTADDR這一類寄存器,作用是保存各個(gè)中斷的處理函數(shù),這里我們的外部中斷EINT16_31的中斷處理函數(shù)是isr_key()。
前面提到,在start.S的第四步中,會(huì)調(diào)用函數(shù)irq_handler(),其代碼如下:
void irq_handler(void)
{
unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};
int i=0;
void (*isr)(void);
for(; i<4; i++)
{
if(intc_getvicirqstatus(i) != 0)
{ isr = vicaddr[i]; break; }
}
(*isr)();
}
當(dāng)有中斷發(fā)生時(shí),硬件上會(huì)將當(dāng)前中斷的中斷處理函數(shù)從寄存器VICVECTADDR自動(dòng)拷貝到寄存器VICDDR中,所以我們?cè)趇rq_handler()函數(shù)里會(huì)調(diào)用保存在寄存器VICDDR里的中斷處理函數(shù)即可。 再來(lái)看看外部中斷EINT16_31的處理函數(shù)isr_key(),代碼如下:
void isr_key(void)
{
printf("we get company\r\n");
beep();
// clear VIC0ADDR intc_clearvectaddr();
// clear pending
bit EXT_INT_2_PEND |= 1<<0;
}
首先打印一句"we get company",然后蜂鳴器會(huì)響一下,最后是清VIC相關(guān)寄存器VIC0ADDR和外部中斷相關(guān)寄存器EXT_INT_2_PEND。
2) intc_enable()
在VIC里通過(guò)設(shè)置寄存器VICINTENABLE使能外部中斷EINT16_31。
第五步 死循環(huán)
打印數(shù)字1、2、3、4…,等待外部中斷EINT16_31的發(fā)生。
第三節(jié) 實(shí)驗(yàn)現(xiàn)象
首先會(huì)不斷的打印數(shù)字1、2、3、4...,當(dāng)我們按下KEY1時(shí)會(huì)產(chǎn)生外部中斷EINT16_31時(shí),會(huì)跳轉(zhuǎn)到IRQ_handler,然后調(diào)用irq_handler(),最后調(diào)用對(duì)應(yīng)的中斷處理函數(shù)isr_key()。該函數(shù)首先打印"we get company:EINT16_31",然后清中斷。
詳細(xì)代碼下載鏈接:http://download.csdn.net/detail/klcf0220/5741305
作者:快樂(lè)出發(fā)0220 ,文章出處:http://www.cnblogs.com/klcf0220/ ,文章版權(quán)歸本人所有。歡迎轉(zhuǎn)載,但未經(jīng)作者同意,必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文鏈接,否則將會(huì)追究法律責(zé)任,請(qǐng)自重?。?!另外喜歡開(kāi)源,樂(lè)意分享的大神們,歡迎加入QQ群:176507146,你值的擁有哦?。?!