欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

S3C2440中斷代碼的深層次分析

作者:佚名   來(lái)源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2014年03月12日   【字體:

在前一段時(shí)間分析了ARM異常處理機(jī)制的處理方式,分析了在異常產(chǎn)生以后CPU自動(dòng)完成的相關(guān)處理以及程序員應(yīng)該完成的基本操作。著重分析了異常代碼的返回地址分析已經(jīng)采用通用代碼處理各種異常的可能性。

異常處理的基本過(guò)程如下:異常產(chǎn)生(在指令的臨界中檢測(cè)CPU的狀態(tài),一般實(shí)質(zhì)在這條指令被執(zhí)行完成,但是還沒(méi)有執(zhí)行下一條指令之前檢測(cè))——>保存狀態(tài)寄存器,切換狀態(tài)寄存器,保存LR=PC-4,強(qiáng)制PC跳轉(zhuǎn)到對(duì)應(yīng)異常向量(以上的過(guò)程都是CPU自動(dòng)完成)——》調(diào)整返回地址,在棧中保存寄存器,便于恢復(fù)寄存器的值——》異常處理函數(shù)——》退出異常。

中斷處理機(jī)制的兩種形式:

1、  采用在中斷向量中存儲(chǔ)簡(jiǎn)單的跳轉(zhuǎn)指令,跳轉(zhuǎn)到異常處理函數(shù)中,但是這種方式存在的缺點(diǎn)就是跳轉(zhuǎn)指令的范圍是有局限性的。

2、  采用更新PC值的方法進(jìn)行,具體的實(shí)現(xiàn)形式是在另一個(gè)固定地址處(handle_addr)保存對(duì)應(yīng)異常處理函數(shù)的地址,然后采用LDR PC [PC, offset],其中offset = handle_addr – vect – 0x08;這種機(jī)制只要保證選擇的地址恰當(dāng)就能實(shí)現(xiàn)不同距離的跳轉(zhuǎn)。

以上的分析和處理在上一次中已經(jīng)分析,這次分析中斷的處理過(guò)程,中斷只是異常的一種特殊情況,對(duì)異常的處理得到了好的理解,那么對(duì)中斷的處理也就比較方便了。

ARM內(nèi)核中只支持IRQIFQ兩種類型的中斷,但是不同的廠商提供不同類型的中斷控制器實(shí)現(xiàn)對(duì)中斷的擴(kuò)展,使得實(shí)際的芯片更加適合我們的使用。但是中斷控制器的差別也使得不同廠商的中斷處理也有差別,但是基本的思想是一致的。

 


 

S3C2440的中斷控制器一個(gè)支持60種中斷源,基本的實(shí)現(xiàn)如上圖所示;镜募拇嫫靼SRCPND、INTPND(有且僅有1bit會(huì)被置位,可以通過(guò)這個(gè)寄存器判斷中斷源,找出那個(gè)IRQ源發(fā)生中斷)、INTMOD、INTMSKPRIORITY(用來(lái)改變中斷的優(yōu)先級(jí)順序,但是其中還是存在一些固有的順序,具體的參看手冊(cè))、INTOFFSET(用來(lái)表示IRQINTPND的那個(gè)bit被置位,這樣每一類的中斷源都存在一個(gè)固定的偏移量,這個(gè)寄存器可以用來(lái)用來(lái)計(jì)算偏移量以及通過(guò)這個(gè)偏移量找到對(duì)應(yīng)的中斷處理函數(shù)地址存儲(chǔ)位置等),當(dāng)然也存在一些關(guān)于多個(gè)中斷源構(gòu)成的子中斷寄存器,SUBSRCPND、INTSUBMSK

 

S3C2440的啟動(dòng)代碼中描述了關(guān)于中斷處理過(guò)程的基本過(guò)程和原理。

首先需要搞清楚下面的一個(gè)宏定義:

       MACRO

