您的位置:首页 > 运维架构

串口 API串口异步通讯

2008-04-01 23:07 218 查看
串口作为一老道但是实用的通讯工具,受到了广大开发者的青睐,这方面的代码和文章,很多。但对于刚学习的初学者来说,还是很有困难的。

我将自己的一点心得写出来,以便可能用的上的初学者以参考。

1.找大牛的代码拿来参考,不要胡乱看一些网上的代码,因为他们可能把你带入歧途。推荐CSerialPort类。

2.首先要明确自己要同步采集还是异步采集,我自己看的是异步。

3.异步采集过程中,我遇到了收字符串,WaitCommEvent()中的第三个LPOVERLAPPED类型的事件变量被置成2次有效,第二次有效没有接收到任何字符。带着这个疑问,做了如下试验

WaitCommEvent(m_hMyComm, &wEven, &oLapped);

  INT nEvent =  WaitForSingleObject(oLapped.hEvent,  INFINITE);

  ResetEvent(oLapped.hEvent);

  fprintf(fp,"Again!-1/n");

  for(int i = 0;i<5;i++)

  {

   //Sleep(1);

   WaitCommEvent(m_hMyComm, &wEven, &oLapped);

   WaitForSingleObject(oLapped.hEvent,  INFINITE);

   ResetEvent(oLapped.hEvent);

   fprintf(fp,"Again!%d/n",i);

  }

按照msdn上所述,每发送一个字节在调用WaitCommEvent()之后,事件都会被置为有效。所以,

@1在发送1个字节的情况下,只打印出Again!-1;

@2发送2,3,4这样的少字节,打印出Again!-1和Again!0;

@3发送多个字节时候,就可以将5条Again都打印出来;

以上所述,可以看到,当oLapped.hEvent第一次有信号时,在执行WaitForSingleObject和ReadFile之间,串口驱动器还在接收其他字节,注意:这段时间是在WaitForSingleObject和ReadFile之间代码执行所用的时间,例如,在正常的如下代码中

UINT CNewSerielPortDlg::MyCommThreadProcess(LPVOID lpvoid)

