首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > VC/MFC >

VC 串口接收数据的解析解决方案

2012-06-02 
VC 串口接收数据的解析以前没弄过串口编程,所以问题可能会比较白痴,我把问题描述的详细点。从串口接收数据,

VC 串口接收数据的解析
以前没弄过串口编程,所以问题可能会比较白痴,我把问题描述的详细点。


从串口接收数据,串口会一直返回这样格式的数据:02001401018200000C334455660000000000000000421E,

02: 数据头
00 14:全数据长度
01 01:特殊含义的字段
82 00:特殊含义的字段
00 0C:需要得到的数据的长度
334455660000000000000000 需要得到的数据
421E:CRC校验

问题,我需要接收到的数据应该是02001401018200000C334455660000000000000000421E一整条,
但结果却是半条,或者一条半,两条半

为什么会这样,该怎样解决,最好详细点。


代码如下:

我接受数据的函数:
读串口:

C/C++ code
DWORD CCESeries::ReadThreadFunc(LPVOID lparam){    CCESeries *ceSeries = (CCESeries*)lparam;        DWORD    evtMask;    BYTE * readBuf = NULL;//读取的字节    DWORD actualReadLen=0;//实际读取的字节数    DWORD willReadLen;        DWORD dwReadErrors;    COMSTAT    cmState;        // 清空缓冲,并检查串口是否打开。    ASSERT(ceSeries->m_hComm !=INVALID_HANDLE_VALUE);             //清空串口    PurgeComm(ceSeries->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR );        SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );    while (TRUE)    {               if (WaitCommEvent(ceSeries->m_hComm,&evtMask,0))        {                        SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );            //表示串口收到字符                    if (evtMask & EV_RXCHAR)             {                ClearCommError(ceSeries->m_hComm,&dwReadErrors,&cmState);                willReadLen = cmState.cbInQue ;                if (willReadLen <= 0)                {                    continue;                }                                //分配内存                readBuf = new BYTE[willReadLen];                ZeroMemory(readBuf,willReadLen);                //读取串口数据                ReadFile(ceSeries->m_hComm, readBuf, willReadLen, &actualReadLen,0);                                //如果读取的数据大于0,                if (actualReadLen>0)                {                    //触发读取回调函数                    if (ceSeries->m_OnSeriesRead)                    {                        ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);                    }                }                //释放内存                delete[] readBuf;                readBuf = NULL;            }        }        //如果收到读线程退出信号,则退出线程        if (WaitForSingleObject(ceSeries->m_hReadCloseEvent,500) == WAIT_OBJECT_0)        {            break;        }        //Sleep(1000);    }    return 0;}


串口响应函数:
C/C++ code
void CALLBACK MyDlg::OnSerialRead(void * pOwner,BYTE* buf,DWORD bufLen){    BYTE *pRecvBuf = NULL; //接收缓冲区    //得到父对象指针    MyDlg* pThis = (MyDlg*)pOwner;    //将接收的缓冲区拷贝到pRecvBuf种    pRecvBuf = new BYTE[bufLen];    CopyMemory(pRecvBuf,buf,bufLen);    //发送异步消息,表示收到串口数据,消息处理完,应释放内存    pThis->PostMessage(WM_RECV_SERIAL_DATA,WPARAM(pRecvBuf),bufLen);}

数据处理函数:
C/C++ code
LONG MyDlg::OnRecvSerialData(WPARAM wParam,LPARAM lParam){    CString strRecv = L"";    //串口接收到的BUF    BYTE *pBuf = (BYTE*)wParam;    //串口接收到的BUF长度    DWORD dwBufLen = lParam;    CString temp;       for(int i=0;i<lParam;i++)       {           temp.Format(L"%02x",pBuf[i]);           strRecv+=temp;       }       //将新接收到的文本添加到接收框中    m_table.InsertItem(0,strRecv);    //释放内存    delete[] pBuf;    pBuf = NULL;    return 0;}






[解决办法]
我觉得是数据传输可能有延迟,时钟怎么定都不可能完全达到一个周期刚好收一条完整的数据
你都知道定长了,还不好办,检查strRecv长度,到这么多时就处理显示
当然要注意CRC效验正确才处理,不然这一条就丢掉,等下一条
------解决方案--------------------


只要涉及到这种数据收发程序,不管是串口还是网口,都应该考虑接收端无法一次接收所有数据的问题。
我以前的做法是接收线程将接收到的数据一直追加到buf后面,由另一个子程序去分析处理该buf的内容。不过要注意线程同步问题,避免接收线程和数据处理线程同时操作buf.
[解决办法]
我做过一个串口通讯的软件,本来要求很简单的,但是由于linux内核一直狂打印数据,所以做的后来就复杂了,但是也更加完备了,基本跟SecureCRT差不多了。

我的做法是申请一个COM缓冲区大小一样的缓存(只可以更大些。或者2倍大小)。当WaitCommEvent得到消息后,立即读取缓冲中的数据以追加的方式到缓存,并清空COM缓冲。

 在缓存中,如果半条数据,如果是前半条,那就暂存数据,等后半条到达。

如果是一条半数据的话,就读取一条数据。把那半条数据放到缓存的头部,然后其他位置清空。等到后半条到达。

如果是2条半,就读取2条数据,把那半条数据放到缓存的头部,然后其他位置清空。等到后半条到达,这样,就在外部完成了数据的拼接了。
[解决办法]
这个其实跟网络通信中的拆包一样的道理。你数据不是已经有了一个数据长度的“域”了吗?为什么不用它呢?第一段数据,你都按照这个长度取,取完后就是下一段数据了,没取够就不处理。

热点排行