$HandlerLabel HANDLER $HandleLabel

$HandlerLabel

      sub  sp,sp,#4  ;decrement sp(to store jump address)

       stmfd     sp!,{r0}   ;PUSH the work register to stack(lr does not push because it return to original address)

       ldr     r0,=$HandleLabel;load the address of HandleXXX to r0

       ldr     r0,[r0]     ;load the contents(service routine start address) of HandleXXX

       str     r0,[sp,#4]      ;store the contents(ISR) of HandleXXX to stack

       ldmfd   sp!,{r0,pc}     ;POP the work register and pc(jump to ISR)

       MEND

 

1、搞清楚ARM中的MACRO偽指令,這個(gè)偽指令就是我們?cè)趨R編中的宏定義,我們都知道宏的實(shí)現(xiàn)能夠避免代碼的重復(fù)型以及代碼的可修復(fù)性。關(guān)于ARM匯編中的宏定義基本的形式如下:

       MACRO

              {$label} macroname {$parameter} {$parameter}…

              Code

       MEND

       其中$label 宏指令被展開(kāi)時(shí),label可被替換為相應(yīng)的符號(hào),一般為一個(gè)標(biāo)號(hào)

       macroname 所定義的宏的名稱

       $parameter 宏指令的參數(shù),當(dāng)宏指令被展開(kāi)時(shí)被替換成對(duì)應(yīng)的值。

2、依據(jù)上面的定義我們可以知道當(dāng)前這段代碼定義了一個(gè)宏指令,HANDLER,其中標(biāo)號(hào)為$HandlerLabel,參數(shù)為$HandleLabel

基本的實(shí)現(xiàn)代碼分析如下:

sub  sp,sp,#4;               在棧中預(yù)留一個(gè)區(qū)域,用來(lái)保存PC的值

stmfd      sp!,{r0}  ;      由于r0還需要被使用,因此需要被壓棧

ldr     r0,=$HandleLabel ;這里的ldr是一個(gè)偽指令,主要是將標(biāo)號(hào)$HandleLabel的地址加載到r0中,這也是壓棧r0的原因。

ldr     r0,[r0]            ;這是ARMldr指令,主要是將$HandleLabel對(duì)應(yīng)地址中的內(nèi)容加載到r0中。如果在$HandleLabel中保存的是一個(gè)中斷處理函數(shù)的地址,那么只需要將這個(gè)值加載到PC即可實(shí)現(xiàn)了中斷任務(wù)跳轉(zhuǎn),實(shí)際上這個(gè)過(guò)程就是采用了異常處理的第二種方式:

即加載PC的方式,而不是簡(jiǎn)單的跳轉(zhuǎn)方式。

 

