现在的位置: 首页 > 综合 > 正文

【原创】再谈WinCE中断开发

2013年10月11日 ⁄ 综合 ⁄ 共 11195字 ⁄ 字号 评论关闭

预备文章:

WinCE 6.0中断驱动程序分析
BY:HJB

WIinCE中断流式实现驱动和APP
51wince

【原创】WinCE中断驱动开发实战
51wince

这篇文章主要总结了最近一段时间关于wince下的中断开发过程。本文仅适合初学,高手请多多指教!

首先我们在来回忆下什么是中断,请阅读文章《中断解析
》,这里再来回忆下中断的概念是源自于我自身的经历,本人毕业于一所金融类为主的大学的计算机学院,主修方向为软件偏向软件工程及ERP金融管理类软件开发。但由于工作,毕业后的主要工作转向与嵌入式开发,对于中断只能说模糊了解,基本上没有实际操作的经验,以至于在刚入职一段时间内被同事而笑话连中断都没有做过,所以当时非常的沮丧,但下定决心要在这个行当里学到点什么,毕竟入了这行再想换难度也比较大。

关于wince的中断上面的几篇预备文章我们已经给出了一些实例和介绍。这里我们再次来完善一下整体的开发流程思路。

wince做为一个嵌入式的OS,其中断的重要性是不言而喻,在《WinCE 6.0中断驱动程序分析
》一文中HJB已经给我们很清楚的介绍了wince下的中断流程,请不熟悉的读者务必仔细阅读此文,我也读过很多遍后才开始写需求分析,也就是《【原创】WinCE中断驱动开发实战
》一问中的一些需求描述。

接下来我再来将一个完整的中断驱动+APP测试程序开发的流程实例分析一边。一是为了自己加深影响,二是为了给一些刚入门的朋友一点借鉴的资料。可能刚入门的朋友也遇到过我相似的遭遇,应为不懂中断而被人嘲笑,我想说的是,虽然一开始不懂,但自己努力学还是可以学会的,也就是,穿别人的鞋,走自己的路,让别人去找去吧

闲话少说我们转入正题。

本次开发的实例是基于wince的中断开发,下面进行具体的内容描述:

 

硬件环境:1.开关电位器(飞梭),用于触发中断并产生一些值(类似于按键按下的功能);

             2.MCU,用于产生中断以及发出值给ARM的主体;

             3.ARM,用于接受MCU发出中断以及接受值的主体;

             4.LCD屏,显示中断后mcu发出值,以及相应处理信息;

软件环境:1.platformbuilder 5.0 OS wince5.0系统开发

             2.VS2005 AP层开发

设计思路:1.开关电位器(飞梭)转动;

             2.MCU采集开关电位器转动状态,通过AD采样的方式取得转动产生的对应值;

             3.MCU产生中断信号,将MCU某引脚拉低,同时该引脚连接于ARM的某一引脚;

             4.ARM定义于MCU连接引脚的中断状态,并等待响应中断命令;

             5.当ARM响应到指定引脚的中断状态时,发出于MCU的通讯指令,获取MCU采集到的值;

             6.获取成功后ARM,将该值读取,并同过驱动将此值由驱动层传递至AP层;

             7.AP层通过API函数于驱动层连接并传递信息,同时将对应接受到的值显示;

实际开发:

MCU程序开发:

     这里关于MCU的开发我们不做过多的描述,只需要实现当开关电位器发生动作时,将AD采样的值获取,同时将对应的IO口拉低,为ARM产生一个中断信号即可,但实际上AD采样值的分析也是一个比较大的工程,需要做一些纠错和处理,但这里我们主要是为wince下的中断开发进行分析,这里我们不多介绍;

OS WinCE 驱动开发:

     关于中断的驱动开发,我的步骤是先参考了HJB大牛的《WinCE 6.0中断驱动程序分析
