在VS2010的MFC下,用CSerialPort类时遇到下位机发送命令后上位机接收不到(下位机发BBBB),但是用串口精灵可以收到,我采用的是龚建伟老师介绍的加消息响应方法接收,是收到一次处理一次,不知道为什么会这样,怎么解决?网上有说是存到缓存从缓存处理,窝子安在这种情况怎么更改呢?希望有系统的解决思路或方法,非常感谢!
串口精灵可以收到,只能说明你的代码有问题。贴代码出来分析吧!
或者,自己分析:但所有用到的系统函数的返回值输出,看看是哪里失败了。串口接收不到,已经排除了硬件的可能性,只能是哪里执行失败了。
需要注意,串口的参数不要设错,设错了可是没有提示的。
这显然是这样,串口串口,当然一次只能传一位数据,你得开个缓冲区(比如说用个全局的数组),收到一个数据就追加进去。等到收到代表停止的字符,或者多长时间没有数据,或者缓冲区满了,再一次处理。
CSerialport类用的是https://github.com/liquanhai/CSerialPort的,修改了一下closeport和语法错误,具体是:
BOOL CSerialPort::InitPort(CWnd* pPortOwner, UINT portnr, UINT baud, char parity, UINT databits, UINT stopbits, DWORD dwCommEvents, UINT writebuffersize,DWORD ReadIntervalTimeout,DWORD ReadTotalTimeoutMultiplier,DWORD ReadTotalTimeoutConstant,DWORD WriteTotalTimeoutMultiplier,DWORD WriteTotalTimeoutConstant )
{
assert(portnr > 0 && portnr < 200);
assert(pPortOwner != NULL);
// if the thread is alive: Kill
if (m_bThreadAlive)
{
do
{
SetEvent(m_hShutdownEvent);
} while (m_bThreadAlive);
//TRACE("Thread ended\n");
}
// create events
if (m_ov.hEvent != NULL)
ResetEvent(m_ov.hEvent);
else
m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hWriteEvent != NULL)
ResetEvent(m_hWriteEvent);
else
m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hShutdownEvent != NULL)
ResetEvent(m_hShutdownEvent);
else
m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
// initialize the event objects
m_hEventArray[0] = m_hShutdownEvent; // highest priority
m_hEventArray[1] = m_ov.hEvent;
m_hEventArray[2] = m_hWriteEvent;
// initialize critical section
InitializeCriticalSection(&m_csCommunicationSync);
// set buffersize for writing and save the owner
m_pOwner = pPortOwner;
if (m_szWriteBuffer != NULL)
delete [] m_szWriteBuffer;
m_szWriteBuffer = new char[writebuffersize];
m_nPortNr = portnr;
m_nWriteBufferSize = writebuffersize;
m_dwCommEvents = dwCommEvents;
BOOL bResult = FALSE;
//char szPort = new char[50];
char *szBaud = new char[50];
// now it critical!
EnterCriticalSection(&m_csCommunicationSync);
// if the port is already opened: close it
if (m_hComm != NULL)
{
CloseHandle(m_hComm);
m_hComm = NULL;
}
// prepare port strings
//sprintf(szPort, "COM%d", portnr);
CString szPort;
szPort.Format(_T("COM%d"),portnr);
// stop is index 0 = 1 1=1.5 2=2
int mystop;
int myparity;
switch(stopbits)
{
case 0:
mystop = ONESTOPBIT;
break;
case 1:
mystop = ONE5STOPBITS;
break;
case 2:
mystop = TWOSTOPBITS;
break;
}
myparity = 0;
parity = toupper(parity);
switch(parity)
{
case 'N':
myparity = 0;
break;
case 'O':
myparity = 1;
break;
case 'E':
myparity = 2;
break;
case 'M':
myparity = 3;
break;
case 'S':
myparity = 4;
break;
}
sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop);
// get a handle to the port
m_hComm = CreateFile(szPort, // communication port string (COMX)
GENERIC_READ | GENERIC_WRITE, // read/write types
0, // comm devices must be opened with exclusive access
NULL, // no security attributes
OPEN_EXISTING, // comm devices must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // Async I/O
0); // template must be 0 for comm devices
if (m_hComm == INVALID_HANDLE_VALUE)
{
// port not found
/*delete [] szPort;/
delete [] szBaud;
return FALSE;
}
// set the timeout values
m_CommTimeouts.ReadIntervalTimeout = ReadIntervalTimeout * 1000;
m_CommTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier * 1000;
m_CommTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000;
m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
m_CommTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant * 1000;
// configure
if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
{
if (SetCommMask(m_hComm, dwCommEvents))
{
if (GetCommState(m_hComm, &m_dcb))
{
m_dcb.EvtChar = 'q';
m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high!
m_dcb.BaudRate = baud; // add by mrlong
m_dcb.Parity = myparity;
m_dcb.ByteSize = databits;
m_dcb.StopBits = mystop;
//if (BuildCommDCB(szBaud, &m_dcb))
//{
if (SetCommState(m_hComm, &m_dcb))
; // normal operation... continue
else
ProcessErrorMessage("SetCommState()");
//}
//else
// ProcessErrorMessage("BuildCommDCB()");
}
else
ProcessErrorMessage("GetCommState()");
}
else
ProcessErrorMessage("SetCommMask()");
}
else
ProcessErrorMessage("SetCommTimeouts()");
//delete [] szPort;
delete [] szBaud;
// flush the port
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
// release critical section
LeaveCriticalSection(&m_csCommunicationSync);
//TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);
return TRUE;
}
DWORD WINAPI CSerialPort::CommThread(LPVOID pParam)
{
// Cast the void pointer passed to the thread back to
// a pointer of CSerialPort class
CSerialPort *port = (CSerialPort*)pParam;
// Set the status variable in the dialog class to
// TRUE to indicate the thread is running.
port->m_bThreadAlive = TRUE;
// Misc. variables
DWORD BytesTransfered = 0;
DWORD Event = 0;
DWORD CommEvent = 0;
DWORD dwError = 0;
COMSTAT comstat;
BOOL bResult = TRUE;
// Clear comm buffers at startup
if (port->m_hComm) // check if the port is opened
PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
// begin forever loop. This loop will run as long as the thread is alive.
for (;;)
{
// Make a call to WaitCommEvent(). This call will return immediatly
// because our port was created as an async port (FILE_FLAG_OVERLAPPED
// and an m_OverlappedStructerlapped structure specified). This call will cause the
// m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to
// be placed in a non-signeled state if there are no bytes available to be read,
// or to a signeled state if there are bytes available. If this event handle
// is set to the non-signeled state, it will be set to signeled when a
// character arrives at the port.
// we do this for each port!
bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
if (!bResult)
{
// If WaitCommEvent() returns FALSE, process the last error to determin
// the reason..
switch (dwError = GetLastError())
{
case ERROR_IO_PENDING:
{
// This is a normal return value if there are no bytes
// to read at the port.
// Do nothing and continue
break;
}
case 87:
{
// Under Windows NT, this value is returned for some reason.
// I have not investigated why, but it is also a valid reply
// Also do nothing and continue.
break;
}
default:
{
// All other error codes indicate a serious error has
// occured. Process this error.
port->ProcessErrorMessage("WaitCommEvent()");
break;
}
}
}
else
{
// If WaitCommEvent() returns TRUE, check to be sure there are
// actually bytes in the buffer to read.
//
// If you are reading more than one byte at a time from the buffer
// (which this program does not do) you will have the situation occur
// where the first byte to arrive will cause the WaitForMultipleObjects()
// function to stop waiting. The WaitForMultipleObjects() function
// resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
// as it returns.
//
// If in the time between the reset of this event and the call to
// ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
// to the signeled state. When the call to ReadFile() occurs, it will
// read all of the bytes from the buffer, and the program will
// loop back around to WaitCommEvent().
//
// At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
// but there are no bytes available to read. If you proceed and call
// ReadFile(), it will return immediatly due to the async port setup, but
// GetOverlappedResults() will not return until the next character arrives.
//
// It is not desirable for the GetOverlappedResults() function to be in
// this state. The thread shutdown event (event 0) and the WriteFile()
// event (Event2) will not work if the thread is blocked by GetOverlappedResults().
//
// The solution to this is to check the buffer with a call to ClearCommError().
// This call will reset the event handle, and if there are no bytes to read
// we can loop back through WaitCommEvent() again, then proceed.
// If there are really bytes to read, do nothing and proceed.
bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
if (comstat.cbInQue == 0)
continue;
} // end if bResult
// Main wait function. This function will normally block the thread
// until one of nine events occur that require action.
Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
switch (Event)
{
case 0:
{
// Shutdown event. This is event zero so it will be
// the higest priority and be serviced first.
port->m_bThreadAlive = FALSE;
// Kill this thread. break is not needed, but makes me feel better.
//AfxEndThread(100);
::ExitThread(100);
break;
}
case 1: // read event
{
GetCommMask(port->m_hComm, &CommEvent);
if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中
ReceiveChar(port);
if (CommEvent & EV_CTS) //CTS信号状态发生变化
::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中
::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
if (CommEvent & EV_BREAK) //输入中发生中断
::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY
::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
if (CommEvent & EV_RING) //检测到振铃指示
::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
break;
}
case 2: // write event
{
// Write character event from port
WriteChar(port);
break;
}
} // end switch
} // close forever loop
return 0;
}
BOOL CSerialPort::StartMonitoring()
{
//if (!(m_Thread = AfxBeginThread(CommThread, this)))
if (!(m_Thread = ::CreateThread (NULL, 0, CommThread, this, 0, NULL )))
return FALSE;
//TRACE("Thread started\n");
return TRUE;
}
void CSerialPort::ClosePort()
{
while (m_bThreadAlive)
{
SetCommMask(m_hComm, 0);
SetEvent(m_hShutdownEvent);
m_bThreadAlive=FALSE;
}
if (m_ov.hEvent)
{
CloseHandle(m_ov.hEvent);
}
if (m_hWriteEvent)
{
CloseHandle(m_hWriteEvent);
}
if (m_hShutdownEvent)
{
CloseHandle(m_hShutdownEvent);
}
m_ov.hEvent = NULL;
m_hWriteEvent = NULL;
m_hShutdownEvent= NULL;
if (m_szWriteBuffer)
{
delete [] m_szWriteBuffer;
}
m_szWriteBuffer = NULL;
// // if the port is still opened: close it
if (m_hComm != NULL)
{
CloseHandle(m_hComm);
m_hComm = NULL;
}
}
我的主函数是:先是获得所有可用串口,在发送数据时依次向串口发数据(''''),接收下位机发送的命令(BBBB),之后开启显示界面(用串口精灵可以实现,但是用下位机收不到数据)
//串口通信内容
//第一项,查找所有可用串口
void CEEGClassfier::sendcommand(CString str)
{
//每次发送控制命令时清空串口号列表,查询新的串口号得到列表
CString comStr;//串口名称,例如COM1
int index=0;//记录可用串口数
if (portCount.begin()!=portCount.end())
{
portCount.clear();
}
for(int i=2;i {
comStr.Format(_T("COM%d"),i);
hCom=CreateFile(comStr,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
if (hCom!=INVALID_HANDLE_VALUE)
{
portCount.push_back(i);
index++;
}
CloseHandle(hCom);
}
if (index==0)
{
MessageBox(_T("没有可用串口!"));
return;
}
//想所有可用串口发送命令
LPSTR cstr=new char[200];
wsprintfA(cstr,"%ls",str);
//vector::iterator it = portCount.end() - 1;
for (unsigned int i=0;i<portCount.size();i++)
{
//auto indic=*it;
if (m_serialport[i].InitPort(this,portCount[i],38400,'N',8,1,EV_RXFLAG|EV_RXCHAR,4096,1,1,1,1,1))
{
m_serialport[i].StartMonitoring();
m_serialportOpend[i]=TRUE;
m_serialport[i].WriteToPort(cstr);
Sleep(1000);
}
}
delete cstr;
}
//接收数据
LONG CEEGClassfier::OnCommunication(WPARAM ch, LPARAM port)
{
if (indicator==0)
{
m_strReceiveMsg+=(CHAR)ch;
if (m_strReceiveMsg.GetLength()>=4)
{
readInstruction(port);
}
return 0;
}
else
{
datadeque.push_back((float)ch);
return 0;
}
}
//设置定时器用来检查下位机发送命令是否正确
void CEEGClassfier::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_strReceiveMsg.GetLength()< 4)
{
indicator = 0;//通讯握手未成功,还是处于通讯状态
if (!isShow)
{
for (unsigned int i = 0; i < portCount.size(); i++)
{
m_serialport[i].StopMonitoring();
m_serialport[i].ClosePort();//新添加的函数,设置线程m_bThreadAlive为False
}
isShow = true;
MessageBox(_T("下位机发送的指令长度不足,请重新发送!"),NULL,MB_OK);
}
if (traintime > 1)//第一次则作废(初始值1,不需减),第二、三次也是作废,减一次
{
traintime--;
}
}
KillTimer(1);
CDialogEx::OnTimer(nIDEvent);
}
//判断接收是否正确,开启界面刺激
void CEEGClassfier::readInstruction(int spNo)
{
if (m_strReceiveMsg== "BBBB")
{
for (unsigned int i = 0; i < portCount.size(); i++)
{
if ((i+2)!= spNo)
{
m_serialport[i].StopMonitoring();
m_serialport[i].ClosePort();//没有这个函数
}
}
sp_user = spNo;
indicator=1;
CStimuLate dlg;
int option=dlg.DoModal();
if (option==IDCANCEL)
{
m_serialport[spNo-2].StopMonitoring();
m_serialport[spNo-2].ClosePort();//没有这个函数
return;
}
//此处开一个线程
}
else
{
indicator= 0;//通讯握手未成功,还是处于通讯状态
if (!isShow)
{
isShow = true;//通讯失败
MessageBox(_T("上下位机通讯未满足握手协议,请重新发送命令!"),NULL,MB_OK);
for (unsigned int i = 0; i < portCount.size(); i++)
{
m_serialport[i].StopMonitoring();
m_serialport[i].ClosePort();//没有这个函数
}
}
if (traintime > 1)//第一次则作废(初始值1,不需减),第二、三次也是作废,减一次
{
traintime--;
}
}
}
我不知道如何分析串口接收不到,可不可以稍微具体点,非常感谢!
不太懂,进来学习一下啊
问题解决了,问题出在InitPort函数中,此程序在
/ configure
if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
{
if (SetCommMask(m_hComm, dwCommEvents))
{
if (GetCommState(m_hComm, &m_dcb))
{
m_dcb.EvtChar = 'q';
m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high!
m_dcb.BaudRate = baud; // add by mrlong
m_dcb.Parity = myparity;
m_dcb.ByteSize = databits;
m_dcb.StopBits = mystop;
//if (BuildCommDCB(szBaud, &m_dcb))
//{
if (SetCommState(m_hComm, &m_dcb))
; // normal operation... continue
else
ProcessErrorMessage("SetCommState()");
//}
//else
// ProcessErrorMessage("BuildCommDCB()");
}
else
ProcessErrorMessage("GetCommState()");
}
else
ProcessErrorMessage("SetCommMask()");
}
else
ProcessErrorMessage("SetCommTimeouts()");
应将//if (BuildCommDCB(szBaud, &m_dcb))
//{取消注销,就可以收到了