str     r0,[sp,#4]      ;store the contents(ISR) of HandleXXX to stack

ldmfd   sp!,{r0,pc}     ;POP the work register and pc(jump to ISR)

這兩句代碼正是這段代碼的精髓;拘问饺缦拢

str     r0,[sp,#4],是指將r0的內(nèi)容,也就是異常處理函數(shù)的地址保存到棧中的SP-4位置處,這個(gè)位置也恰好是之前sub       sp,sp,#4; 用來(lái)預(yù)留給保存PC值的位置,這時(shí)將異常處理函數(shù)的地址保存在這個(gè)地址處,接下來(lái)的ldmfd   sp!,{r0,pc}剛好就是將棧中的內(nèi)容加載到R0PC中,這樣也就實(shí)現(xiàn)了將異常處理函數(shù)地址加載到PC.實(shí)現(xiàn)了跳轉(zhuǎn)過(guò)程。

 

 

高地址

SP_0/SP_3

SP_1

Handle_addr

SP_2

R0

 

低地址

從上面的分析可以知道這種中斷處理的方式,并不是中斷處理中的簡(jiǎn)單跳轉(zhuǎn)方式(因?yàn)樘D(zhuǎn)范圍的局限性)而是采用更新PC值的形式實(shí)現(xiàn)的。

 

接下來(lái)分析IRQ,這種在我們實(shí)際開(kāi)發(fā)中使用比較多的中斷形式進(jìn)行分析。

首先可以發(fā)現(xiàn)存在:

1b       HandlerIRQ  ;handler for IRQ interrupt

這種情況下發(fā)生在中斷產(chǎn)生過(guò)程中,是在IRQ向量中執(zhí)行的,也就是在0x18處執(zhí)行,其中HandlerIRQ實(shí)質(zhì)上是一個(gè)標(biāo)號(hào),對(duì)應(yīng)一個(gè)具體的地址。其中保存的內(nèi)容就是對(duì)應(yīng)IRQ處理函數(shù)的地址。但是在代碼中只有一個(gè)HandlerIRQ,形式如下

HandlerIRQ  HANDLER HandleIRQ

 

2、HandlerIRQ    HANDLER HandleIRQ

根據(jù)上面的宏定義,可以將這句代碼進(jìn)行擴(kuò)展,得到如下的形式:

HandlerIRQ

      sub  sp,sp,#4 

       stmfd     sp!,{r0}

ldr     r0,= HandleIRQ

       ldr     r0,[r0]

       str     r0,[sp,#4]

       ldmfd   sp!,{r0,pc}

 

3、關(guān)于HandleIRQ其中存放的內(nèi)容可以從下面的代碼中得到。

       ; Setup IRQ handler

       ldr   r0,=HandleIRQ    ;This routine is needed

       ldr   r1,=IsrIRQ    ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c

       str   r1,[r0]

其中可以看到,在HandleIRQ中保存的內(nèi)容是IsrIRQ的地址,而IsrIRQ我們可以知道是一個(gè)中斷服務(wù)函數(shù),因?yàn)樵趯?xiě)代碼的過(guò)程中進(jìn)程會(huì)遇到這個(gè)特殊字符__ISR,這段代碼是在啟動(dòng)代碼中執(zhí)行的。

4、IsrIRQ實(shí)現(xiàn)問(wèn)題

IsrIRQ

       sub  sp,sp,#4       ;reserved for PC

       stmfd     sp!,{r8-r9}

       ldr   r9,=INTOFFSET

       ldr   r9,[r9]

       ldr   r8,=HandleEINT0

       add  r8,r8,r9,lsl #2

       ldr   r8,[r8]

       str   r8,[sp,#8]

       ldmfd     sp!,{r8-r9,pc}

還是一句一句的分析:

       sub  sp,sp,#4        ;為保存PC值預(yù)留一個(gè)棧區(qū)域,這個(gè)區(qū)域與上面的處理過(guò)程是異曲同工的。

       stmfd      sp!,{r8-r9}     ;保存r8,r9中的值,因?yàn)榻酉聛?lái)將使用這兩個(gè)寄存器

ldr   r9,=INTOFFSET; 這是一個(gè)偽指令操作,實(shí)質(zhì)上是將寄存器INTOFFSET的地址加載到r9中。

ldr   r9,[r9];得到寄存器中的值,這個(gè)寄存器中的值恰好保存了當(dāng)前最高優(yōu)先級(jí)中斷的中斷號(hào)(優(yōu)先級(jí)是可以調(diào)節(jié)的,而中斷號(hào)是一個(gè)固定值,因此選擇中斷號(hào)比較恰當(dāng)),這樣也就知道了具體是那個(gè)中斷源產(chǎn)生了中斷。

 

ldr   r8,=HandleEINT0;這句的ldr是偽指令,意思是將標(biāo)號(hào)的地址加載到r8

add  r8,r8,r9,lsl #2;從指令的意義分析:r8 = r8 + r9>>2 = r8+r9*4;