》,然后参考在网上收集的关于2410下按键中断开发的参考代码,具体代码请参考《WIinCE中断流式实现驱动和APP
》一文中的资源下载。

      通过以上两片文章的阅读,我们可以对驱动开发的框架有一个大概的认识,并对中断驱动开发的流程有了一个了解。这里我想要提出的是四个函数,XXX_DetectThread,XXX_Init,XXX_Deinit和MCL_Read这四个函数,在这里是因为项目需要,这里的XXX可以理解为MCU或者各自项目中所定义的,可以任意定义,当然,后面三个是流式驱动固定格式即可;

XXX_DetectThread,在HJB大牛的文中用的是PowerButtonIntrThread,在参考2410的驱动开发中的叫EINTKey_IntrThread,这里我们折中取了个名子叫XXX_DetectThread,这个函数的实现的主要功能是做一个死循环,等待型号量,这里我们给出这个函数的实现部分,其中有一个MCUCTL结构体,大家可以根据自己的需要去定义,主要是一些handle和dword型,为事件和优先级做一个事先的准备,在程序中大家可以根据赋值来区分我结构体中定义的内容,这里我不一一介绍,给大家一个读程序的空间

   1:  

/////////////////////////////////////////////////////////////////////

   2:  

//=============================================================================

   3:  

//Title : MCU_DetectThread

   4:  

//Detail: Receive INTR EVENT from gpio for mcu communication

   5:  

//Input : PVOID pArg

   6:  

//Output: DWORD

   7:  

//Author: Mercury        

   8:  

//Data  : 2009-12-26

   9:  

//=============================================================================

  10:  

DWORD MCU_DetectThread(PVOID pArg)
  11:  

{
  12:  

    DWORD dwRet, dwAction;
  13:  

    MCUCTL*pMcuCtl = (MCUCTL *)pArg;    
  14:  

    HANDLE rghEvents[2] = {pMcuCtl->hDeinitEvt, pMcuCtl->hGioEvt};    
  15:  

    unsigned char

  i = 0;
  16:  

    RETAILMSG(1, (TEXT("MCU_DetectThread Enter/r/n"

)));
  17:  

    CeSetThreadPriority(GetCurrentThread(), pMcuCtl->dwPriority256);
  18:  

    Sleep(3000);    
  19:  

    while

 (1) 
  20:  

    {
  21:  

        dwRet = WaitForMultipleObjects(2, rghEvents, FALSE, INFINITE);
  22:  

        if

(pMcuCtl->bDeinit)
  23:  

   {
  24:  

            return

 0;    
  25:  

       }            
  26:  

        switch

(dwRet) 
  27:  

  {
  28:  

        case

 WAIT_OBJECT_0:
  29:  

            RETAILMSG(1, (TEXT("mcu_DetectThread Wait deinit/r/n"

)));
  30:  

            //deinit event

  31:  

            return

 0;
  32:  

        break

;
  33:  

        
  34:  

        case

 WAIT_OBJECT_0+1: //power key event    

  35:  

            {
  36:  

                     //RETAILMSG(1, (TEXT("mcu_DetectThread Wait gio/r/n")));

  37:  

            //__try

  38:  

            //{

  39:  

                        OperationMCU(READ, &sendOut[0], 2);    //于mcu的通讯函数模块

  40:  

            #if

 1
  41:  

                for

(i = 0 ; i < 2 ; i++)
  42:  

                {
  43:  

                RETAILMSG(1,(TEXT("the %d number  SentOut Value for point variable is %x/r/n"

),i,sendOut[i]));
  44:  

                }
  45:  

            #endif

    
  46:  

            //}

  47:  

            //__except (GetExceptionCode() == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)

  48:  

            //{

  49:  

            //    RETAILMSG(1, (TEXT("Emcu_DetectThread MCU_gio!!!!/r/n")));

  50:  

            //    SetLastError(E_FAIL);

  51:  

            //}

  52:  

                SetEvent(gReadKeyEvent[0]);    /* 通知读函数, 外部中断按键按键按下 */

        
  53:  

            }
  54:  

        break

;
  55:  

        
  56:  

        default

:
  57:  

            //error

  58:  

            dwAction = 0x00;//MCU_STAT_NOCHANGE;

  59:  

        break

;
  60:  

        }
  61:  

         //do real action

  62:  

    }  
  63:  

    return

 0;
  64:  

}

