您的位置:首页 > 其它

再谈WinCE中断开发

2010-05-31 10:48 363 查看
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));


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 [i]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层的整个开发流程进行一个梳理,希望对大家有帮助。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: