關於mfc作為上位機接收硬件端USB或串口數據顯示成圖片 解決串口接收數據丟字節丟包問題


 

原文作者:aircraft

原文地址:https://www.cnblogs.com/DOMLX/p/9490616.html

 

本文用的是VS2013MFC寫串口數據接收:

 

第一步:首先建立一個MFC工程,成功后會跳出一個對話框,直接在對話框上點擊右鍵-》點擊插入ACTIVAE控件-》選擇MicrosoftCommunications Control, version 6.0

成功后會顯示一個電話的圖標在對話框上,運行起來不會顯示的 不用擔心這個美觀問題。如果沒有這個插件的話,可能是版本太低  可以自己下載一個補上

 

第二步:大概的窗體搞好:   那個顯示圖片的大框是PICTURE控件變量

 

 

然后就要項目->類向導中定義變量了  如果你們是英文版就找英文字符對應的就行了。(英文不會比我還差吧 哈哈哈哈哈哈哈)

 

 

定義的變量大概如上圖所示 ,那個小電話就是串口通信最重要的    變量ID是   IDC_MSCOMM1    變量名如上圖:

 

 

 

這時候簡單的綁定變量后 要開始寫第一個小函數了  ,直接雙擊那個對話框上 BUTTON1這個按鈕

 

這時候就會生成一個關聯函數:代碼如下:

void CMFCApplication2Dlg::OnBnClickedButton1()
{
    // TODO:  在此添加控件通知處理程序代碼
    if (m_ctrlComm.get_PortOpen())
        m_ctrlComm.put_PortOpen(FALSE);

    m_ctrlComm.put_CommPort(4); //注意了  這里因為我硬件端接我電腦的口是com4  所以我打開一樣的    你們不知道自己是哪個的話 就打開硬件經常用的串口調試助手先找找  不過 測試的時候一定要關閉其他串口  不然會打開失敗的
    if (!m_ctrlComm.get_PortOpen())
    {
        m_ctrlComm.put_PortOpen(TRUE);//打開串口
        AfxMessageBox(L" open serial port successfully");
    }
    else
        AfxMessageBox(L"cannot open serial port");

    m_ctrlComm.put_Settings(L"9600,n,8,1"); //波特率9600,無校驗,8個數據位,1個停止位
    m_ctrlComm.put_InputMode(1); //1:表示以二進制方式檢取數據
    m_ctrlComm.put_RThreshold(1);
    //參數1表示每當串口接收緩沖區中有多於或等於1個字符時將引發一個接收數據的OnComm事件
    m_ctrlComm.put_InputLen(0); //設置當前接收區數據長度為0
    m_ctrlComm.get_Input();//先預讀緩沖區以清除殘留數據
}

 

 

好這是打開串口的函數 ,既然打開的串口那么硬件就要給我們發數據了 ,而mFC也要有接收的能力 所以這時候我們要添加一個 串口數據的響應函數:

 

可以雙擊那個消息函數改名字在點  添加處理程序應用:

 

 然后插入代碼:

void CMFCApplication2Dlg::onComm()
{
    // TODO:  在此處添加消息處理程序代碼
    VARIANT variant_inp;
    COleSafeArray safearray_inp;
    LONG len, k;
    //BYTE rxdata[1000480];//設置BYTE數組 An 8-bit integerthat is not signed.
    unsigned char * data = m_COMBits + m_COMIndex;
    CString strtemp;
    //m_COMIndex = 0;
    if (m_ctrlComm.get_CommEvent() == 2) //事件值為2表示接收緩沖區內有字符
    {             ////////以下你可以根據自己的通信協議加入處理代碼
        variant_inp = m_ctrlComm.get_Input(); //讀緩沖區  
        safearray_inp = variant_inp; //VARIANT型變量轉換為ColeSafeArray型變量  
        len = safearray_inp.GetOneDimSize(); //得到有效數據長度  注意了這里數據是一段一段接收的
        for (k = 0; k < len; ++k, ++m_COMIndex)
        {
            if (m_COMIndex > 240 * 320 - 1)
                break;
            safearray_inp.GetElement(&k, data + k);//轉換為BYTE型數組
        }
        if (m_COMIndex > 240 * 320 - 1)
        {
            m_COMIndex = 0;
            m_hasCOMImage = true;
            LoadImageData(m_COMImage, m_COMBits);
            OnPaint();
        }
    }
    //UpdateData(FALSE); //更新編輯框內容
}

 

 這里是我的圖片大小 240*320的:

你們的自己看    至於為什么要大於后馬上跳出循環呢   因為 接收數據是一段一段接收的從緩沖區  所以我們一次性接收夠了我們就跳出來  要是一直接收肯定會炸的  不信可以自己試試哈哈哈哈哈哈

 

還有這里有時候會出現一個問題,就是  串口傳輸數據的時候回丟包     有時候單步調試的時候卻不會丟包 丟字節   STM32   單片機51都有可能出現這種情況  (串口調試助手收發大量數據時是怎樣處理的新手求教,寫了一個串口調試助手,接收數據會丟幀串口通訊,丟包嚴重是什么問題為什么串口單步調試正常,全速會丟包)這是因為因為CPU處理速度太快導致FIFO中數據早就被讀完了,RBR為空,而后續的數據不能及時到達被MCU拋棄掉了。我加了一個延時就OK了   這里加延時 可以硬件端發送加  也可以MFC 中加  都可以反正  串口發送數據會丟包說白就是電腦跟不上  電腦垃圾    這時候我們就輔助一個延時函數 然程序停一下  慢點接  讓緩沖區有點東西在接收

 

下面是繪制圖片調用的函數:

第一個是 位圖的數據操作輔助用的    第二是將圖片數據LOAD  

bool CMFCApplication2Dlg::InitalImage(CImage &image, int width, int height)
{
    if (image.IsNull())
        image.Create(width, height, 8);
    else
    {
        if (width <= 0 || height <= 0)
            return false;
        else if (image.GetHeight() == width && image.GetWidth() == height)
            return true;
        else
        {
            image.Destroy();
            image.Create(width, height, 8);
        }
    }
    //寫入調色板
    RGBQUAD ColorTable[256];
    image.GetColorTable(0, 256, ColorTable);
    for (int i = 0; i < 256; i++)
    {
        ColorTable[i].rgbBlue = (BYTE)i;
        ColorTable[i].rgbGreen = (BYTE)i;
        ColorTable[i].rgbRed = (BYTE)i;
    }
    image.SetColorTable(0, 256, ColorTable);
    return true;
}
void CMFCApplication2Dlg::LoadImageData(CImage &image, unsigned char * data)
{
    if (data == nullptr)
        return;
    byte *pS;
    byte *pImg = (byte *)image.GetBits();
    int step = image.GetPitch();
    int height = image.GetHeight();
    int width = image.GetWidth();
    for (int i = 0; i < image.GetHeight(); ++i)
    {
        pS = data + i * width;
        for (int j = 0; j < image.GetWidth(); ++j)
        {
            *(pImg + i*step + j) = pS[j];
        }
    }
}

 

 

 同樣的 在Dlg頭文件中葯加上這兩個函數的聲明和用到變量的定義:

//位圖函數
    void CMFCApplication2Dlg::LoadImageData(CImage &image, unsigned char * data);
    bool CMFCApplication2Dlg::InitalImage(CImage &image, int width, int height);
    //位圖數據
    unsigned char m_COMBits[240 * 320];
    int m_COMIndex;
    bool m_hasCOMImage;
    CImage m_COMImage;

 

然后在看Dlg.cpp文件  我們還需要幾個函數:

第一  在Dlg::OnInitDialog()初始中 我們要先給圖片變量分配內存 不然程序會中斷:

InitalImage(m_COMImage, 240, 320);
    m_COMIndex = 0;

 

 

第二 要將圖片繪制 就要繪制函數中操作Dlg::OnPaint():

首先定義下面要用到的變量

    int cx, cy;
    CRect    rect;
    CWnd *pWnd = NULL;

然后加入調用的代碼:

else
    {
        if (m_hasCOMImage && !m_COMImage.IsNull())
        {
            //獲取圖片的寬 高度
            cx = m_COMImage.GetWidth();
            cy = m_COMImage.GetHeight();
            //ResizeWindow(cx, cy);
            //獲取IDC_PIC1的窗口指針
            pWnd = GetDlgItem(IDC_PICCOM);
            //獲取Picture Control控件的大小
            pWnd->GetWindowRect(&rect);
            //將客戶區選中到控件表示的矩形區域內
            ScreenToClient(&rect);
            //窗口移動到控件表示的區域
            pWnd->MoveWindow(rect.left, rect.top, cx, cy, TRUE);
            pWnd->GetClientRect(&rect);//獲取句柄指向控件區域的大小
            CDC *pDc = NULL;
            pDc = pWnd->GetDC();//獲取picture的DC
            m_COMImage.Draw(pDc->m_hDC, rect);//將圖片繪制到picture表示的區域內
            ReleaseDC(pDc);
        }

是在那個 onpaint():函數中的 else部分里面添加噢    給你們看看總體的  不要抄下面這個   用上面的就行了  復制下面這個也沒用 因為對話框有不同:

void CMFCApplication2Dlg::OnPaint()
{
    int cx, cy;
    CRect    rect;
    CWnd *pWnd = NULL;
    if (IsIconic())
    {
        CPaintDC dc(this); // 用於繪制的設備上下文

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // 使圖標在工作區矩形中居中
        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;

        // 繪制圖標
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        if (m_hasCOMImage && !m_COMImage.IsNull())
        {
            //獲取圖片的寬 高度
            cx = m_COMImage.GetWidth();
            cy = m_COMImage.GetHeight();
            //ResizeWindow(cx, cy);
            //獲取IDC_PIC1的窗口指針
            pWnd = GetDlgItem(IDC_PICCOM);
            //獲取Picture Control控件的大小
            pWnd->GetWindowRect(&rect);
            //將客戶區選中到控件表示的矩形區域內
            ScreenToClient(&rect);
            //窗口移動到控件表示的區域
            pWnd->MoveWindow(rect.left, rect.top, cx, cy, TRUE);
            pWnd->GetClientRect(&rect);//獲取句柄指向控件區域的大小
            CDC *pDc = NULL;
            pDc = pWnd->GetDC();//獲取picture的DC
            m_COMImage.Draw(pDc->m_hDC, rect);//將圖片繪制到picture表示的區域內
            ReleaseDC(pDc);
        }
        //確認對話框數據中是否有一張完整的圖像
        /*
        if (m_hasFileImage && !m_FileImage.IsNull())
        {
            //獲取圖片的寬 高度
            cx = m_FileImage.GetWidth();
            cy = m_FileImage.GetHeight();
            //ResizeWindow(cx, cy);
            //獲取IDC_PIC1的窗口指針
            pWnd = GetDlgItem(IDC_PICFILE);
            //獲取Picture Control控件的大小
            pWnd->GetWindowRect(&rect);
            //將客戶區選中到控件表示的矩形區域內
            ScreenToClient(&rect);
            //窗口移動到控件表示的區域
            pWnd->MoveWindow(rect.left, rect.top, cx, cy, TRUE);
            pWnd->GetClientRect(&rect);//獲取句柄指向控件區域的大小
            CDC *pDc = NULL;
            pDc = pWnd->GetDC();//獲取picture的DC
            m_FileImage.Draw(pDc->m_hDC, rect);//將圖片繪制到picture表示的區域內
            ReleaseDC(pDc);
        }
        */
        CDialogEx::OnPaint();
    }
}

 

要   清空緩沖 的數據的話    就這樣雙擊那個按鈕 加這個:

void CMFCApplication2Dlg::OnBnClickedButton3()
{
    // TODO:  在此添加控件通知處理程序代碼
    m_COMIndex = 0;
}

 

好了這就是所有的代碼了 ,語文不好可能需要一點MFC基礎才能聽得懂哈哈哈哈哈:

不過給你們准備了福利嘿嘿嘿,我測試的項目代碼:

MFC做上位機與USB串口連接傳輸數據顯示圖像:鏈接:https://pan.baidu.com/s/1iQyeu50-2joZgp4xedGzpg 密碼:bed9

 

若有興趣交流分享技術,可關注本人公眾號,里面會不定期的分享各種編程教程,和共享源碼,諸如研究分享關於c/c++,python,前端,后端,opencv,halcon,opengl,機器學習深度學習之類有關於基礎編程,圖像處理和機器視覺開發的知識


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM