今天终于将串口通讯的接收端勉强实现了。 基于学习简单, 我是利用Win32的串口通讯API实现了一个控制台程序。
首先附上代码
#include
#include
#include
//Global variable
HANDLE m_hCommPort;
DWORD WINAPI ComThreadFunc();
void main()
{
DCB dcb;
char input;
printf("This is a main thread!\n");
printf("Do you want to open COM6(Yes/no)?\n");
scanf("%c",&input);
if('y'==input)
{
/*Open a file! Here is comm!*/
m_hCommPort= ::CreateFile("COM6",
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if(INVALID_HANDLE_VALUE==m_hCommPort)
{
printf("invalid handle!\n");
}
/*Set comm state!*/
if(!SetupComm(m_hCommPort,1024,1024)) // set the inqueue&outqueue buffer
{
printf("Setup Comm input buffer and output buffer failed!\n");
}
if (!GetCommState(m_hCommPort,&dcb)) {
// Handle the error.
printf ("GetCommState failed with error %d.\n", GetLastError());
return;
}
dcb.BaudRate = CBR_115200; // set the baud rate
dcb.ByteSize = 8; // data size, xmit, and rcv
dcb.Parity = NOPARITY; // no parity bit
dcb.StopBits = ONESTOPBIT; // one stop bit
if (!SetCommState(m_hCommPort, &dcb)) {
// Handle the error.
printf ("SetCommState failed with error %d.\n", GetLastError());
return;
}
printf("set com6 with baudrate 115200 seccussfully!\n");
/*Set comm mask*/
if(!SetCommMask(m_hCommPort,EV_RXCHAR|EV_RXFLAG))
{
printf("Set comm mask failed!");
}
/*Create thread!*/
HANDLE hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ComThreadFunc,NULL,0,NULL);
CloseHandle(hThread);
while(1)
{
//main thread
}
}
else if('n'==input)
{
return;
}
else
{
printf("Please just input yes or no!\n");
Sleep(5000);
return;
}
}
DWORD WINAPI ComThreadFunc()
{
char buff[1024]={0};
DWORD res,factbyte;
COMSTAT rst;
int fError,commEvError;
DWORD dwEvtMask,CommResult,readResult;
BOOL bRet;
BOOL fWaitingOnStat=FALSE; // indicate if wait comm event is pending.
OVERLAPPED osComm = {0}; // overlapped structure for comm event operations
OVERLAPPED osReader = {0}; // overlapped structure for read operations
printf("I am the comm thread!\n");
osComm.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (osComm.hEvent == NULL)
printf("Create comm Event failed!\n");
osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osReader.hEvent == NULL)
printf("Create reader Event failed!\n");
while(1)
{
if(!fWaitingOnStat)
{
bRet=WaitCommEvent(m_hCommPort,&dwEvtMask,&osComm);
if(!bRet)
{
commEvError=GetLastError();
assert(commEvError==ERROR_IO_PENDING);
fWaitingOnStat=TRUE;
}
else
{
//WaitCommEvent always will return FALSE for overlapped I/O
}
}
/*wait for pending operations to complete*/
if(fWaitingOnStat)
{
CommResult=WaitForSingleObject(osComm.hEvent,500);//Now it can't get the event???Block for ever!!
switch(CommResult)
{
case WAIT_OBJECT_0:
ClearCommError(m_hCommPort,&res,&rst);
if(!ReadFile(m_hCommPort,buff,rst.cbInQue,&factbyte,&osReader))
{
fError=GetLastError();
assert(fError==ERROR_IO_PENDING);
readResult=WaitForSingleObject(osReader.hEvent,500);
switch(readResult)
{
case WAIT_OBJECT_0:
printf("%s",buff);
memset(buff,0,1024);
fWaitingOnStat=FALSE;
case WAIT_TIMEOUT:
//ClearCommError(m_hCommPort,&res,&rst);
//SetCommMask(m_hCommPort,EV_RXCHAR|EV_RXFLAG);
break;
default:
//do nothing!
break;
}
}
else
{
buff[rst.cbInQue]=0;
printf("%s",buff);
memset(buff,0,1024);
fWaitingOnStat=FALSE;
}
break;
case WAIT_TIMEOUT:
//ClearCommError(m_hCommPort,&res,&rst);
//SetCommMask(m_hCommPort,EV_RXCHAR|EV_RXFLAG);
break;
default:
//do nothing!
break;
}
}
}
return 0L;
}
现在修改WaitForSingleObject的time-out interval 为500(原来是用INFINITE一直阻塞的),目前简单实现了类似超级终端的接收端功能, 待以后完善并且利用MFC制作GUI界面的程序。
另外对标题的WaitCommEvent返回87的原因, 参考以下说明:
Take a look at
ERROR_INVALID_PARAMETER.
The issue is that WaitCommEvent is being invoked with
the same OVERLAPPED structure before the system has
completed the previous overlapped I/O.
In the case of ERROR_IO_PENDING, the code should use
a WaitForSingleObject, WaitForMultipleObjects, or
GetOverlappedResult so that the OVERLAPPED object
hEvent member becomes signalled before invoking
WaitCommEvent again.
WaitCommEvent在重叠I/O模式下不会阻塞, 而是立即返回的。
但通常都会失败,报ERROR_IO_PENDING错误。
在Pending状态下, 我们必须使用WaitForSingleObject, WaitForMultipleObjects或者GetOverlappedResult 等待Event产生,
在这个等待过程中,我们不能再去调用WaitCommEvent函数, 否则会产生87---ERROR_INVALID_PARAMETER错误。
只有当上述 wait function等待到Event,并将Event置位为有信号状态下,下次再去调用WaitCommEvent才不会报87错误。
故对87错误有如下几种解决方法:
1. 按上述代码, 在WaitCommEvent前加一个判断, 判断当前的Pending状态是否处理完,如果没处理完, 就不再调用WaitCommEvent函数。
2. WaitForSingleObjects的time-out interval 使用INFINITE,这样就和非重叠的效果是一样的了, 阻塞住了。
3. 我们可以对WAIT_TIMEOUT的case下对Event进行重新设定, 调用SetCommMask, 由于SetCommMask后WaitCommEvent立即返回,故下次调用时不会报87的错误。
参考MSDN的解释
If a process attempts to change the device handle's event mask by using the SetCommMask function while an overlapped WaitCommEvent operation is in progress, WaitCommEvent returns immediately. The variable pointed to by the lpEvtMask parameter is set to zero.
当然第三种方法可能不太正确, 但确实也能解决87这个问题。