为什么 串口通信 分多次接收数据解决思路

为什么 串口通信 分多次接收数据

参考的代码如下所示,我是直接把串口2、3脚短接后测试的。请教下为啥数据都是分几个来接收的呢?这样我不好处理数据呢。
C/C++ code
// comDlg.cpp : implementation file
//

#include "stdafx.h"
#include "com.h"
#include "comDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif



UINT CommProc(LPVOID pParam);        //全局线程

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

// Dialog Data
    //{{AFX_DATA(CAboutDlg)
    enum { IDD = IDD_ABOUTBOX };
    //}}AFX_DATA

    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CAboutDlg)
    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //}}AFX_VIRTUAL

// Implementation
protected:
    //{{AFX_MSG(CAboutDlg)
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
    //{{AFX_DATA_INIT(CAboutDlg)
    //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CAboutDlg)
    //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
    //{{AFX_MSG_MAP(CAboutDlg)
        // No message handlers
    //}}AFX_MSG_MAP
    
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CComDlg dialog

CComDlg::CComDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CComDlg::IDD, pParent)
{
    //{{AFX_DATA_INIT(CComDlg)
    m_send = _T("");
    //}}AFX_DATA_INIT
    // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CComDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CComDlg)
    DDX_Control(pDX, IDC_EDIT2, m_receive);
    DDX_Control(pDX, IDC_COMBO2, m_bord);
    DDX_Control(pDX, IDC_COMBO1, m_port);
    DDX_Text(pDX, IDC_EDIT1, m_send);
    //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CComDlg, CDialog)
    //{{AFX_MSG_MAP(CComDlg)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON3, OnConnect)
    ON_BN_CLICKED(IDC_BUTTON1, OnSend)
    //}}AFX_MSG_MAP
    ON_MESSAGE(WM_COMMNOTIFY, OnCommNotify)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CComDlg message handlers

BOOL CComDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        CString strAboutMenu;
        strAboutMenu.LoadString(IDS_ABOUTBOX);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);            // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon
    
    // TODO: Add extra initialization here
    m_port.AddString("COM1");
    m_port.AddString("COM2");
    m_port.AddString("COM3");
    m_port.AddString("COM4");
    m_port.SetCurSel(0);

    m_bord.AddString("9600");
    m_bord.AddString("19200");
    m_bord.AddString("38400");
    m_bord.SetCurSel(0);
    //事件
    memset(&m_osRead, 0, sizeof(OVERLAPPED));
    memset(&m_osWrite, 0, sizeof(OVERLAPPED));

    if((m_hPostMsgEvent=CreateEvent(NULL, TRUE, TRUE, NULL))==NULL)
        MessageBox("为WM_COMMNOTIFY消息创建事件对象失败");
    // 为重叠读创建事件对象,手工重置,初始化为无信号的
    if((m_osRead.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL))==NULL)
        MessageBox("为重叠读创建事件对象失败");
    // 
    if((m_osWrite.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL))==NULL)
        MessageBox("为重叠写创建事件对象失败");

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CComDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CComDlg::OnPaint() 
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CComDlg::OnQueryDragIcon()
{
    return (HCURSOR) m_hIcon;
}


/////////////////////////////////////////////////////////
//                    用户代码部分
//
////////////////////////////////////////////////////////
//串口号列表
const CString ComName[4]={"COM1","COM2","COM3","COM4"};

//确认连接
void CComDlg::OnConnect() 
{
    CString sPort;
    COMMTIMEOUTS TimeOuts;                    //串口设置超时结构体
    sPort=ComName[m_port.GetCurSel()];        //获得所选串口号
    
    //打开一个串口设备
    h_com=CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 
        NULL); // 重叠方式
    
    if(h_com==INVALID_HANDLE_VALUE)
    {
        MessageBox("不能打开这个串口");
        return;
    }

    SetupComm(h_com,4096,4096);            //1.设置输入输出缓冲
    SetCommMask(h_com, EV_RXCHAR);        //2.设置事件



    //3.超时设置
    // 把间隔超时设为最大,把总超时设为0将导致ReadFile立即返回并完成操作
    TimeOuts.ReadIntervalTimeout=MAXDWORD; 
    TimeOuts.ReadTotalTimeoutMultiplier=0; 
    TimeOuts.ReadTotalTimeoutConstant=0;

    /* 设置写超时以指定WriteComm成员函数中的GetOverlappedResult函数的等待时间*/
    TimeOuts.WriteTotalTimeoutMultiplier=50;
    TimeOuts.WriteTotalTimeoutConstant=2000;

    SetCommTimeouts(h_com, &TimeOuts);

    //4.配置串口
    if(!ConfigConnect())
    {
        MessageBox("配置串口的时候失败");
        return;
    }
//    m_bConnected=TRUE;            //连接标志

    //5.打开接收数据线程
    //AfxBeginThread(CommProc,this);
    m_pThread=AfxBeginThread(CommProc, this, THREAD_PRIORITY_NORMAL, 
            0, CREATE_SUSPENDED, NULL); // 创建并挂起线程
    if(m_pThread==NULL)
    {
        CloseHandle(h_com);
    }
    else
    {
        m_bConnected=TRUE;
        m_pThread->ResumeThread(); // 恢复线程运行
    }
}

//发送函数
void CComDlg::OnSend() 
{
    // TODO: Add your control notification handler code here
    COMSTAT ComStat;
    ULONG nLength=0;
    UpdateData(true);
    ULONG szLength = m_send.GetLength();
    
    char *sz = new char[szLength];
    if(sz==NULL)
        return;
    memcpy(sz,m_send.GetBuffer(szLength),szLength);
    DWORD dwErrorFlags;

    ClearCommError(h_com,&dwErrorFlags,&ComStat);
    BOOL fState=WriteFile(h_com,sz,szLength,&nLength,&m_osWrite);
    if(!fState)
    {
        ULONG my_error=GetLastError();
        if(my_error==ERROR_IO_PENDING)
        {
            GetOverlappedResult(h_com,&m_osWrite,&nLength,TRUE);
        }
        else
            nLength=0;
    }
    delete[] sz;
    sz=NULL;
    
}




const ULONG TheBord[3]={9600,19200,38400}; 
//配置串口
BOOL CComDlg::ConfigConnect()
{
    DCB dcb;

    if(!GetCommState(h_com, &dcb))
        return FALSE;

    dcb.fBinary=TRUE;
    dcb.BaudRate = TheBord[m_bord.GetCurSel()];        // 数据传输速率
    dcb.ByteSize = 8;                                // 每字节位数
    dcb.fParity = TRUE;                                //校验
    dcb.Parity=NOPARITY;                            //为无校验
    dcb.StopBits=ONESTOPBIT;                        //1停止位
        
    // 硬件流控制设置
    dcb.fOutxCtsFlow = FALSE;
    dcb.fRtsControl = TRUE;

    // XON/XOFF流控制设置
    dcb.fInX=dcb.fOutX = TRUE;
    dcb.XonChar = 0x11;
    dcb.XoffChar = 0x13;
    dcb.XonLim = 50;
    dcb.XoffLim = 50;

    return SetCommState(h_com, &dcb);
}


//工作者线程
//用于判断串口是否接收到数据,如果有数据,就发信息给WM_COMMNOTIFY
// 工作者线程,负责监视串行口
//
UINT CommProc(LPVOID pParam)
{
    OVERLAPPED os;
    DWORD dwMask, dwTrans;
    COMSTAT ComStat;
    DWORD dwErrorFlags;
    CComDlg *pX1=(CComDlg*)pParam;

    memset(&os, 0, sizeof(OVERLAPPED));
    os.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);

    if(os.hEvent==NULL)
    {
        AfxMessageBox("Can't create event object!");
        return (UINT)-1;
    }

    while(pX1->m_bConnected)
    {
        ClearCommError(pX1->h_com,&dwErrorFlags,&ComStat);

        if(ComStat.cbInQue)
        {
            // 无限等待WM_COMMNOTIFY消息被处理完
            WaitForSingleObject(pX1->m_hPostMsgEvent, INFINITE);
            //重置事件
            ResetEvent(pX1->m_hPostMsgEvent);

            // 通知!!!
            PostMessage(pX1->GetSafeHwnd(),WM_COMMNOTIFY, EV_RXCHAR, 0);
                        //pX1->m_hTermWnd
            continue;
        }

        dwMask=0;

        if(!WaitCommEvent(pX1->h_com, &dwMask, &os)) // 重叠操作
        {

            if(GetLastError()==ERROR_IO_PENDING)
            // 无限等待重叠操作结果
                GetOverlappedResult(pX1->h_com, &os, &dwTrans, TRUE);
            else
            {
                CloseHandle(os.hEvent);
                return (UINT)-1;
            }
        }
    }

    CloseHandle(os.hEvent);
    return 0;
}


//串口消息函数(自己建立的消息)
LRESULT CComDlg::OnCommNotify(WPARAM wParam, LPARAM lParam)
{
    

    if( (!m_bConnected) || (wParam&EV_RXCHAR)!=EV_RXCHAR)
    {
        SetEvent(m_hPostMsgEvent);    //允许允许发送下一个信息
        return 0L;
    }
    //开始接收数据
    int nLength;
    char buf[4096/4];
    CString str;
    nLength=ReadComm(buf,100);        //获取数据和长度
    if(nLength)
    {
        for(int i=0;i<nLength;i++)
            str+=buf[i];
    
        m_receive.ReplaceSel(str);
    
    }
    SetEvent(m_hPostMsgEvent);        //允许发送下一个信息
    return 0L;


}



//读取串口数据,返回长度。
//输入参数:要读的长度
//注意事项:输入的要读的长度如果比实际缓冲里面的数据量大的话,返回的是较小的值
DWORD CComDlg::ReadComm(char *buf, DWORD dwLength)
{
    DWORD length=0;
    COMSTAT ComStat;
    DWORD dwErrorFlags;

    ClearCommError(h_com,&dwErrorFlags,&ComStat);
    length=min(dwLength, ComStat.cbInQue);        //看上面的注意事项
    ReadFile(h_com,buf,length,&length,&m_osRead);

    return length;
}