其實(shí)這兩句結(jié)合一下S3C2440的中斷資料就不難分析得出,因?yàn)?/span>HandleEINT0實(shí)質(zhì)上是指存儲(chǔ)外部中斷0處理函數(shù)地址的地方,那么我們可以將這一塊內(nèi)存地址看做是一個(gè)IRQISR中斷向量表,而EINT0恰好是中斷優(yōu)先級(jí)最高的中斷,那么可以將這個(gè)地址HandleEINT0作為IRQ中斷向量表的入口地址,其他中斷號(hào)的地址,只需要通過(guò)偏移地址就能得到,由于指針的大小恰好為4個(gè)字節(jié),因此得到的相應(yīng)中斷號(hào)的入口地址是

HandleEINT0 = HandleEINT0 + INTOFFSET*4,

這些地址中都保存了對(duì)應(yīng)中斷處理函數(shù)的函數(shù)地址。

       ldr   r8,[r8]是指將r8的內(nèi)容加載到r8中,也就是將對(duì)應(yīng)中斷處理函數(shù)的地址加載到r8中。

       str   r8,[sp,#8];這句代碼的作用實(shí)質(zhì)上就是和上面的分析一樣,也就是將r8的值保存到之前為PC預(yù)留的區(qū)域中。

ldmfd      sp!,{r8-r9,pc};這句也恰好驗(yàn)證了上面的分析,PC中的值恰好就是之前的sp+8處的內(nèi)容,這樣中斷處理函數(shù)的地址就到了PC中。

小結(jié):

我們可以將ARM中采用2級(jí)向量表的形式實(shí)現(xiàn)異常的中斷處理,其中第一級(jí)是CPU中定義好的向量表,也就是異常向量表。在這一級(jí)的向量表中,實(shí)現(xiàn)跳轉(zhuǎn)到對(duì)應(yīng)的異常公共處理函數(shù),另外每一種異常問(wèn)題都存在自己的子問(wèn)題,這時(shí)候采用第二級(jí)的向量表就可以解決各種子問(wèn)題。第一級(jí)的向量表一般來(lái)說(shuō)都是CPU定義好的,而第二級(jí)向量表則是我們?cè)诔绦蛟O(shè)計(jì)中人工實(shí)現(xiàn)的。

 

5、那么又是如何得到C語(yǔ)言中的函數(shù)呢,實(shí)質(zhì)上已經(jīng)很簡(jiǎn)單了,具體的分析如下:

//S3c2440init.s

       ^   _ISR_STARTADDRESS             ; _ISR_STARTADDRESS=0x33FF_FF00

HandleReset   #   4

HandleUndef #   4

HandleSWI            #   4

HandlePabort    #   4

HandleDabort    #   4

HandleReserved  #   4

HandleIRQ             #   4

HandleFIQ             #   4

 

這邊就可以看做第二級(jí)中斷向量表

       ;@0x33FF_FF20

HandleEINT0         #   4

HandleEINT1         #   4

HandleUART0       #   4

….

HandleSPI1           #   4

HandleRTC           #   4

HandleADC           #   4

       _ISR_STARTADDRESSs3c2440中是一個(gè)具體的地址值,這個(gè)地址值可以在option.h中找到。因此依據(jù)這個(gè)值我們就可以知道我們的二級(jí)向量表的實(shí)際位置,這種處理的方式存在一定的巧妙性,同時(shí)中斷地址的選擇也需要我們恰當(dāng)?shù)脑O(shè)置。這里的“^ 其實(shí)就是 MAP ,這段程序的意思是,從 _ISR_STARTADDRESS 開(kāi)始,預(yù)留一個(gè)變量,每個(gè)變量一個(gè)標(biāo)號(hào),預(yù)留的空間為 4個(gè)字節(jié),也就是 32BIT,其實(shí)這里放的是真正的C寫(xiě)的處理函數(shù)的地址,說(shuō)白了,就是函數(shù)指針,這樣做就很靈活了。

//option.h

#define _ISR_STARTADDRESS               0x33ffff00

