天天看點

【.Net Micro Framework PortingKit - 07】NVIC中斷處理

Cortex-M3的中斷架構和以往的ARM7、ARM9、ARM11有了很大的差別,IRQ、FIQ的概念的已經消失,随之而來的是NVIC中斷管理(支援最多256個中斷優先級,128級搶斷)及中斷向量表。這個中斷向量表似有相識之感,在當時大學時期學習DOS平台下的C語言開發的時候,在設定BIOS時鐘中斷的時候,就曾把中斷函數的指針位址設定到時鐘中斷的入口位址區,以期中斷發生的時候,執行指定的函數。當然不僅是時鐘中斷,其它的中斷的處理方式也類似。在DOS那個時代,估計最高深一點的程式設計就是程式駐留和鈎子函數(姑且把修改中斷位址以截獲資訊的這一類函數為鈎子函數吧),以此看來,Cortex-M3的中斷架構倒是借鑒了BIOS的中斷處理機制。

ARM公司大力推出Cortex-M3系列CPU核,一改往日的命名方式,并且記憶體映射、中斷架構等以往因不同晶片廠商而多變的架構,變得統一起來。看來ARM其志不小,想要在嵌入式領域開發技術層面一統江湖。這種統一意義巨大,大大降低了嵌入式軟體移植的難度,不知道今後這種改變,能否在嵌入式領域中催生出類似昔日PC領域的王者組合:作業系統(Microsoft)+CPU(Intel)。

.Net Micro Framework的整個底層架構是建立在ARM7和ARM9基礎之上的,而Cortex-M3的出現,大大的沖擊了這一架構,這也是在MF3.0源碼中就已經出現了CortexM3的目錄,而到現在官方無一款可支援的Cortex-M3開發闆的原因。因為要改動的代碼太多,要保持ARM7、ARM9和Cortex-M3的代碼統一太難。

是以在這裡我單立了一個目錄,大力進行修改而不遺餘力(所謂不破不立),我覺得,這也是我們在一個劃時代事物出現的面前,所應持有的态度和立場。

在第4回<修改啟動代碼&重寫向量表>中我們所寫的中斷向量表,我們要進行一番調整,以期支援動态加入中斷函數的功能。

新的中斷函數表,我們僅需預先填寫前三項:(1) 堆棧TOP位址,(2)複位位址,(3)不可屏蔽中斷函數位址(NMIException)。VectorsTrampolines.s修改後的代碼如下(和以前的代碼相比,是不是簡化了很多):

  EXPORT  ARM_Vectors

    IMPORT  StackTop

    IMPORT  EntryPoint

     IMPORT  NMIException

   

;********************************************************

    AREA |.text|, CODE, READONLY

 

    ;向量表

ARM_Vectors

         DCD  StackTop                   ; Top of Stack  棧頂

         DCD  EntryPoint                                    ; 複位

         DCD  NMIException

    ;...                            ; 向量表定位在 RAM中,由程式動态生成

    SPACE  384                     ; 預留白間 (76 - 3) * 4 = 292   

   

;*********************************************************

    END           
其中SPACE 384的代碼也可以不要,不過要確定Scatterfile_tools_mdk.xml檔案中,如下項沒有<FileMapping Name="*" Options="(SectionForFlashOperations)" />

  <ExecRegion Name="ER_RAM_RO" Base="0x20000000" Options="ABSOLUTE" Size="">

       <FileMapping Name="VectorsTrampolines.obj" Options="(+RO, +FIRST)" />

       <FileMapping Name="*" Options="(SectionForFlashOperations)" />

  </ExecRegion>           

此外還有一個改進就是删除我們原先在/DeviceCode/Targets/Native/CortexM3/DeviceCode目錄下的建立的VectorsHandler_Temp庫檔案,這個庫我們已經不需要了,我們随時根據需要,動态的在中斷向量表中寫入我們所需的中斷函數位址。

