2 zhaojuntaow zhaojuntaow 于 2014.12.12 17:34 提问

VS2010下MFC CSerialport类接收问题

在VS2010的MFC下,用CSerialPort类时遇到下位机发送命令后上位机接收不到(下位机发BBBB),但是用串口精灵可以收到,我采用的是龚建伟老师介绍的加消息响应方法接收,是收到一次处理一次,不知道为什么会这样,怎么解决?网上有说是存到缓存从缓存处理,窝子安在这种情况怎么更改呢?希望有系统的解决思路或方法,非常感谢!

5个回答

91program
91program   Ds   Rxr 2014.12.12 18:51
已采纳

串口精灵可以收到,只能说明你的代码有问题。贴代码出来分析吧!
或者,自己分析:但所有用到的系统函数的返回值输出,看看是哪里失败了。串口接收不到,已经排除了硬件的可能性,只能是哪里执行失败了。
需要注意,串口的参数不要设错,设错了可是没有提示的。

zhaojuntaow
zhaojuntaow   2014.12.14 10:42

问题解决了,问题出在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))
//{取消注销,就可以收到了
caozhy
caozhy   Ds   Rxr 2014.12.12 20:41

这显然是这样,串口串口,当然一次只能传一位数据,你得开个缓冲区(比如说用个全局的数组),收到一个数据就追加进去。等到收到代表停止的字符,或者多长时间没有数据,或者缓冲区满了,再一次处理。

zhaojuntaow
zhaojuntaow   2014.12.12 20:43

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--;
        }
    }

}
我不知道如何分析串口接收不到,可不可以稍微具体点,非常感谢!

wangyaninglm
wangyaninglm   Ds   Rxr 2014.12.13 09:57

不太懂,进来学习一下啊

Csdn user default icon
上传中...
上传图片
插入图片