同時(shí)在s3c2440addr.h中又可以找到下面的定義:

//s3c2440addr.h

// Exception vector(異常向量,不是CPU的異常向量)

#define pISR_RESET           (*(unsigned *)(_ISR_STARTADDRESS+0x0))

#define pISR_UNDEF           (*(unsigned *)(_ISR_STARTADDRESS+0x4))

#define pISR_SWI        (*(unsigned *)(_ISR_STARTADDRESS+0x8))

#define pISR_PABORT         (*(unsigned *)(_ISR_STARTADDRESS+0xc))

#define pISR_DABORT        (*(unsigned *)(_ISR_STARTADDRESS+0x10))

#define pISR_RESERVED     (*(unsigned *)(_ISR_STARTADDRESS+0x14))

#define pISR_IRQ         (*(unsigned *)(_ISR_STARTADDRESS+0x18))

#define pISR_FIQ         (*(unsigned *)(_ISR_STARTADDRESS+0x1c))

// Interrupt vector(中斷向量)

#define pISR_EINT0            (*(unsigned *)(_ISR_STARTADDRESS+0x20))

#define pISR_EINT1            (*(unsigned *)(_ISR_STARTADDRESS+0x24))

#define pISR_EINT2            (*(unsigned *)(_ISR_STARTADDRESS+0x28))

#define pISR_EINT3            (*(unsigned *)(_ISR_STARTADDRESS+0x2c))

#define pISR_EINT4_7  (*(unsigned *)(_ISR_STARTADDRESS+0x30))

#define pISR_SPI1        (*(unsigned *)(_ISR_STARTADDRESS+0x94))

#define pISR_RTC        (*(unsigned *)(_ISR_STARTADDRESS+0x98))

#define pISR_ADC        (*(unsigned *)(_ISR_STARTADDRESS+0x9c))

從上面的代碼中我們可以知道pISR_EINT0之類的實(shí)質(zhì)上就是一個(gè)地址,如果我們?cè)谶@個(gè)地址中填充處理函數(shù)的地址值也就形成了函數(shù)指針,實(shí)際上只需要將函數(shù)名賦值給對(duì)應(yīng)的中斷向量即可。這樣也就找到了適當(dāng)?shù)奶幚矸绞?/span>.基本的形式如下所示:

       Void main()

{

       

pISR_EINT0 = (U32)Button_ISR;

While(1)

{

}

}

/*中斷服務(wù)函數(shù)*/

static void _irq Button_ISR(void)

{

      

}

幾個(gè)分析的比較清晰的網(wǎng)址可以去看看:

http://blog.sina.com.cn/s/blog_8c134b590100yke9.html

http://hi.baidu.com/%C9%B3%BC%D3%BB%C6%BD%F0%CA%A5%B6%B7%CA%BF/blog/item/fbc56f137f475a085baf5334.html

 

總結(jié):

 


問(wèn)題?在其中的代碼中,我并沒(méi)有看到返回地址的操作問(wèn)題,我找了很多的代碼,但是好像都不是特別的準(zhǔn)確。也就是沒(méi)有找到對(duì)應(yīng)的
 subs pc, lr, =0x04操作。

 

代碼中經(jīng)典的片段就是如何實(shí)現(xiàn)了代碼的跳轉(zhuǎn)問(wèn)題:

sub sp,sp, #0x04;為保存PC值預(yù)留空間

stmfd     sp!,{r0}; 保存需要使用到的寄存器值,需要使用多少,就壓多少的堆棧

…//使用r0進(jìn)行相關(guān)的操作

ldr r0,[r0];

str r0, [sp,#0x04]; //這個(gè)操作類似于函數(shù)調(diào)用中的問(wèn)題

ldmfd sp!,{r0, pc};//出棧操作,實(shí)現(xiàn)了對(duì)PC值的賦值

關(guān)閉窗口

相關(guān)文章