好了,在/DeviceCode/Targets/Native/CortexM3/DeviceCode目錄中建立NVIC目錄(相當于以前體系的INTC),在寫具體代碼之前,我們現在寫下如下代碼(大概),這是對NVIC相關寄存器的描述。

struct CortexM3_NVIC

{  

    static const UINT32 c_Base = 0xE000E100;

    //中斷使能 置位複位

    /****/ volatile UINT32 SETENA[2];

    UINT32  RESERVED0[30];

    /****/ volatile UINT32 CLRENA[2];

    UINT32  RSERVED1[30];

   

    //中斷懸起 置位複位

    /****/ volatile UINT32 SETPEN[2];

    UINT32  RESERVED2[30];

    /****/ volatile UINT32 CLRPEN[2];

    UINT32  RESERVED3[30];

   

    //中斷活動狀态

    /****/ volatile UINT32 ACTIVE[2];

    UINT32  RESERVED4[62];

 

    //中斷優先級

    /****/ volatile UINT8 PRI[60];

   

    static const UINT32 c_IRQ_MAX_INDEX = 16+60;

   

    //IRQ Index Table   

    static const UINT32 c_IRQ_Index_NMI = 0x2;

    static const UINT32 c_IRQ_Index_HardFault = 0x3;

    static const UINT32 c_IRQ_Index_MemManage = 0x4;

    static const UINT32 c_IRQ_Index_BusFault = 0x5;

    static const UINT32 c_IRQ_Index_UsageFault = 0x6;

    //7,8,9,A NULL

    static const UINT32 c_IRQ_Index_SVC = 0xB;

    static const UINT32 c_IRQ_Index_DebugMonitor = 0xC;

         //D NULL

    static const UINT32 c_IRQ_Index_PendSVC = 0xE;

    static const UINT32 c_IRQ_Index_SysTick = 0xF; 

    //--

    static const UINT32 c_IRQ_Index_WWDG = 0x10; 

    static const UINT32 c_IRQ_Index_PVD = 0x11;

    static const UINT32 c_IRQ_Index_TAMPER = 0x12; 

     //略

    static const UINT32 c_IRQ_Index_DMA2_Channel3 = 0x4A;

    static const UINT32 c_IRQ_Index_DMA2_Channel4_5 = 0x4B;   

 

    //IRQ Priority index Table

    static const UINT32 c_IRQ_Priority_NULL = 0xFF;

    static const UINT32 c_IRQ_Priority_Leve0 = 0x00;

    static const UINT32 c_IRQ_Priority_Leve1 = 0x10;

    static const UINT32 c_IRQ_Priority_Leve2 = 0x20;

     //略

    static const UINT32 c_IRQ_Priority_Leve14 = 0xE0;

    static const UINT32 c_IRQ_Priority_Leve15 = 0xF0;

};           

在NVIC目錄下建立三個檔案:NVIC.cpp、NVIC.h、doNetMF.proj

NVIC.cpp的核心内容如下:

void CortexM3_NVIC_Driver::Initialize()

{

   //外部中斷

   CortexM3_NVIC &NVIC = CortexM3::NVIC();

   NVIC.CLRENA[0]=0xFFFFFFFF;   //清外部中斷

   NVIC.CLRENA[1]=0xFFFFFFFF;

   NVIC.CLRPEN[0]=0xFFFFFFFF;

   NVIC.CLRPEN[1]=0xFFFFFFFF;

 

   //系統中斷

   CortexM3_SCB &SCB = CortexM3::SCB();  

   SCB.ICSR = 0x0A000000;

   SCB.VTOR = (UINT32)&IMAGE_RAM_RO_BASE;   // 重定向中斷向量表

   SCB.AIRCR = CortexM3_SCB::AIRCR_VECTKEY_MASK | CortexM3_SCB::NVIC_PriorityGroup_1;

   SCB.SCR = 0x00000000;

   SCB.CCR = 0x00000000;

   SCB.SHCSR = 0x00000000;  //系統中斷使能位

   SCB.CFSR = 0xFFFFFFFF;  

   SCB.HFSR = 0xFFFFFFFF;

   SCB.DFSR = 0xFFFFFFFF;

 

   //優先級設定

   IRQ_VECTORING *IsrVector = s_IsrTable;

   for(int i = 0; i < CortexM3_NVIC::c_IRQ_MAX_INDEX; i++)

   {

     //略

   }

}

//激活中斷

BOOL CortexM3_NVIC_Driver::ActivateInterrupt(UINT32 Irq_Index,UINT32 ISR)

{

    InterruptEnable(Irq_Index);

    UINT32 *p= (UINT32*)&IMAGE_RAM_RO_BASE + Irq_Index;

         *p=ISR;       

    return TRUE;

}

//關閉中斷

BOOL CortexM3_NVIC_Driver::DeactivateInterrupt(UINT32 Irq_Index)

{

    InterruptDisable(Irq_Index);

    return TRUE;

}

 

BOOL CortexM3_NVIC_Driver::InterruptEnable(UINT32 Irq_Index)

{

    if(Irq_Index<16)

    {

       CortexM3_SCB &SCB = CortexM3::SCB(); 

             if(Irq_Index==CortexM3_NVIC::c_IRQ_Index_MemManage)  //4

                           SCB.SHCSR |= 0x1<<16;

             if(Irq_Index==CortexM3_NVIC::c_IRQ_Index_BusFault)   //5

                           SCB.SHCSR |= 0x1<<17;

        if(Irq_Index==CortexM3_NVIC::c_IRQ_Index_UsageFault) //6

                           SCB.SHCSR |= 0x1<<18;

                   if(Irq_Index==CortexM3_NVIC::c_IRQ_Index_SysTick)    //15

                   {

                      CortexM3_SysTick &SysTick= CortexM3::SysTick();   

                SysTick.CTRL |= CortexM3_SysTick::CTRL_TICKINT | CortexM3_SysTick::CTRL_ENABLE;

                   }

          

    }

         else

         {

            CortexM3_NVIC &NVIC = CortexM3::NVIC();

            int irq=Irq_Index-16;

            NVIC.SETENA[irq/32] = 0x1 << (irq % 32);

         }

    return TRUE;

}

BOOL CortexM3_NVIC_Driver::InterruptDisable(UINT32 Irq_Index)

{

    //略

    return TRUE;

}           

在其中,我們為了适應新架構的需要,增加了一個中斷函數連接配接接口,聲明如下:

BOOL CPU_INTC_ActivateInterruptEx(UINT32 Irq_Index, UINT32 ISR)

{

         return CortexM3_NVIC_Driver::ActivateInterrupt(Irq_Index, ISR);

}           

原先的(如下),已經廢棄不用(暫保留)。

BOOL CPU_INTC_ActivateInterrupt(UINT32 Irq_Index, HAL_CALLBACK_FPN ISR, void *ISR_Param)

{

    return FALSE;

}           

以上代碼中最重要的莫過于“激活中斷”函數中的如下代碼

UINT32 *p= (UINT32*)&IMAGE_RAM_RO_BASE + Irq_Index;

*p=ISR;              

簡單的兩行代碼,便實作了中斷函數的動态注冊(在GPIO和序列槽驅動中我們将詳細講這一點)。

此外,最後要記得在NativeSample.proj添加如下内容:

  <ItemGroup>

    <RequiredProjects Include="$(SPOCLIENT)/DeviceCode/Targets/Native/CortexM3/DeviceCode/Nvic/dotNetMF.proj" />

    <DriverLibs Include="nvic.$(LIB_EXT)" />

  </ItemGroup>           

前一兩個禮拜沒有寫相關文章,是因為代碼修改量很大,一直在調試相關功能。目前GPIO、序列槽等功能均已經調試通過,是以後續的幾天,我便一一介紹它們的實作過程。

繼續閱讀