Wince外部中斷控制LED詳解---動態申請

本實例是基於S3C2440,WINCE5.0

1.中斷分兩大類:內部中斷和外部中斷。

          外部中斷:由外部設備所引發的中斷,這些外部中斷都是通過GPIO中的中斷引腳產生的。S3C2440有24個外部中斷,相關的寄存器如下:

                EXTINT0-EXTINT2:三個寄存器設定EINT0-EINT23的觸發方式。

                EINTFLT0-EINTFLT3:控制濾波時鐘和濾波寬度。

                EINTPEND:中斷掛起寄存器

                EINTMASK:中斷屏蔽寄存器

         內部中斷:內部中斷是有CPU內部器件產生的中斷,如定時器中斷,USB中斷,UART中斷等。相關的寄存器如下:

                 SUBSRCPND:次級中斷掛起寄存器。

                 INTSUBMSK:次級中斷屏蔽寄存器。

                 INTMOD:中斷方式寄存器

                 PRIORITY :優先級寄存器

                 SRCPND :中斷掛起寄存器

                 INTMSK :中斷屏蔽寄存器。

                 INTPND :中斷髮生後,SRCPND中會有位置1,可能好幾個,這些中斷會由優先級仲裁器選出一個優先級最高的,然後INTPND中相應位置1,同一時間只有一位是1。

                這裏要注意一級中斷和次級中斷的區別,次級中斷的設置要比一級中斷複雜一些。
2.WINCE中斷機制:ISR和IST

                WINCE中斷從大的方面分爲ISR和IST兩部分,具體ISR,IST是什麼這裏就不詳說了,網上一搜一大把。簡單的說是ISR負責把IRQ轉化爲邏輯中斷並返回給內核;IST則負責中斷的邏輯處理。

3.WINCE中斷實例:外部按鍵中斷控制LED燈

              (1)創建一個簡單的流驅動模板,這一步可以手動創建,也可以通過"Windows CE Developer Samples" -> "Windows CE 5.0 Embedded Development Labs" -> "DrvWiz.exe" 框架產生。建議採用後者,簡單快捷,不易出錯。這裏工程命名爲LED,即生產的DLL爲LED_DLL。

              (2)填充函數體。這裏主要介紹與中斷相關的兩個函數:

                              DWORD LED_Init( LPCTSTR pContext)//驅動初始化函數,主要做內存的分配和調用中斷初始化函數。 

                               DWORD WINAPI  LED_IST( LPVOID lpvParam ) //中斷處理線程,即IST,在這裏進行中斷處理.

                               DWORD InitInterrupt( void ) //中斷初始化函數,包括中斷寄存器的設置,事件和線程的創建,初始化等。

                   首先介紹DWORD InitInterrupt( void ),代碼如下:

DWORD InitInterrupt( void )
{
 HANDLE g_htIST;  //線程返回句柄
 BOOL fRetVal;
 DWORD dwThreadID;
 printfmsg((TEXT("**************come into the setup interrupt!!!********************/r/n")));

//初始化外部中斷8
      s2440IO->rGPGCON &= ~(0X3);
      s2440IO->rGPGCON  |= 0X2;//設置爲中斷模式
      s2440IO->rEXTINT1 &= ~(0X0f);
      s2440IO->rEXTINT1  |= 0XA;  //下降沿觸發,使能濾波器
      s2440IO->rEINTMASK &= ~(1<<8);//打開中斷 8
      s2440IO->rEINTPEND |= (1<<8);//清除中斷
      //GPIO 設置-LED
      s2440IO->rGPBCON  = (s2440IO->rGPBCON  &~(3 << 14)) | (1<< 14); // GPB7 == OUTPUT.
      s2440IO->rGPBCON  = (s2440IO->rGPBCON  &~(3 << 16)) | (1<< 16);       // GPB8 == OUTPUT.


   s2440INTR->rINTMSK &= ~(0x20); //取消外部中斷8的屏蔽
   s2440INTR->rSRCPND |= (0x20);  //清除外部中斷8
   s2440INTR->rINTPND  |=0X20;    //清除外部中斷,即初始化
 // Create an event
 // 創建一個事件
 g_hevInterrupt = CreateEvent(NULL, FALSE, FALSE, NULL);
 if(!g_hevInterrupt) return -10;

 // Have the OAL Translate the IRQ to a system irq
 //將物理中斷IRQ轉換爲邏輯中斷
 fRetVal = KernelIoControl( IOCTL_HAL_TRANSLATE_IRQ,
        &dwIrq,
        sizeof( dwIrq ),
        &g_dwSysInt,
        sizeof( g_dwSysInt ),
        NULL );

  
 if( !fRetVal )
 { return -1 }
  
 // Create a thread that waits for signaling
 // 創建中斷服務線程IST
 g_htIST = CreateThread(NULL,// CE Has No Security
       0, // No Stack Size
       ThreadIST,// Interrupt Thread
       NULL,// No Parameters
       CREATE_SUSPENDED,// Create Suspended until we are done
       &dwThreadID // Thread Id
       );
 if( !g_htIST )
 {return -1 }
 // Set the thread priority to real time
 // 設置線程的優先級
 int m_nISTPriority = 7;
 if(!CeSetThreadPriority( g_htIST, m_nISTPriority))
 { return -1 }
 // Initialize the interrupt
 // 初始化中斷,將邏輯中斷號與事件關聯,即中斷產生時觸發該事件
 //在中斷服務線程IST中會等該事件的發生,即WaitForSingleObject(g_hevInterrupt, INFINITE);
 //從而中斷髮生就導致IST運行,處理中斷任務
 if ( !InterruptInitialize(g_dwSysInt, g_hevInterrupt, NULL, 0) )
 {return -1; }
 ResumeThread( g_htIST );
      printfmsg((TEXT("*************leave the setup interrupt!!!********************/r/n")));

 return 1;
}

      簡單說一下初始化中斷的流程,首先是初始化相關的中斷寄存器,我這裏採用的是外部中斷8。接下來是創建一個事件,用於關聯外部中斷8和IST線程。在IST中會通過dwStatus = WaitForSingleObject(g_hevInterrupt, INFINITE)來等待中斷的發生。然後利用KernelIoControl( IOCTL_HAL_TRANSLATE_IRQ,
        &dwIrq, sizeof( dwIrq ), &g_dwSysInt, sizeof( g_dwSysInt ), NULL ); 將物理中斷IRQ轉換爲邏輯中斷。這是給內核用的。接着就要創建中斷服務線程了:g_htIST = CreateThread(NULL,// CE Has No Security
       0, // No Stack Size
       ThreadIST,// Interrupt Thread
       NULL,// No Parameters
       CREATE_SUSPENDED,// Create Suspended until we are done
       &dwThreadID // Thread Id
       );

      這些都做好以後就可以初始化中斷了.

     // Initialize the interrupt 
     // 初始化中斷,將邏輯中斷號與事件關聯,即中斷產生時觸發該事件
     //在中斷服務線程IST中會等該事件的發生,即WaitForSingleObject(g_hevInterrupt, INFINITE);
     //從而中斷髮生就導致IST運行,處理中斷任務
   InterruptInitialize(g_dwSysInt, g_hevInterrupt, NULL, 0) 該函數就將物理中斷對應的邏輯中斷號和事件關聯起來了。

    到這裏整個初始化就做好了,這其中的函數調用順序不是唯一的,但只要理清各個函數的調用順序就行了。

    DWORD TST_Init( LPCTSTR pContext)
{
  printfmsg((TEXT("**************come into the init!!!********************/r/n")));


          // GPIO Virtual alloc
 s2440IO = (volatile IOPreg *) VirtualAlloc(0,sizeof(IOPreg),MEM_RESERVE, PAGE_NOACCESS);
 if(s2440IO == NULL) {
   printfmsg((TEXT("For s2440IO: VirtualAlloc faiLED!/r/n")));
 }
 else {
  if(!VirtualCopy((PVOID)s2440IO,(PVOID)(IOP_BASE),sizeof(IOPreg),PAGE_READWRITE | PAGE_NOCACHE )) {
   printfmsg((TEXT("For s2440IO: Virtualcopy faiLED!/r/n")));
  }
 }

  s2440INTR = (volatile INTreg *) VirtualAlloc(0,sizeof(INTreg ),MEM_RESERVE, PAGE_NOACCESS);
 if(s2440INTR == NULL) {
  printfmsg((TEXT("For s2440INTR: VirtualAlloc faiLED!/r/n!/r/n")));
 }
 else {
  if(!VirtualCopy((PVOID)s2440INTR,(PVOID)(INT_BASE),sizeof(INTreg),PAGE_READWRITE | PAGE_NOCACHE )) {
   printfmsg((TEXT("For s2440INTR: Virtualcopy faiLED!/r/n!/r/n")));
  }

   InitInterrupt();
   return 0x1234;

}
    這個函數是在驅動被加載的時候調用的,所以要把初始化的任務放在這裏。

     與中斷相關的部分就這麼多,這是動態申請中斷的方法,比較複雜。其實完全可以不用動態申請的方法,而且不推薦採用此方法。靜態申請是一種很好的方法,比較簡單。在後面的文章會進一步講解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章