串口 API串口异步通讯

来源:互联网 发布:php代码执行漏洞 编辑:程序博客网 时间:2024/04/28 11:57

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

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

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()");
      }      
     }  // 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也没有信号!