{

 FILE* fp;

 fp = fopen("f://SerielPortLog.txt","w+");

 CNewSerielPortDlg* pDlg = (CNewSerielPortDlg*)lpvoid;

 

 DWORD dErrInformation;

 COMSTAT comState;

 //important-不能通过中断

 OVERLAPPED oLapped;

 oLapped.Offset = 0;

 oLapped.OffsetHigh = 0;

 oLapped.hEvent = NULL;

 oLapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

 //important-否则收不到消息

 OVERLAPPED m_osRead;

 memset(&m_osRead,0,sizeof(OVERLAPPED));

 m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

 

 SetCommMask(m_hMyComm, EV_RXCHAR);

 if (m_hMyComm != NULL)

  ClearCommError(m_hMyComm,&dErrInformation,&comState);

 char czReceiveBuffer[100] = {0};

 SYSTEMTIME systime;

 CString name;

 while (TRUE)

 {

  DWORD wEven;

  INT bResult = WaitCommEvent(m_hMyComm, &wEven, &oLapped);//异步操作!

  if (!bResult)

  {

   DWORD BytesRead = 0;

   switch ( GetLastError())

            {

    case ERROR_IO_PENDING:

     break;

    case 87:

     break;

    default:

     break;

   }

  }

  else

  {

   ClearCommError(m_hMyComm, &dErrInformation, &comState);

   if (comState.cbInQue == 0)

   {

    fprintf(fp,"comState.cbInQue == 0/n");

    continue;

   }

  }

  INT nEvent =  WaitForSingleObject(oLapped.hEvent,  INFINITE);

  if (nEvent == WAIT_OBJECT_0)

  {

   DWORD CommEvent = 0;

   GetCommMask(m_hMyComm, &CommEvent);

   if (CommEvent & EV_RXCHAR ==  EV_RXCHAR)

   {

    BOOL  bRead = TRUE;

    BOOL  bResult = TRUE;

    DWORD dwError = 0;

    DWORD BytesRead = 0;

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

    COMSTAT comstat;//lingshi 

    for (;;)

    {

     //fprintf(fp,"for循环体%d/n",CommEvent);

     DWORD dRBufferSize = 100;

     // Gain ownership of the comm port critical section.

     // This process guarantees no other part of this program

     // is using the port object.

     // ClearCommError() will update the COMSTAT structure and

     // clear any other errors.   

     bResult = ClearCommError(m_hMyComm, &dwError, &comstat);   

     // start forever loop.  I use this type of loop because I

     // do not know at runtime how many loops this will have to

     // run. My solution is to start a forever loop and to

     // break out of it when I have processed all of the

     // data available.  Be careful with this approach and

     // be sure your loop will exit.

     // My reasons for this are not as clear in this sample

     // as it is in my production code, but I have found this

     // solution to be the most efficient way to do this.    

     if (comstat.cbInQue == 0)

     {

      GetLocalTime(&systime);

      name.Format("%02d-%02d-%02dm %02d:%02d:%02d",

       systime.wYear,

       systime.wMonth,

       systime.wDay,systime.wHour, systime.wMinute, systime.wSecond);

      // break out when all bytes have been read

      fprintf(fp,name+"for循环体------exit from comstat.cbInQue == 0--------/n");

      break;

     }    

     if (bRead)

     {

      bResult = ReadFile(m_hMyComm,czReceiveBuffer, 100, &dRBufferSize, &m_osRead);

     

     

      GetLocalTime(&systime);

      name.Format("%02d-%02d-%02dm %02d:%02d:%02d",

       systime.wYear,

       systime.wMonth,

       systime.wDay,systime.wHour, systime.wMinute, systime.wSecond);

      fprintf(fp,name);

      for (int k = 0; k < dRBufferSize; k++)

      {     

       fprintf(fp,"%c",czReceiveBuffer[k]);

      }

      fprintf(fp,"/n");

     

      // deal with the error code

      if (!bResult) 

      {

       switch (dwError = GetLastError())

       {

       case ERROR_IO_PENDING: 

        {

         bRead = FALSE;

         break;

        }

       default:

        {

         break;

        }

       }

      }

      else

      {

       bRead = TRUE;

      }

     }  // close if (bRead)

    

     if (!bRead)

     {

      bRead = TRUE;

      bResult = GetOverlappedResult(m_hMyComm, // Handle to COMM port

       &oLapped,   // Overlapped structure

       &BytesRead,  // Stores number of bytes read

       TRUE);   // Wait flag

     

      // deal with the error code

      if (!bResult) 

      {

       //port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");

      }       <
4000
br />
     }  // close if (!bRead)

    

    } // end forever loop

   }

  }

 GetLocalTime(&systime);

 name.Format("%02d-%02d-%02dm %02d:%02d:%02d",

   systime.wYear,

   systime.wMonth,

   systime.wDay,systime.wHour, systime.wMinute, systime.wSecond);

 fprintf(fp,name+"____________________________while 循环一次!____________________________/n/n");

 ReadCount++;

 //ResetEvent( oLapped.hEvent);

   //SetCommMask(m_hMyComm, EV_RXCHAR);

 }

 fclose(fp);

 return TRUE;

}

无论发送的字节数多么长,但是一次接收的字节数一定是14,(可能和不同人的电脑和波特率有关,这里只是举例说明一下问题),但是如果变成

UINT CNewSerielPortDlg::MyCommThreadProcess(LPVOID lpvoid)

