串口作为一老道但是实用的通讯工具,受到了广大开发者的青睐,这方面的代码和文章,很多。但对于刚学习的初学者来说,还是很有困难的。
我将自己的一点心得写出来,以便可能用的上的初学者以参考。
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也没有信号!