<var id="fnfpo"><source id="fnfpo"></source></var>
<rp id="fnfpo"></rp>

<em id="fnfpo"><object id="fnfpo"><input id="fnfpo"></input></object></em>
<em id="fnfpo"><acronym id="fnfpo"></acronym></em>
  • <th id="fnfpo"><track id="fnfpo"></track></th>
  • <progress id="fnfpo"><track id="fnfpo"></track></progress>
  • <tbody id="fnfpo"><pre id="fnfpo"></pre></tbody>

  • x
    x

    超詳細的2440中斷機制分析!

    發布時間:2011-3-24 13:47    發布者:techshare
    關鍵詞: 2440 , 中斷
    一直在看2440的中斷處理部分,不懂的實在太多了,百度到這篇文章,實在有聽君一席話,勝養十年豬的感覺啊,下面上文章:

    中斷向量

    b HandlerIRQ ;handler for IRQ interrupt

    很自然,因為所有的單片機都是那樣,中斷向量一般放在開頭,用過單片機的人都會很熟悉,那就不多說了。

    異常服務程序

    這里不用中斷(interrupt)而用異常(exception),畢竟中斷只是異常的一種情況,

    下面主要分析的是“中斷異!闭f白了,就是我們平時單片機里面用的中斷。!所有由器件引起的中斷,例如TIMER中斷,UART中斷,外部中斷等等,都有一個統一的入口,那就是中斷異常 IRQ ! 然后從IRQ的服務函數里面分辨出,當前究竟是什么中斷,再跳轉到相應的中斷服務程序。這樣看來,ARM比單片機要復雜一些了,不過原理是不變的。

    上面說的就是思路,跟著這個思路來接著分析。

    HandlerIRQ 很明顯是一個標號,我們找到了

    HandlerIRQ HANDLER HandleIRQ

    這里是一個宏定義,我們再找到這個宏,看他是怎么定義的:

    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

    用 HandlerIRQ 將這個宏展開之后得到的結果實際是這樣的

    HandlerIRQ

    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,=HandleIRQ ;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)

    至于具體的跳轉原理下面再說,

    好了,這樣的話就容易看的多了,很明顯, HandlerIRQ 還是一個標號,IRQ異常向量就是跳轉到這里執行的,這里粗略看一下,應該是保存現場,然后跳轉到真正的處理函數,那么很容易發現了這么一句 ldr r0,=HandleIRQ ,沒錯,我們又找到了一個標號 HandleIRQ ,看來真正的處理函數應該是這個 HandleIRQ ,繼續尋找

    AREA RamData, DATA, READWRITE

    ^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00

    HandleReset # 4

    HandleUndef # 4

    HandleSWI # 4

    HandlePabort # 4

    HandleDabort # 4

    HandleReserved # 4

    HandleIRQ # 4

    最后我們發現在這里找到了 HandleIRQ ,^ 其實就是 MAP ,這段程序的意思是,從 _ISR_STARTADDRESS 開始,預留一個變量,每個變量一個標號,預留的空間為 4個字節,也就是 32BIT,其實這里放的是真正的C寫的處理函數的地址,說白了,就是函數指針 - - 這樣做的話就很靈活了


    接著,我們需要安裝IRQ處理句柄,說白了,就是設置處理函數的地址,讓PC指針可以正確的跳轉。

    于是我們在接著的找到安裝句柄的語句


    ; 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]

    說白了就是將 IsrIRQ 的地址填到 HandleIRQ對應的地址里面,前面說了 HandleIRQ 放的是中斷處理的函數的入口地址,我們繼續找 IsrIRQ

    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}


    要理解這個代碼,得先學學2440的中斷系統了,INTOFFSET存放的是當前中斷的偏移號,根據偏移就知道當前是哪個中斷源發生的中斷。

    注意了,我們說的是中斷,而不是異常,看看原來的表是啥樣子的

    ^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00

    HandleReset # 4

    HandleUndef # 4

    HandleSWI # 4

    HandlePabort # 4

    HandleDabort # 4

    HandleReserved # 4

    HandleIRQ # 4

    HandleFIQ # 4

    HandleEINT0 # 4

    HandleEINT1 # 4

    HandleEINT2 # 4

    HandleEINT3 # 4

    .......

    可以看到,前面幾個是異常,從 HandleEINT0 就是 IRQ異常的向量存放的地方了,這樣就可以理解為什么上面 IsrIRQ 里面里面要執行那條指令

    ldr r8,=HandleEINT0

    add r8,r8,r9,lsl #2

    道理很簡單, HandleEINT0 就是所有IRQ中斷向量表的入口,在這個地址上面,加上一個適當的偏移量,INTOFFSET ,那么我們知道現在,到底是哪個IRQ在申請中斷了。

    至于具體怎么跳轉的?

    首先,我們說了,HandleEINT0 開始的一段內存里面,存放的就是中斷服務函數的函數指針,ARM的體系的話,每個指針變量就是占4個字節,這里就解釋了,為什么這里為每個標號分配了4個字節的空間,里面放的就是函數指針。!下面再看看怎么跳轉,繼續看 IsrIRQ 里面就實現了跳轉了

    str r8,[sp,#8]

    ldmfd sp!,{r8-r9,pc}

    其實最核心就是這兩句了,先查找到當前中斷服務程序的地址,將他放到 R8 里面,然后出棧,彈出給PC那么PC很自然就跳到中斷服務程序了。至于這里的堆棧問題又是一個非常棘手的,需要好好的參透ARM的中斷架構,需要了解的可以自己仔細的閱讀 《ARM體系結構與編程》里面說的很詳細。我們這里的重點是研究怎么跳轉。

    最后,我們看看在C代碼中是怎么安裝終端向量的,例如看 按鍵的外部中斷,是怎么具體設置的,參看/src/keyscan.c 里面的代碼很簡單,里面只有3個函數

    KeyScan_Test 是按鍵測試的主函數

    Key_ISR 是按鍵中斷服務函數

    在 KeyScan_Test里面,我們發現了有這么一句

    pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR;

    可以理解否? Key_ISR就是上面提到的按鍵中斷服務函數,函數的名字,代表的就是函數的地址。。!

    將中斷服務函數的地址,注意了,是地址,這是一個 U32型的變量。送到幾個變量,我們以pISR_EINT0 作為例子,查看頭文件定義,在 2440addr.h 里面找到

    // Interrupt vector

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

    _ISR_STARTADDRESS有沒有似曾相識的感覺?沒錯,剛才分析的匯編代碼里面就提到了

    ^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00

    HandleReset # 4

    HandleUndef # 4

    ......

    對,地址就是這里,然后 _ISR_STARTADDRESS+0x20 就是跳過前面的異常向量,進入IRQ中斷向量的入口,所以說到尾

    pISR_EINT0 = (U32)Key_ISR;

    完成的操作就是,將 Key_ISR 的地址存放到

    HandleEINT0 # 4

    這個IRQ向量表里面。。!

    當按鍵中斷發生的時候,發生IRQ異常中斷當前PC值-4 保存到LR_IRQ里面,然后執行

    b HandlerIRQ

    然后是執行

    HandlerIRQ

    sub sp,sp,#4 ; 預留一個用來存放PC地址

    stmfd sp!,{r0} ; 保存R0,因為下面使用了

    ldr r0,=HandleIRQ ; 將HandleIRQ(服務程序)的地址裝載到R0

    ldr r0,[r0]

    str r0,[sp,#4] ; 保存到剛才預留的地方

    ldmfd sp!,{r0,pc} ; 彈出堆棧,恢復R0,并且將剛才計算好的 HandleIRQ 地址彈出到 PC堆棧是向下生長的,所以 SUB SP,SP,#4 就相當于 PUSH XX,但是這個XX這個時候并沒有用,因為這里用的是強制移動 SP 指針實現的。然后得到服務程序的地址,再將這個值放回剛才預留的棧的空位上面,最后就是POP出R0恢復,并且將剛才得到的服務程序的地址送到 PC,那么實現的效果就是跳轉到 HandleIRQ 里面了。

    接著看剛才是怎么安裝的HandleIRQ 的

    ; 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]

    可以看出,這里將 IsrIRQ 的地址的值保存到 HandleIRQ 中,也就是說,上面的 IRQ 服務程序,這個時候實際上就是指 IsrIRQ !

    所以接著的事情就是轉移到 IsrIRQ 中執行:

    IsrIRQ

    sub sp,sp,#4 ; 預留一個值來保存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] ; 因為保存了2個寄存器R8 R9 ,所以SP下移了8位

    ldmfd sp!,{r8-r9,pc} ; 恢復寄存器,彈出到PC,同上面的一樣怎么保存,操作SP,跟最后彈出到PC的部分和上面的例子一樣,下面說說中間的計算部分計算偏移量,其實原理很簡單,首先 INTOFFSET 保存著當前是哪個IRQ中斷,例如 0代表著 HandleEINT0,1代表HandleEINT1 ..... 等等,這不是亂來,有一個表的,這個是由 S3C2440 的datasheet說的,自己可以去查看。

    然后得到 中斷處理函數的向量表,這個表的首地址就是 HandleEINT0,那么很自然的想到,怎么查表?那還不簡單?HandleEINT0 + INTOFFSET 不就完了?基地址加偏移量就得到表中某項了,當然,因為這里是中斷處理向量

    每一項占用4個字節,所以用lsl #2處理一下,左移2位相當于乘以4,偏移量乘以4,這應該很好理解的。

    我們這個例子找到的就是 HandleEINT0 ,將里面的值讀出來,里面放的是 HandleEINT0 服務函數的地址,這個地址怎么來的?是在C程序里面設置的。我們看 keyscan.c 程序,找到一個 void KeyScan_Test(void) 函數,

    里面有這么一句:

    pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR;

    這里是安裝了3個按鍵中斷服務程序,我們只關注 0號中斷,也就是

    pISR_EINT0 = (U32)Key_ISR;

    這句話什么意思?先看看pISR_EINT0的定義,在 2440addr.h 中定義

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

    看到沒有?_ISR_STARTADDRESS 不就是剛才說的那個異常向量的入口地址?加上一個 0x20 之后實際上指向的,就是 HandleEINT0 。!這么說來,上面的意思就是,將 Key_ISR 處理函數的入口地址,送到 HandleEINT0 中。

    再來看 Key_ISR ,這是一個典型的服務程序,加了_irq 作為編譯關鍵字,告訴編譯器,這個函數是中斷服務程序

    得保存需要的寄存器,免得被破壞。具體可以參考 《ARM體系結構與編程》P283 頁的描述。

    static void __irq Key_ISR(void)

    {

    .......

    }

    加上 _irq 關鍵字之后,編譯器就會處理好所有的保存動作了,并不需要多關心。但是這個是 ARM-CC 編譯器的關鍵字,GCC中并沒有這個東西,所以GCC處理中斷的時候最好還是自己保存一下。

    到這里為止,整個中斷的過程就解釋完畢。分析的過程中確實學習了很多。
    本文地址:http://www.portaltwn.com/thread-59734-1-1.html     【打印本頁】

    本站部分文章為轉載或網友發布,目的在于傳遞和分享信息,并不代表本網贊同其觀點和對其真實性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問題,我們將根據著作權人的要求,第一時間更正或刪除。
    您需要登錄后才可以發表評論 登錄 | 立即注冊

    廠商推薦

    • Microchip視頻專區
    • EtherCAT®和Microchip LAN925x從站控制器介紹培訓教程
    • MPLAB®模擬設計器——在線電源解決方案,加速設計
    • 讓您的模擬設計靈感,化為觸手可及的現實
    • 深度體驗Microchip自動輔助駕駛應用方案——2025巡展開啟報名!
    • 貿澤電子(Mouser)專區

    相關視頻

    關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯系我們
    電子工程網 © 版權所有   京ICP備16069177號 | 京公網安備11010502021702
    快速回復 返回頂部 返回列表
    精品一区二区三区自拍图片区_国产成人亚洲精品_亚洲Va欧美va国产综合888_久久亚洲国产精品五月天婷