{

 FILE* fp;

 fp = fopen("f://SerielPortLog.txt","w+");

 CNewSerielPortDlg* pDlg = (CNewSerielPortDlg*)lpvoid;

 

 DWORD dErrInformation;

 COMSTAT comState;

 //important-不能通过中断

 OVERLAPPED oLapped;

 oLapped.Offset = 0;

 oLapped.OffsetHigh = 0;

 oLapped.hEvent = NULL;

 oLapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

 //important-否则收不到消息

 OVERLAPPED m_osRead;

 memset(&m_osRead,0,sizeof(OVERLAPPED));

 m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

 

 SetCommMask(m_hMyComm, EV_RXCHAR);

 if (m_hMyComm != NULL)

  ClearCommError(m_hMyComm,&dErrInformation,&comState);

 char czReceiveBuffer[100] = {0};

 SYSTEMTIME systime;

 CString name;

 while (TRUE)

 {

  DWORD wEven;

  INT bResult = WaitCommEvent(m_hMyComm, &wEven, &oLapped);//异步操作!

  if (!bResult)

  {

   DWORD BytesRead = 0;

   switch ( GetLastError())

            {

    case ERROR_IO_PENDING:

     break;

    case 87:

     break;

    default:

     break;

   }

  }

  else

  {

   ClearCommError(m_hMyComm, &dErrInformation, &comState);

   if (comState.cbInQue == 0)

   {

    fprintf(fp,"comState.cbInQue == 0/n");

    continue;

   }

  }

  INT nEvent =  WaitForSingleObject(oLapped.hEvent,  INFINITE);

  ResetEvent(oLapped.hEvent);

  fprintf(fp,"Again!-1/n");

  for(int i = 0;i<5;i++)

  {

   //Sleep(1);

   WaitCommEvent(m_hMyComm, &wEven, &oLapped);

   WaitForSingleObject(oLapped.hEvent,  INFINITE);

   ResetEvent(oLapped.hEvent);

   fprintf(fp,"Again!%d/n",i);

  }

  //Sleep(500);

  if (nEvent == WAIT_OBJECT_0)

  {

   DWORD CommEvent = 0;

   GetCommMask(m_hMyComm, &CommEvent);

   if (CommEvent & EV_RXCHAR ==  EV_RXCHAR)

   {

    BOOL  bRead = TRUE;

    BOOL  bResult = TRUE;

    DWORD dwError = 0;

    DWORD BytesRead = 0;

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

    COMSTAT comstat;//lingshi 

    for (;;)

    {

     //fprintf(fp,"for循环体%d/n",CommEvent);

     DWORD dRBufferSize = 100;

     // Gain ownership of the comm port critical section.

     // This process guarantees no other part of this program

     // is using the port object.

     // ClearCommError() will update the COMSTAT structure and

     // clear any other errors.   

     bResult = ClearCommError(m_hMyComm, &dwError, &comstat);   

     // start forever loop.  I use this type of loop because I

     // do not know at runtime how many loops this will have to

     // run. My solution is to start a forever loop and to

     // break out of it when I have processed all of the

     // data available.  Be careful with this approach and

     // be sure your loop will exit.

     // My reasons for this are not as clear in this sample

     // as it is in my production code, but I have found this

     // solution to be the most efficient way to do this.    

     if (comstat.cbInQue == 0)

     {

      GetLocalTime(&systime);

      name.Format("%02d-%02d-%02dm %02d:%02d:%02d",

       systime.wYear,

       systime.wMonth,

       systime.wDay,systime.wHour, systime.wMinute, systime.wSecond);

      // break out when all bytes have been read

      fprintf(fp,name+"for循环体------exit from comstat.cbInQue == 0--------/n");

      break;

     }    

     if (bRead)

     {

      bResult = ReadFile(m_hMyComm,czReceiveBuffer, 100, &dRBufferSize, &m_osRead);

     

     

      GetLocalTime(&systime);

      name.Format("%02d-%02d-%02dm %02d:%02d:%02d",

       systime.wYear,

       systime.wMonth,

       systime.wDay,systime.wHour, systime.wMinute, systime.wSecond);

      fprintf(fp,name);

      for (int k = 0; k < dRBufferSize; k++)

      {     

       fprintf(fp,"%c",czReceiveBuffer[k]);

      }

      fprintf(fp,"/n");

     

      // deal with the error code

      if (!bResult) 

      {

       switch (dwError = GetLastError())

       {

       case ERROR_IO_PENDING: 

        {

         bRead = FALSE;

         break;

        }

       default:

        {

         break;

        }

       }

      }

      else

      {

       bRead = TRUE;

      }

     }  // close if (bRead)

    

     if (!bRead)

     {

      bRead = TRUE;

      bResult = GetOverlappedResult(m_hMyComm, // Handle to COMM port

       &oLapped,   // Overlapped structure

       &BytesRead,  // Stores number of bytes read

       TRUE);   // Wait flag

     

      // deal with the error code

      if (!bResult) 

      {

       //port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");

      }      

     }  // close if (!bRead)

    

    } // end forever loop

   }

  }

 GetLocalTime(&systime);

 name.Format("%02d-%02d-%02dm %02d:%02d:%02d",

   systime.wYear,

   systime.wMonth,

   systime.wDay,systime.wHour, systime.wMinute, systime.wSecond);

 fprintf(fp,name+"____________________________while 循环一次!____________________________/n/n");

 ReadCount++;

 //ResetEvent( oLapped.hEvent);

   //SetCommMask(m_hMyComm, EV_RXCHAR);

 }

 fclose(fp);

 return TRUE;

}

中间加入了那些红色代码,接收的字节数就会增加,所以,就有论坛上一些说,如果在这两者中间加入Sleep(若干时间)就可以全部接收到,这个是可以说明问题,但是,在实际的工程应用中,是不允许的,因为通讯可能在任意时刻发起,如果Sleep,就及其可能漏掉通讯的信息。

        这样,为什么会接收两次就很容易解释了,因为在第一次WaitForSingleObject和ReadFile之间代码执行所用的时间的时间内,串口把全部的字节都读出来,但是,当第2次运行到WaitCommEvent(m_hMyComm, &wEven, &oLapped);时,肯定是被置为有信号,这样,自然就接收了2次,且第二次接收了0字节。但是,当第一次接收完要发送的若干字符串后,Sleep一段时间,再从新回到while的开始调用WaitCommEvent,oLapped.hEvent也没有信号!

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息