这里大家可以注意下CeSetThreadPriority,WaitForMultipleObjects和Swich这三处的使用技巧。

XXX_Init这个函数在参考文章中所起到的作用相同,都是对中断引脚进行初始化工作,完成对对应引脚的初始化后,对我们上面定义好的XXX_DetectThread进行一个创建线程,在初始化结束的时候创建两个读键的事件,下面给出参考代码,部分敏感部分用伪代码代替。

   1:  

extern

 "C"

 DWORD MCL_Init(DWORD Index)
   2:  

{
   3:  

#if

 1
   4:  

    BOOL bEn;
   5:  

    DWORD IDThread, dwPullUpOrDown;
   6:  

    IOCTL_INFO ioctl_info;    
   7:  

    WINCE_GPIO_DEFINE pin;                        
   8:  

    MSGQUEUEOPTIONS msgQueueOptions = {0};
   9:  

    MCUCTL *pMcuCtl = NULL;
  10:  

#endif

  11:  

 
  12:  

    RETAILMSG(1,(TEXT("++MCU_Init!/r/n "

)));   
  13:  

    InitializeCriticalSection(&m_removalLock);//add by mercury for lock and unlock 20090819

  14:  

#if

 1//初始化中断开始

  15:  

    pMcuCtl  =  (MCUCTL *)LocalAlloc(LPTR, sizeof

(MCUCTL));
  16:  

    if

(pMcuCtl == NULL) 
  17:  

    {
  18:  

        RETAILMSG(1, (TEXT("-MCU_INIT/r/n"

)));
  19:  

            return

(0);
  20:  

        }
  21:  

    memset(pMcuCtl, 0, sizeof

(MCUCTL));
  22:  

 
  23:  

    //set rtc thread to priority to 104

  24:  

 
  25:  

     pMcuCtl->dwPriority256 = MCU_DEFAULT_THREAD_PRIORITY;
  26:  

 
  27:  

    pMcuCtl->hGioEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
  28:  

    pMcuCtl->hDeinitEvt= CreateEvent(NULL, FALSE, FALSE, NULL);
  29:  

    if

((pMcuCtl->hGioEvt == NULL) || (pMcuCtl->hDeinitEvt == NULL)  ){
  30:  

        DEBUGMSG(ZONE_ERROR, (TEXT("MCU_Init::CreateEvent failed/r/n"

)));
  31:  

      RETAILMSG(1,(TEXT("MCU_Init::CreateEvent failed/r/n"

)));
  32:  

        goto

 Error;
  33:  

    }    
  34:  

    
  35:  

    pMcuCtl->hDetectThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) MCU_DetectThread, (LPVOID)pMcuCtl, 0, &IDThread);
  36:  

    if

 (pMcuCtl->hDetectThread == 0) 
  37:  

    {
  38:  

        DEBUGMSG(ZONE_ERROR, (TEXT("MCU_Init::: CreateThread Failed/r/n"

)));
  39:  

     RETAILMSG(1,(TEXT("MCU_Init::: CreateThread Failed/r/n"

)));
  40:  

     goto

 Error;
  41:  

    }
  42:  

 
  43:  

    pMcuCtl->hGio= CreateFile(L"GIO1:"

, GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING, 0, NULL);
  44:  

    if

(pMcuCtl->hGio == INVALID_HANDLE_VALUE) {
  45:  

        DEBUGMSG(ZONE_ERROR, (TEXT("MCU_Init::Open gio failed/r/n"

)));
  46:  

     RETAILMSG(1,(TEXT("MCU_Init::: Open gio Failed/r/n"

)));
  47:  

        goto

 Error;
  48:  

    }
  49:  

    
  50:  

    //init gpio here  

  51:  

    //power button    gpio init start

  52:  

    pin  = WINCE_DGPIO2;//BSP_GetPowerButtonIO();

  53:  

    if

(pin  == WINCE_NULLIO) {
  54:  

        goto

 Error;        
  55:  

    }
  56:  

 
  57:  

    ioctl_info.pin = (    UINT32)pin;
  58:  

    //set input    

  59:  

    ioctl_info.parameter.Setting = GPIO_DIR_INPUT;
  60:  

    DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_DIRECTION,
  61:  

        (LPVOID)&ioctl_info, sizeof

