zoukankan      html  css  js  c++  java
  • 【原创】再谈WinCE中断开发

    预备文章:

    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层的整个开发流程进行一个梳理,希望对大家有帮助。

  • 相关阅读:
    欠采样和过采样
    分类模型之K近邻算法
    机器学习之分类模型
    。。。
    等人
    习惯
    六月一日
    回首
    你还年轻他们老了
    C#和JavaScript交互(asp.net前台和后台互调)总结 (转)
  • 原文地址:https://www.cnblogs.com/mercuryxu/p/1639069.html
Copyright © 2011-2022 走看看