函數指針數組在ARM中斷中的應用

函數指針數組在ARM異常中斷處理中的應用

介紹一種簡潔、高效、靈活的ARM異常中斷處理方法。

      在ARM中,由於所有的中斷都使用同一個異常中斷入口地址,即0x00000018。因此需要在異常中斷處理程序中根據相應的中斷號調用對應的中斷服務函數。

        一般有兩種處理方式:

        1. 在彙編中保存現場,然後調用C語言編寫的中斷處理程序,任務處理完成之後,再返回到彙編中恢復現場,並返回到斷點。其中C語言編寫的中斷處理程序,
    通過switch語句對INTOFFSET進行判斷,然後散轉執行對應的服務函數。

          IMPORT    IRQ_EXCEPTION

         0x00000018       LDR   PC,=IRQ_ENTRY

          …………        ………………………………

         IRQ_ENTRY

                           STMFD  SP!,{R0-R8,LR}

                           BL     IRQ_EXCEPTION

                           LDMFD     SP!,{R0-R8,LR}

                           SUBS        PC,LR,#4

 

         void    IRQ_EXCEPTION()

         {

                   switch(INTOFFSET)

                   {

                            case 0:

                                              break;

                            case 1:

                                              break;

                   }

         }

 

         缺點:1)所有的中斷處理函數都必須在這個C文件中定義。

               2)中斷處理函數不能再程序執行過程中被更換。

               3)由於不知道中斷處理函數用到了哪些寄存器,因此保護現場時,需要把可能用到的所有工作寄存器

                  都保護起來。再加上C語言中的判斷,這些步驟都會增加中斷響應時間。

 

       2. 使用關鍵字__irq來定義每個中斷處理函數,由編譯器來插入保護現場及中斷返回的代碼,由於編譯器知道此
      函數用到了哪些寄存器,因此它只保護被用到的寄存器。接下來的問題是,當產生中斷時,如何直接調用對應的中斷處理函數?

           一般會在內存中分配32*4個存儲單元,存放每個中斷處理函數的首地址,在彙編中,直接根據INTOFFSET從中斷處理函數向量表
      中取出對應的函數首地址送給PC,直接調用對應的中斷處理函數。C語言中需要借用函數指針將中斷處理函數首地址寫入到中斷處
      理函數向量表裏的對應位置上。

         IRQ_HandlerStart        EQU         0x33FFFF00

         0x00000018                LDR         PC,=IRQ_ENTRY

          …………                ………………………………

         IRQ_ENTRY
                                   SUB          SP,SP,#4            ;爲存放中斷處理函數首地址留出空間

                                   STMFD     SP!,{R0,R1,R2}         ;保護下面的算法用到的工作寄存器      
                                   LDR          R0,=INTOFFSET

                                   LDR          R1,[R0]             ;取出中斷號

                                   LDR          R2,=IRQ_HandlerStart       

                                   ADD          R0,R2,R1,LSL  #2      ;計算中斷號對應的中斷處理函數在向量表中的位置

                                   LDR          R1,[R0]               ;取出對應的中斷處理函數首地址

                                   STR          R1,[SP,#12]           ;存儲到剛纔預留的空間裏

                                   LDMFD     SP!,{R0,R1,R2,PC}        ;出棧,數據從左向右恢復,最後將中斷處理函數首地址給PC

 

            #define         ISR_StartAddr                    0x33FFFF00

            #define         pISR_EINT0              (*(unsigned *)(ISR_StartAddr+0*4))

            #define         pISR_UART0              (*(unsigned *)(ISR_StartAddr+28*4))

            void   InitISR()

            {

                            pISR_EINT0  =   EINT0_Handler;

                            pISR_TIMER0 = UART0_Handler;

             }

             void  __irq   EINT0_Handler()

             {

                          ………………

             }

             void  __irq   UART0_Handler()

             {

                          ………………

             }

      

            缺點:1)要保證彙編與C中定義的中斷處理函數向量表的首地址相同

                  2)要定義很多個函數指針,編寫起來比較麻煩

 

            我們可以將中斷處理函數向量表看成一個具有32個成員的數組,每個成員都是函數指針,
        指向的是無形參、無返回值的中斷處理函數。我們可以在彙編中用SPACE關鍵字來定義這
            個函數指針數組變量,併爲其分配空間。 在C語言中只需要用extern申明下它是外部定義的即可。

         0x00000018                LDR         PC,=IRQ_ENTRY

          …………                 ………………………………

         IRQ_ENTRY

                                   SUB          SP,SP,#4           ;爲存放中斷處理函數首地址留出空間

                                   STMFD     SP!,{R0,R1,R2}        ;保護下面的算法用到的工作寄存器      

                                   LDR          R0,=INTOFFSET

                                   LDR          R1,[R0]             ;取出中斷號

                                   LDR          R2,=INTVECTOR       ;獲取函數指針數組首地址

                                   ADD          R0,R2,R1,LSL  #2    ;計算中斷號對應的中斷處理函數在向量表中的位置

                                   LDR          R1,[R0]             ;取出對應的中斷處理函數首地址

                                   STR          R1,[SP,#12]         ;存儲到剛纔預留的空間裏

                                   LDMFD     SP!,{R0,R1,R2,PC}      ;出棧,數據從左向右恢復,最後將中斷處理函數首地址給PC

 

                                   AREA         INTVECT,DATA

            INTVECTOR            SPACE       32*4

            爲了將此函數指針數組變量分配到內存中,需要在分散加載文件中指定這個段的執行域在內存空間

            VECT_REGION       0x33FFFF00

            {

                        StartUp.o(INTVECT)

            }

 

            typedef   void  __irq (*INTFUNC)(void);                      //函數指針類型重定義,

            extern   INTFUNC   INTVECTOR[32];

 

            void   InitiISR()

            {

                          INTVECTOR[0] = EINT0_Handler;

                          INTVECTOR[28] = UART0_Handler;

            }

 

             void  __irq   EINT0_Handler()

             {

                          ………………

             }

             void  __irq   UART0_Handler()

             {

                          ………………

             }

               

            特點:1)只需要在分散加載文件中對這個中斷處理函數向量表的首地址指定一次,避免出錯。

                  2)使用函數指針數組,省略多個函數指針的定義。

                  3)在程序執行過程中,可以通過修改函數指針數組裏的內容更換中斷處理函數。         

                  4)可以再定義一箇中斷註冊函數,提高程序的靈活性。

 

            void  ISR_Register(INT8U  num,INT32U  addr)

            {

                         INTVECTOR[num] = addr;

            }

 

            以上提到的變量都可以只放在interrupt.c中,不同的中斷處理函數可以在不同的文件中編寫,它們只需要調用ISR_Register即可。這樣可以提高程序的結構化。

           另外,還可以將中斷號用#define定義一下,以提高程序的可讀性,如下:

            #define      INT_TIMER0           10

            #define      INT_UART0            28

            #define      INT_RTC                30

 

            INTVECTOR[INT_UART0] = UART0_Handler;

            INTVECTOR[INT_RTC] = RTC_Handler;

 

            本文系原創,轉載請註明出處【硅谷芯微·技術中心 www.threeway.cc】
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章