(IOCTL_INFO), NULL, 0 ,NULL, NULL);
  62:  

        
  63:  

    //set event handle    

  64:  

    ioctl_info.parameter.Setting = (UINT32)pMcuCtl->hGioEvt;
  65:  

    DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_HANDLE,
  66:  

        (LPVOID)&ioctl_info, sizeof

(IOCTL_INFO), NULL, 0 ,NULL, NULL);
  67:  

    //polarity to trigger interrupt

  68:  

    ioctl_info.parameter.Setting = BSP_GetPowerButtonPolarity();
  69:  

    DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_POLARITY,
  70:  

        (LPVOID)&ioctl_info, sizeof

(IOCTL_INFO), NULL, 0 ,NULL, NULL);    
  71:  

    //set change polarity after interrupt

  72:  

    ioctl_info.parameter.Setting = GPIO_IO_INT_CHG_POLARITY;
  73:  

    DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_PINCHANGE,
  74:  

        (LPVOID)&ioctl_info, sizeof

(IOCTL_INFO), NULL, 0 ,NULL, NULL);    
  75:  

    //enable pull down

  76:  

    dwPullUpOrDown = BSP_GetPowerButtonPullUpDown(&bEn);
  77:  

    if

(bEn) {    
  78:  

        ioctl_info.parameter.Setting = TRUE;
  79:  

    }
  80:  

    else

 {    
  81:  

        ioctl_info.parameter.Setting = FALSE;
  82:  

    }    
  83:  

    DeviceIoControl(pMcuCtl->hGio, dwPullUpOrDown,
  84:  

        (LPVOID)&ioctl_info, sizeof

(IOCTL_INFO), NULL, 0 ,NULL, NULL);    
  85:  

    //enable power key interrupt

  86:  

    ioctl_info.parameter.Setting = GPIO_INTE_ENABLE;
  87:  

    DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_INTR,
  88:  

        (LPVOID)&ioctl_info, sizeof

(IOCTL_INFO), NULL, 0 ,NULL, NULL);
  89:  

    //初始化结束

  90:  

    gReadKeyEvent[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
  91:  

    gReadKeyEvent[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
  92:  

    RETAILMSG(1,(TEXT("--MCU_Init!/r/n "

)));   
  93:  

    return

(DWORD)pMcuCtl;    
  94:  

    
  95:  

Error:
  96:  

    (VOID)MCL_Deinit((DWORD)pMcuCtl); 
  97:  

    RETAILMSG(1,(TEXT("--error !MCU_Init!/r/n "

)));   
  98:  

    #endif

  99:  

    return

(0);
 100:  

}

 

这里请大家注意下CreateThread函数,这个函数是中断程序初始化函数中必须的,通过他去叫起前面定义的XXX_DetectThread函数。

XXX_Deinit函数是有始有终的表示,init对应的处理函数,在init函数中我们在最后的ERROR段中定义了几句话,就是针对当初始化出现问题是跳转至出错处理部分的处理,我们需要将一系列创建对象deinit,也就是将其销毁,防止占用空间以及中断再次启动的失败,也就是HJB大牛文章中的最后一句话:“在使用驱动调试助手调试有关中断的驱动程序时,需要善始善终,否则会出现中断不能正常工作的情况。”

下面给出参考代码

   1:  

extern

 "C"

 BOOL MCL_Deinit(DWORD dwData)
   2:  

{
   3:  

    MCUCTL *pMcuCtl = (MCUCTL *)dwData;
   4:  

    WINCE_AK7801_GPIO_DEFINE pin;        
   5:  

    pMcuCtl->bDeinit = TRUE;
   6:  

    RETAILMSG(1,(TEXT("MCU_DeInit!/r/n "

)));
   7:  

    DeleteCriticalSection(&m_removalLock);
   8:  

//add by mercury xu for lock and unlock si4730 20090819

   9:  

    if

(pMcuCtl->hGio) {
  10:  

        IOCTL_INFO ioctl_info;            
  11:  

        //disable power button interrupt

  12:  

        pin = WINCE_AK7801_DGPIO2;//BSP_GetPowerButtonIO();

  13:  

        if

(pin != WINCE_AK7801_NULLIO) {
  14:  

            ioctl_info.pin = (UINT32)pin;                 
  15:  

            ioctl_info.parameter.Setting = GPIO_INTE_DISABLE;
  16:  

            DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_INTR,
  17:  

                (LPVOID)&ioctl_info, sizeof

(IOCTL_INFO), NULL, 0 ,NULL, NULL);    
  18:  

        }
  19:  

        CloseHandle(pMcuCtl->hGio);
  20:  

        pMcuCtl->hGio = NULL;
  21:  

    }
  22:  

 
  23:  

    if

(pMcuCtl->hDetectThread) {
  24:  

     SetEvent(pMcuCtl->hDeinitEvt);
  25:  

        WaitForSingleObject(pMcuCtl->hDetectThread, 10000);
  26:  

     CloseHandle(pMcuCtl->hDetectThread);
  27:  

        pMcuCtl->hDetectThread = NULL;     
  28:  

    }
  29:  

 
  30:  

    if

(pMcuCtl->hDeinitEvt) {
  31:  

        CloseHandle(pMcuCtl->hDeinitEvt);
  32:  

        pMcuCtl->hDeinitEvt = NULL;             
  33:  

    }
  34:  

 
  35:  

    if

(pMcuCtl->hGioEvt) {
  36:  

        CloseHandle(pMcuCtl->hGioEvt);
  37:  

        pMcuCtl->hGioEvt = NULL;             
  38:  

    }
  39:  

    
  40:  

    if

(pMcuCtl->hPowerNotify) {
  41:  

        StopPowerNotifications(pMcuCtl->hPowerNotify);
  42:  

        pMcuCtl->hPowerNotify = NULL;     
  43:  

    }    
  44:  

    
  45:  

    if

(pMcuCtl->hMsgQ) {
  46:  

        CloseHandle(pMcuCtl->hMsgQ);
  47:  

        pMcuCtl->hMsgQ = NULL;     
  48:  

    }    
  49:  

    LocalFree(pMcuCtl);
  50:  

    pMcuCtl = NULL;
  51:  

    SetEvent(gReadKeyEvent[1]);        /* 通知调用读函数的线程, 驱动已经关闭 */

  52:  

    CloseHandle(gReadKeyEvent[0]);            /* 关闭相关事件 */

  53:  

    CloseHandle(gReadKeyEvent[1]);
  54:  

    return

 TRUE;
  55:  

}

 

 

最后是XXX_Read,因为这里我们采用的是中断方式,iocontrol在中断中不太适合使用,在轮询的方式下IOcontrol还是比较适合,这里XXX_Read函数中,我们通过pBuf来将值传递给API层的readfile文件中的lpBuffer,这里我们给出XXX_READ和READFILE两个函数的参考.

WINBASEAPI

BOOL

WINAPI

ReadFile(

    HANDLE hFile,

    LPVOID lpBuffer,//-> xxx_read :LPVOID
pBuf

,

    DWORD nNumberOfBytesToRead,

    LPDWORD lpNumberOfBytesRead,

    LPOVERLAPPED lpOverlapped

    );

DWORD xxx_Read(


  DWORD

 dwData



,


  LPVOID 

pBuf



,


  DWORD 

dwLen




);

下面给出参考代码

 

   1:  

extern

 "C"

 DWORD MCL_Read(DWORD dwData,
   2:  

               LPVOID pBuf,
   3:  

               DWORD Len)
   4:  

{
   5:  

    DWORD ret;
   6:  

    unsigned char

  *pReadBuffer = NULL;
   7:  

    if

 ((pBuf == NULL) || (Len <= 0))
   8:  

        return

 0;
   9:  

    pReadBuffer = (unsigned char

  *)MapPtrToProcess(pBuf, GetCallerProcess());
  10:  

    *pReadBuffer = NULL;
  11:  

    
  12:  

        ret = WaitForMultipleObjects(2, gReadKeyEvent, FALSE, INFINITE);    
  13:  

    if

 (ret == WAIT_OBJECT_0)
  14:  

    {
  15:  

        ResetEvent(gReadKeyEvent[0]);
  16:  

        *pReadBuffer = sendOut[0];                                        /* 按键按下 */

  17:  

        return

 1;
  18:  

    }
  19:  

    else

 if

(ret == (WAIT_OBJECT_0 + 1))
  20:  

    {
  21:  

        ResetEvent(gReadKeyEvent[1]);
  22:  

        *pReadBuffer = sendOut[1];                                        /* 驱动关闭 */

  23:  

        return

 1;        
  24:  

    }
  25:  

    return

(0);
  26:  

}

 

这里纠正下《WIinCE中断流式实现驱动和APP
》提供代码中的一个bug,pReadBuffer = (unsigned char
*)MapPtrToProcess(pBuf, GetCallerProcess());这里,需要强制类型转换一下MapPtrToProcess函数,因为该函数在msdn上描述为lpvoid型,而我们这里用到的pReadBuffer 是unsigned char*。

在这里我们也是需要等待我们所创建的gReadKeyEvent事件量,通过这个来判断中断执行的位置,同时作出相应的处理,到这里为止,驱动层的开发完成,对应的变量和结构体定义请大家按照自己的需求来定义,这里我只列出一个结构框架,大家可以做填空题的形式填空即可,这里也是符合wince开发的特点,填空式的开发。

应用层开发:

应用层的开发的灵活度比驱动层要大很多,这里我们以MFC为例,其实不论是MFC还是win32 api,其主旨都是在初始化的时候建立一个线程,类似于驱动层里的XXX_DetectThread和XXX_init之间的关系,这里我定义一个ReadKey1Thread函数,做为读取事件的入口,具体实现如下:

   1:  

DWORD CMCUReadDlg::ReadKey1Thread(LPVOID lparam)
   2:  

{
   3:  

    BYTE status;
   4:  

    DWORD actlen;
   5:  

    CString strCount;
   6:  

    CMCUReadDlg *pDlg = (CMCUReadDlg*)lparam;    
   7:  

    /* 取得对话框指针 */

   8:  

    CStatic *pCountStatic = (CStatic*)pDlg->GetDlgItem(IDC_NewShow);
   9:  

    /* 取得显示计数值的文本框指针 */

  10:  

    while

(TRUE)
  11:  

    {
  12:  

        if

 (hStr == INVALID_HANDLE_VALUE)
  13:  

            break

;                                                  /* 驱动未打开, 退出线程 */

  14:  

        if

 (ReadFile(hStr, &status, 1, &actlen, NULL) == TRUE)
  15:  

        {
  16:  

        Key1Count++;                                        /* 计数器计数 */

  17:  

        strCount.Format(_T("%d,0x%.2x"

), Key1Count,status);
  18:  

        pCountStatic->SetWindowText(strCount);    /* 显示 */

                
  19:  

        }
  20:  

        else

  21:  

        break

;    /* ReadFile()执行错误 */

  22:  

    }
  23:  

    return

 1;
  24:  

}

这里大家注意下while的处理,while里我们用了READFILE来于驱动层进行配对联合,读取驱动层来的信息,同时将数据用变量获取出来,实现非常简单。

这个函数完成后就是在AP的初始化部分进行线程的创建,程序很简单:

   1:  

    hReadKey1Thread = CreateThread(0, 0, ReadKey1Thread, this

, 0, &IDThread);
   2:  

 
   3:  

    if

 (hReadKey1Thread == NULL) 
   4:  

    {
   5:  

        CloseHandle(hStr);
   6:  

        hStr = INVALID_HANDLE_VALUE;
   7:  

        CloseHandle(hReadKey1Thread);
   8:  

        return

 FALSE;
   9:  

    }

 

 

以上我就把wince下中断驱动开发从驱动层到AP层的整个开发流程进行一个梳理,希望对大家有帮助。

抱歉!评论已关闭.