標題: Visual C++ MFC數據采集與通信及圖形顯示應用程序的設計 [打印本頁]
作者: zhaoqingpeng 時間: 2018-11-30 17:25
標題: Visual C++ MFC數據采集與通信及圖形顯示應用程序的設計
2、設計任務書
設計MFC應用程序,實現圖形顯示、數據采集和數據通信功能。程序設計可以選用下列兩種結構:
(1)基于對話框的MFC應用程序;
(2)基于文檔/視圖的MFC應用程序;
設計應用程序界面時,可根據選用的程序結構,通過控件/菜單實現相關的控制功能。
2.1設計任務一:圖形顯示程序設計
1、根據功能要求設計軟件界面,設計圖形顯示區域的坐標系統和坐標變換公式,要求圖形顯示區域能夠顯示不少于200點數據;
2、選用適當的繪圖工具(包括畫筆、畫刷和字體等)和繪圖函數繪制圖形顯示區域;
3、使用隨機數函數rand()產生數據,實現多點數據采集功能,每次采集一屏數據,手動刷新;
4、使用隨機數函數rand()產生數據,實現單點數據采集功能,每次采集一個數據,引入WM_TIMER消息,實現定時采集并刷新。數據點數超過數據顯示區域范圍時,曲線自動左移,實現滾動顯示;
2.2設計任務二:數據采集程序設計
5、在項目中引入數據源設備DLL庫文件;
6、設計數據源設備控制模塊,包括設備打開、參數設置和設備關閉等模塊;
7、使用數據源設備DLL接口,實現單點數據采集功能,采集正弦波、方波和三角波等信號,每次采集一個數據,引入WM_TIMER消息,實現定時采集并刷新;
2.3設計任務三:串口通信程序設計
8、在項目中引入MSCOMM串口通信控件,設計串口控制功能模塊,包括“打開串口”、“關閉串口”、“參數設置”和串口數據接收處理模塊;
9、根據MODBUS通信協議,實現軟件和模擬流量計的通信接口模塊,包括數據包組包、數據包解包和CRC校驗等模塊;
10、通過串口通信方式,采集模擬流量計的質量流量數據,引入WM_TIMER消息,實現定時采集并顯示質量流量參數曲線。
3、軟件設計
經過小組討論,確定了程序整體結構,選擇基于對話框的MFC應用程序,應用各種控件實現程序設計要求。
3.1圖形顯示程序設計
1、根據功能要求設計軟件界面,設計圖形顯示區域的坐標系統和坐標變換公式,要求圖形顯示區域能夠顯示不少于200點數據;
設計方案1:坐標系統:邏輯坐標系統;設備坐標系統(屏幕坐標系統、窗口坐標系統、用戶區坐標系統)。繪圖區域定位坐標為六行十列,繪出一份從(0,0)到(520,320)的矩形網格,網格大小為50*50,坐標功能實現在OnPanint()函數中,通過不斷刷新來實現數據的刷新。并且定義了一個m_npoint【】數組來儲存數據實現曲線數據點的儲存。
2、選用適當的繪圖工具(包括畫筆、畫刷和字體等)和繪圖函數繪制圖形顯示區域;
設計方案2:
CPen類的成員函數CreatePen()用于創建畫筆,其原型為:
BOOL CreatePen (int nPenStyle,
int nWidth, COLORREF crColor);
第1個參數是畫筆樣式,各種虛線只有當線寬為1時有效。第2個參數為線寬,第3個參數為線的顏色,可使用RGB()宏指定。
創建畫刷的成員函數的原型為:
BOOL CreateSolidBrush ( COLORREF crColor );
參數crColor指定了畫刷的顏色。除此而外,還可以創建一個陰影風格的畫刷:
BOOL CreateHatchBrush ( int nIndex,COLORREF crColor );
其中參數nIndex指定了陰影風格。
繪圖用到紅色、綠色和藍色三種顏色寬度為3的線。
1)繪制直線
CDC::MoveTo(int x, int y)
將畫筆移動到當前位置,即坐標(x, y)處,并沒有畫線。
CDC::LineTo(int x, int y)
畫筆從當前位置繪制一條子線到(x, y)點,但不包含(x, y)點。
(3) 繪制矩形
CDC::Rectangle(int x1, int y1, int x2, int y2)
參數x1、y1表示矩形左上角坐標,參數x2、y2表示矩形右下角坐標。
(6) 繪制橢圓函數
CDC::Ellipse(int x1, int y1, int x2, int y2)
參數x1、y1是繪制橢圓外接矩形左上角的坐標,x2、y2是外接矩形的右下角坐標。當繪制的外接矩形長和寬相同,即繪制的是圓。
3、使用隨機數函數rand()產生數據,實現多點數據采集功能,每次采集一屏數據,手動刷新;
設計方案3:圖形刷新是繪圖過程中必須考慮的重要問題,包括:刷新請求、對刷新請求的響應、刷新方法。當用戶區的內容需要刷新時,系統向應用程序消息隊列發送WM_PAINT消息。當用戶拖動最小化窗口時系統調用此函數取得光標,操作界面為左側是網格,右側為多點采集的點擊窗口,點擊一次即對數據進行采集,點擊刷新則刷新一次重新采集數據。
4、使用隨機數函數rand()產生數據,實現單點數據采集功能,每次采集一個數據,引入WM_TIMER消息,實現定時采集并刷新。數據點數超過數據顯示區域范圍時,曲線自動左移,實現滾動顯示;
設計方案4:定時器:可以幫助開發者或者用戶定時完成某項任務。在使用定時器時,我們可以給系統傳入一個時間間隔數據,然后系統就會在每個此時間間隔后觸發定時處理程序,實現周期性的自動操作。例如,我們可以在數據采集系統中,為定時器設置定時采集時間間隔為1個小時,那么每隔1個小時系統就會采集一次數據,這樣就可以在無人操作的情況下準確的進行操作。
MFC定時器:VS2010編程中,我們可以使用MFC的CWnd類提供的成員函數SetTimer實現定時器功能,也可以使用Windows API函數SetTimer來實現。兩者使用方法實際上很類似,但也有不同。CWnd類的SetTimer成員函數只能在CWnd類或其派生類中調用,而API函數SetTimer則沒有這個限制,這是一個很重要的區別。
單點采集及顯示:在定時器的ontimer()函數中,當數據點沒超出顯示范圍時,利用隨機函數rand()產生數據依次儲存到m_point[]數組中,在onpaint()函數中,利用ployline()函數連接m_point()中的點,定義一個m_nPointNum變量,從1變到99,因此連接m_nPointNum個點,實現信號的單點采集。
滾動顯示:當數據點超出顯示區域后,也就是m_nPointNum變量等于100時,m_point[]數組中的每一個數代替后一個數也就是m_Point[k].y = m_Point[k+1].y,然后只剩下一個m_point[99],令其等于新產生的隨機變量,實現滾動顯示。
單點采集開始后,添加控件函數Invalidate()通知處理程序代碼并且刷新窗口,當采集點數超過屏幕時則顯示范圍左移,以此實現滾動顯示。
3.2數據采集程序設計
5、在項目中引入數據源設備DLL庫文件;
設計方案5:DLL(Dynamic Link Library)文件為動態鏈接庫文件,又稱“應用程序拓展”,是軟件文件類型。在Windows中,許多應用程序并不是一個完整的可執行文件,它們被分割成一些相對獨立的動態鏈接庫,即DLL文件,放置于系統中。當我們執行某一個程序時,相應的DLL文件就會被調用。一個應用程序可使用多個DLL文件,一個DLL文件也可能被不同的應用程序使用,這樣的DLL文件被稱為共享DLL文件。
lib庫:一種是靜態lib(static Lib),也就是最常見的lib庫,在編譯時直接將代碼加入程序當中。靜態lib中,一個lib文件實際上是任意個obj文件的集合,obj文件是cpp文件編譯生成的。
另一種lib包含了函數所在的DLL文件和文件中函數位置的信息(入口),代碼由運行時加載在進程空間中的DLL提供。也就是平時編寫dll時附帶產生的lib,其中Lib只是Dll的附帶品,是DLL導出的函數列表文件而已。
共同點:兩者都是二進制文件,都是在鏈接時調用,使用static lib的exe可以直接運行,使用另一種lib的exe需要對應的dll才能運行。
編譯之后生成一個mylib.lib的庫文件,引用庫文件如下:
(1)右擊工程-->屬性(alt+F7)-->C/C++-->附加包含目錄--->包含所需要的頭文件;
(2)右擊工程-->屬性(alt+F7)-->鏈接器-->常規--->附加目錄--->包含lib庫所在的目錄;
(3)右擊工程-->屬性(alt+F7)-->輸入--->附加依賴項--->lib庫名。
這樣就能引用該lib內的函數了。
dll的調用:
(1)dll的顯示調用:前提是我們必須知道函數名、返回值、參數列表。此時不需要相應的頭文件,也不需要lib文件。只需要對應的dll就可以了。
這種情況下:HINSTANCE hInstance = LoadLibrary("mydll1.dll");會出現”2 IntelliSense: "const char *" 類型的實參與 "LPCWSTR" 類型的形參不兼容 “問題。解決辦法:工程--->屬性--->常規-->字符集--->使用多字節字符集就可以了。
(2)dll的隱式調用:
這是相應的lib庫就派上用場了,其用法static library用法一致。
總結:顯示調用和隱式調用這兩種方法各有千秋,顯示調用優點是只要接口參數列表沒有發生改變,修改了函數實現的細節也沒關系,所調用的exe程序不用重新編譯只需替換新版的dll就可以。在大的項目中只用比較方便,但調用過程相對復雜需要使用一系列的windows函數還有函數指針等。對于隱式調用和static library用法一致比較容易理解,缺點是只要修改了任意內容相應的exe都需要重新編譯。
6、設計數據源設備控制模塊,包括設備打開、參數設置和設備關閉等模塊;
設計方案6:數據源設備的操作界面為,左側為數據網格,右側為包括“設備打開”、“設備關閉”、“數據采集”、“停止”、“方波”、“三角波”、“正弦波”和“刷新”的界面,各個控件皆為按鈕控件。設備打開直接調用DLL庫中的OpenDevice()函數,設備關閉直接調用DLL庫中的CloseDevice()函數,參數設置需要用滾動條的采集點和滾動參數調用到DLL庫中的SetSignalPara()函數,實現三個不同信號的參數設置,包括幅值,頻率,相位設置。
7、使用數據源設備DLL接口,實現單點數據采集功能,采集正弦波、方波和三角波等信號,每次采集一個數據,引入WM_TIMER消息,實現定時采集并刷新;
設計方案7:三種信號的數據采集均為單點采集,切換采集信號類型的操作方式:設備打開后選擇波類型,然后開始數據采集。切換其他波時需先進行“停止”,再清屏刷新,不然最新產生的波形會在之前產生的波形之后顯示,點擊其他波形后再點擊數據采集,從而進行切換采集數據類型的操作。切換數據類型的方法是改變參數m_flag的值,然后通過ontimer()函數生成新的信號并且儲存,不斷刷新Onpaint()函數實現。
3.3串口通信程序設計
8、在項目中引入MSCOMM串口通信控件,設計串口控制功能模塊,包括“打開串口”、“關閉串口”、“參數設置”和串口數據接收處理模塊;
設計方案8:MSCOMM控件:屬性描述 CommPort 設置并返回通訊端口號。 Settings 以字符串的形式設置并返回波特率、奇偶校驗、數據位、停止位。 PortOpen 設置并返回通訊端口的狀態。也可以打開和關閉端口。 Input 從接收緩沖區返回和刪除字符。 Output 向傳輸緩沖區寫一個字符串。
“啟動串口”“關閉串口”“參數設置”均位于操作界面的左側,“串口數據接收模塊”位于操作界面的右上方,各類流量計的參數顯示在相應的參數區域內,“實時曲線”則位于操作界面的右下方,
9、根據MODBUS通信協議,實現軟件和模擬流量計的通信接口模塊,包括數據包組包、數據包解包和CRC校驗等模塊;
設計方案9:Modbus是一種串行通信協議,是Modicon公司(現在的施耐德電氣 Schneider Electric)于1979年為使用可編程邏輯控制器(PLC)通信而發表。Modbus已經成為工業領域通信協議的業界標準(De facto),并且現在是工業電子設備之間常用的連接方式。
使用RING BUFF,接收部分就接收不分析數據,然后外面單獨解碼,可以再加上超時RESET。或者直接用dll或庫里的函數的參數,程序調用函數時,直接把通信內容放參數里。
CRC校驗原理:
其根本思想就是先在要發送的幀后面附加一個數(這個就是用來校驗的校驗碼,但要注意,這里的數也是二進制序列的,下同),生成一個新幀發送給接收端。當然,這個附加的數不是隨意的,它要使所生成的新幀能與發送端和接收端共同選定的某個特定數整除(注意,這里不是直接采用二進制除法,而是采用一種稱之為“模2除法”)。到達接收端后,再把接收到的新幀除以(同樣采用“模2除法”)這個選定的除數。因為在發送端發送數據幀之前就已通過附加一個數,做了“去余”處理(也就已經能整除了),所以結果應該是沒有余數。如果有余數,則表明該幀在傳輸過程中出現了差錯。
模2除法:
模2除法與算術除法類似,但每一位除的結果不影響其它位,即不向上一位借位,所以實際上就是異或。在循環冗余校驗碼(CRC)的計算中有應用到模2除法。
CRC校驗步驟:
CRC校驗中有兩個關鍵點,一是預先確定一個發送送端和接收端都用來作為除數的二進制比特串(或多項式),可以隨機選擇,也可以使用國際標準,但是最高位和最低位必須為1;二是把原始幀與上面計算出的除數進行模2除法運算,計算出CRC碼。
具體步驟:
(1)選擇合適的除數;
(2)看選定除數的二進制位數,然后再要發送的數據幀上面加上這個位數-1位的0,然后用新生成的幀以模2除法的方式除上面的除數,得到的余數就是該幀的CRC校驗碼。注意,余數的位數一定只比除數位數少一位,也就是CRC校驗碼位數比除數位數少一位,如果前面位是0也不能省略;
(3)將計算出來的CRC校驗碼附加在原數據幀后面,構建成一個新的數據幀進行發送;最后接收端在以模2除法方式除以前面選擇的除數,如果沒有余數,則說明數據幀在傳輸的過程中沒有出錯。
先將一個float型轉化為內存存儲格式的步驟:
(1)先將這個實數的絕對值化為二進制格式;
(2)將這個二進制格式實數的小數點左移或右移n位,直到小數點移動到第一個有效數字的右邊;
(3)從小數點右邊第一位開始數出二十三位數字放入第22到第0位;
(4)如果實數是正的,則在第31位放入“0”,否則放入“1”;
(5)如果n是左移得到的,說明指數是正的,第30位放入“1”。如果n是右移得到的或n=0,則第30位放入“0”;
(6)如果n是左移得到的,則將n減去1后化為二進制,并在左邊加“0”補足七位,放入第29到第23位。
如果n是右移得到的或n=0,則將n化為二進制后在左邊加“0”補足七位,再各位求反,再放入第29到第23位。
再將一個內存存儲的float二進制格式轉化為十進制:
(1)將第22位到第0位的二進制數寫出來,在最左邊補一位“1”,得到二十四位有效數字。將小數點點在最左邊那個“1”的右邊;
(2)取出第29到第23位所表示的值n。當30位是“0”時將n各位求反。當30位是“1”時將n增1;
(3)將小數點左移n位(當30位是“0”時)或右移n位(當30位是“1”時),得到一個二進制表示的實數;
(4)將這個二進制實數化為十進制,并根據第31位是“0”還是“1”加上正號或負號即可。
10、通過串口通信方式,采集模擬流量計的質量流量數據,引入WM_TIMER消息,實現定時采集并顯示質量流量參數曲線。
設計方案10:
將采集到的數據中反映質量流量的數據存入一一個BYTE類型的數組,轉換為float類型后,進行參數曲線繪制,由于顯示區域顯示數值最大為300,而采集數據值的范圍約為0 3600,所以將采集數據乘系數-0.05并且起點加上310的偏移量,以便于在圖形顯示區進行顯示。
4、軟件開發實現
4.1圖形顯示程序實現代碼說明
1、根據功能要求設計軟件界面,設計圖形顯示區域的坐標系統和坐標變換公式,要求圖形顯示區域能夠顯示不少于200點數據;
代碼段1:本段代碼實現了繪制網格線的功能
代碼:
CPaintDC dc(this);
CPen PenGrid;
CPen PenLine;
CPen *pOldPen = NULL;
dc.Rectangle(0,0,520,320);
PenGrid.CreatePen(PS_DASH,1,RGB(255,0,0));
pOldPen = dc.SelectObject(&PenGrid);
for(int k=0; k<7;k++) // 畫方格線
{
dc.MoveTo(10,10+50*k);
dc.LineTo(510,10+50*k);
}
for(int k=0; k<11; k++)
{
dc.MoveTo(10+50*k,10);
dc.LineTo(10+50*k,310);
}
2、選用適當的繪圖工具(包括畫筆、畫刷和字體等)和繪圖函數繪制圖形顯示區域;
代碼段1:本段代碼實現了選線型的功能
代碼:
UpdateData(TRUE);//DDX 控件--〉變量
switch(m_nLineStyle) //選線型
{
case 0:
PenLine.CreatePen(PS_SOLID,3,
RGB(m_Red.GetScrollPos(),
m_Green.GetScrollPos(),
m_Blue.GetScrollPos()));
break;
case 1:
PenLine.CreatePen(PS_DASH,1,
RGB(m_Red.GetScrollPos(),
m_Green.GetScrollPos(),
m_Blue.GetScrollPos()));
break;
case 2:
PenLine.CreatePen(PS_DOT,1,
RGB(m_Red.GetScrollPos(),
m_Green.GetScrollPos(),
m_Blue.GetScrollPos()));
break;
}
3、使用隨機數函數rand()產生數據,實現多點數據采集功能,每次采集一屏數據,手動刷新;
代碼段1:本段代碼實現了多點采集數據的功能
代碼:
void CDemoDlg::OnClickedRefresh() //多點采集 手動刷新
{
// TODO: 在此添加控件通知處理程序代碼
m_flag=0;
for(int k=0; k<100;k++)
{
m_Point[k].x = 10+5*k;
m_Point[k].y = 10+rand()%300;
}
Invalidate();// 發送WM_PAINT消息
}
void CDemoDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)//顏色設置滑動塊
{
// TODO: 在此添加消息處理程序代碼和/或調用默認值
UINT Pos;
switch(nSBCode)
{
case SB_THUMBTRACK://拖動滑動塊
pScrollBar->SetScrollPos(nPos);
break;
case SB_LINELEFT:
Pos = pScrollBar->GetScrollPos();
pScrollBar->SetScrollPos(Pos-10);
break;
case SB_LINERIGHT:
Pos = pScrollBar->GetScrollPos();
pScrollBar->SetScrollPos(Pos+10);
break;
case SB_PAGELEFT:
Pos = pScrollBar->GetScrollPos();
pScrollBar->SetScrollPos(Pos-30);
break;
case SB_PAGERIGHT:
Pos = pScrollBar->GetScrollPos();
pScrollBar->SetScrollPos(Pos+30);
break;
}
Invalidate();// 發送WM_PAINT消息
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
4、使用隨機數函數rand()產生數據,實現單點數據采集功能,每次采集一個數據,引入WM_TIMER消息,實現定時采集并刷新。數據點數超過數據顯示區域范圍時,曲線自動左移,實現滾動顯示;
代碼段1:本段代碼實現了單點采集數據的功能
代碼:
void CDemoDlg::OnClickedShownow() //實現單點采集
{ m_flag=1;
SetTimer(1,50,NULL);
m_nRunSta = 1;
}
void CDemoDlg::OnBnClickedShowstop() //停止采集
{
KillTimer(1);
m_nRunSta = 0;// TODO: 在此添加控件通知處理程序代碼
}
void CDemoDlg::OnTimer(UINT nIDEvent) //單點采集定時循環
{
if(m_nPointNum<100)
{
m_Point[m_nPointNum].x = 10+5*m_nPointNum;
m_Point[m_nPointNum].y = 10+rand()%300;
m_nPointNum++;
}
else //點數超過屏幕顯示范圍左移
{
for(int k=0; k<99; k++)
{
m_Point[k].y = m_Point[k+1].y;
}
m_Point[99].y = 10+rand()%300;
}
Invalidate(); // 通知刷新窗口
CDialog::OnTimer(nIDEvent);
}
if(m_flag==1) //單點采集
{
dc.Polyline(m_Point,m_nPointNum);
}
4.2數據采集程序實現代碼說明
5、在項目中引入數據源設備DLL庫文件;
代碼段1:本段代碼實現了引入頭文件的功能
代碼:#include "MyDLL.h"
// DemoDlg.cpp : 實現文件
//
#include "stdafx.h"
#include "Demo.h"
#include "DemoDlg.h"
#include "afxdialogex.h"
#include "MyDLL.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
DLL文件:
extern "C"
{
int OpenDevice();/// 打開設備
/// 返回值:0-成功 其他-失敗
void SetSignalPara(int pSigType, double pAmp, double pFreq, double pPhase0);/// 設置信號參數
/// pSigType:信號類型,0-正弦信號;1-方波信號;2-三角波信號
/// pAmp:信號幅值
/// pFreq:信號頻率,單位:HZ
/// pPhase0:信號初始相位,單位:弧度
void SetSWDuty(double pDuty);/// 設置方波占空比/// pDuty:方波占空比,取值范圍0.00-1.00
void SetSampPara(double pSampFreq);/// 設置采樣參數 /// pSampFreq:采樣頻率,單位:點/秒
int ReadOneData(double *pData);/// 采集正弦信號數據(單點)
/// pData:數據緩存區地址
/// 返回值:0-成功 其他-失敗
int ReadData(int Num, double *pData);/// 采集正弦信號數據(多點)
/// Num:數據點數
/// pData:數據緩存區地址
/// 返回值:0-成功 其他-失敗
int CloseDevice(void);/// 關閉設備
/// 返回值:0-成功 其他-失敗
}
6、設計數據源設備控制模塊,包括設備打開、參數設置和設備關閉等模塊;
代碼段1:本段代碼實現了設備打開和關閉的功能
void CDemoDlg::OnClickedRun()
{
OpenDevice();
AfxMessageBox(_T("設備打開成功"));
// TODO: 在此添加控件通知處理程序代碼
}
void CDemoDlg::OnBnClickedClose()
{
// TODO: 在此添加控件通知處理程序代碼
CloseDevice();
AfxMessageBox(_T("設備關閉成功"));
}
代碼段2:實現了參數設置功能
//參數設置初始化
m_fuzhi.SetScrollRange(0,150);
m_fuzhi.SetScrollPos(150);
m_pinlv.SetScrollRange(10,150);
m_pinlv.SetScrollPos(10);
m_xiangwei.SetScrollRange(0,150);
m_xiangwei.SetScrollPos(0);
void CDemoDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) //滾動條拖動處理參數
{
// TODO: 在此添加消息處理程序代碼和/或調用默認值
UINT Pos;
switch(nSBCode)
{
case SB_THUMBTRACK://拖動滑動塊
pScrollBar->SetScrollPos(nPos);
break;
case SB_LINELEFT:
Pos = pScrollBar->GetScrollPos();
pScrollBar->SetScrollPos(Pos-10);
break;
case SB_LINERIGHT:
Pos = pScrollBar->GetScrollPos();
pScrollBar->SetScrollPos(Pos+10);
break;
case SB_PAGELEFT:
Pos = pScrollBar->GetScrollPos();
pScrollBar->SetScrollPos(Pos-30);
break;
case SB_PAGERIGHT:
Pos = pScrollBar->GetScrollPos();
pScrollBar->SetScrollPos(Pos+30);
break;
}
Invalidate();// 發送WM_PAINT消息
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
SetSignalPara(0,m_fuzhi.GetScrollPos(),m_pinlv.GetScrollPos(),m_xiangwei.GetScrollPos()) //參數代入到信號類型采集函數中,實現參數的變化呈現
7、使用數據源設備DLL接口,實現單點數據采集功能,采集正弦波、方波和三角波等信號,每次采集一個數據,引入WM_TIMER消息,實現定時采集并刷新;
代碼段1:本段代碼實現了正弦波、方波和三角波的單點信號采集
代碼:
void CDemoDlg::OnTimer(UINT nIDEvent) //定時單點采集
{
if(m_flag==0) //正弦信號單點采集
{ if(m_nPointNum<100)
{
SetSignalPara(0,1, 30, 0);
double dblData[100];
ReadData(100,dblData);
m_Point[m_nPointNum].y = 160-150*dblData[m_nPointNum];
m_nPointNum++;
}
else
{
for(int k=0; k<99; k++)
{
m_Point[k].y = m_Point[k+1].y;
}
SetSignalPara(0,1, 30, 0);
double dblData[10000];
ReadData(10000,dblData);
m_Point[99].y = 160-150*dblData[m_k];
m_k++;
}
Invalidate(); // 通知刷新窗口
}
else if(m_flag==1) //方波信號單點采集
{
if(m_nPointNum<100)
{
SetSignalPara(1,1, 30, 0);
SetSWDuty(0.5);
double dblData[100];
ReadData(100,dblData);
m_Point[m_nPointNum].y = 160-150*dblData[m_nPointNum];
m_nPointNum++;
}
else
{
for(int k=0; k<99; k++)
{
m_Point[k].y = m_Point[k+1].y;
}
SetSignalPara(1,1, 30, 0);
SetSWDuty(0.5);
double dblData[10000];
ReadData(10000,dblData);
m_Point[99].y = 160-150*dblData[m_k];
m_k++;
}
Invalidate(); // 通知刷新窗口
}
else if(m_flag==2) //三角波信號單點采集
{
if(m_nPointNum<100)
{
SetSignalPara(2,1,30,0);
double dblData[10000];
ReadData(10000,dblData);
m_Point[m_nPointNum].y = 160-150*dblData[m_nPointNum];
m_nPointNum++;
}
else
{
for(int k=0; k<99; k++)
{
m_Point[k].y = m_Point[k+1].y;
}
SetSignalPara(2,1, 30, 0);
double dblData[10000];
ReadData(1000,dblData);
m_Point[99].y = 160-150*dblData[m_k];
m_k++;
}
Invalidate(); // 通知刷新窗口
}
CDialog::OnTimer(nIDEvent);
} // //單點
void CDemoDlg::OnClickedShownow() //數據開始采集
{
SetTimer(1,50,NULL);
}
void CDemoDlg::OnBnClickedShowstop() //停止采集
{
KillTimer(1);
}
dc.Polyline(m_Point,m_nPointNum); //實現單點的曲線繪制
4.3串口通信程序實現代碼說明
8、在項目中引入MSCOMM串口通信控件,設計串口控制功能模塊,包括“打開串口”、“關閉串口”、“參數設置”和串口數據接收處理模塊;
代碼段1:本段代碼實現了設置串口參數的功能
void CCommDlg::InitComm(int iComport,int bps)
{
m_Comm.SetCommPort(iComport);
m_Comm.SetInBufferSize(1024);
m_Comm.SetOutBufferSize(1024);
if(!m_Comm.GetPortOpen())
{
m_Comm.SetPortOpen(TRUE);
}
m_Comm.SetInputMode(1);
CString strparm;
strparm.Format("%d,n,8,1",bps);
m_Comm.SetSettings(strparm);
m_Comm.SetRThreshold(1);
m_Comm.SetInBufferCount(0);
}
代碼段2:本段代碼實現了串口開關的功能
void CCommDlg::OnStopreceive()
{
if(m_Comm.GetPortOpen())
{
m_Comm.SetPortOpen(FALSE);
AfxMessageBox("關閉串口成功");
}
else
{
AfxMessageBox("串口未打開 ");
}
}
void CCommDlg::OnStartreceive()
{
CCommApp* pApp = (CCommApp*)AfxGetApp();
UpdateData(TRUE);
pApp->m_nPort = atoi(m_Port);
int n = pApp->m_nPort;
int bps = atoi(m_bps);;
InitComm(n, bps);
if(m_Comm.GetPortOpen())
{
AfxMessageBox("打開串口成功");
}
else
{
AfxMessageBox("打開失敗");
}
9、根據MODBUS通信協議,實現軟件和模擬流量計的通信接口模塊,包括數據包組包、數據包解包和CRC校驗等模塊;
代碼段1:本段代碼實現了數據傳送的功能
void CCommDlg::OnSend()
{
TODO: Add your control notification handler code here
InitComm();
UpdateData(TRUE);
int Count=m_Send.GetLength();
CByteArray m_Array;
m_Array.RemoveAll();
m_Array.SetSize(Count);
for(int i=0;i<Count;i++)
m_Array.SetAt(i,m_Send);
m_Comm.SetOutput(COleVariant(m_Array));
UpdateData(FALSE);
}
代碼段2:本段代碼實現了CRC檢驗功能
WORD CRC16(BYTE *ptr, int len)
{
WORD wcrc = 0xFFFF; // 預置16位crc寄存器,初值全部為1
BYTE temp; // 定義中間變量
int i = 0, j = 0; // 定義計數
for (i = 0; i < len; i++) // 循環計算每個數據
{
temp = *ptr & 0x00FF; // 將八位數據與crc寄存器亦或
ptr++; // 指針地址增加,指向下個數據
wcrc ^= temp; // 將數據存入crc寄存器
for (j = 0; j < 8; j++) // 循環計算數據的
{
if (wcrc & 0x0001) // 判斷右移出的是不是1,如果是1則與多項式進行異或。
{
wcrc >>= 1; // 先將數據右移一位
wcrc ^= 0xA001; // 與上面的多項式進行異或
}
else // 如果不是1,則直接移出
{
wcrc >>= 1;//直接移出
}
}
}
return (unsigned int)(wcrc);
}
代碼段3:本段代碼實現了float字節轉換類型功能
static int m_nFLMode1 = 1; //浮點數字節序
float TransferFloat(BYTE *pBytes)
{
float fltRetVal = 0.00;
BYTE *p = (BYTE*)&fltRetVal;
static BYTE nIdx[4][4] = { { 0, 1, 2, 3 }, { 1, 0, 3, 2 }, { 2, 3, 0, 1 }, { 3, 2, 1, 0 } };
for (int k = 0; k < sizeof(float); k++)
{
p[nIdx[m_nFLMode1][k]] = *pBytes++;
}
return fltRetVal;
}
10、通過串口通信方式,采集模擬流量計的質量流量數據,引入WM_TIMER消息,實現定時采集并顯示質量流量參數曲線。
代碼段1:本段代碼實現了繪制網格線的功能
CGdiObject* pOldPen = pDC->SelectObject(pPenLine);
int ipontstart = 10;
rect = CRect(rect.left+ipontstart, rect.top+ipontstart,rect.right-ipontstart,rect.bottom-ipontstart);
for (i = 0; i <= max_nums_x; i++)
{
pDC->MoveTo(ipontstart + 1.0*rect.Width() /max_nums_x * i,ipontstart + rect.Height());
pDC->LineTo(ipontstart + 1.0*rect.Width() /max_nums_x * i,ipontstart);
}
for ( i = 0; i <= max_nums_y ; i++)
{
pDC->MoveTo(ipontstart ,ipontstart +1.0*rect.Height()/max_nums_y*i);
pDC->LineTo(ipontstart+ rect.Width() , ipontstart +1.0*rect.Height()/max_nums_y*i);
}
pOldPen = pDC->SelectObject(pPenBlue);
int lenwith = rect.Width()/max_nums_x;
for ( i = 1; i < m_pointnumshava; i++)
{
int lentemp = m_fIntensity[i-1]/max_ydata*rect.Height();
代碼段2:本段代碼實現了繪制流量曲線的功能
void CCommDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if (nIDEvent ==1)
{
//發送命令
if(m_Comm.GetPortOpen() ==FALSE) return;
UCHAR Sendbuf[8] = {0x01, 0x03 ,0x13, 0xBA,0x00,0x0F,0x20,0xAF};
CByteArray m_Array;//CByteArray構造一個空的字節數組
m_Array.RemoveAll();
m_Array.SetSize(sizeof(Sendbuf));
for(int i=0;i<sizeof(Sendbuf);i++)
m_Array.SetAt(i,Sendbuf);
//發送
m_Comm.SetOutput(COleVariant(m_Array));//向緩沖區寫數據流
Sleep(500);
UpdateData(TRUE);
//處理顯示原始數據
VARIANT m_input;
int count = m_Comm.GetInBufferCount();
if(count< 35) return;
if( count >= 35) count = 35;
UCHAR * porgdata;
if(count > 0)
//得到接收數據
m_input = m_Comm.GetInput();//從緩沖區取走一串字符
porgdata = (unsigned char*)m_input.parray->pvData;
CString strshouw;
m_Receive = "";
for (int i = 0; i < count ; i++)
{
strshouw.Format("%02x ",porgdata);
m_Receive +=strshouw;
}
// 顯示數據
UpdateData(FALSE);
if(porgdata[0] != 0x01) return;
if(porgdata[1] != 0x03) return;
//判斷校驗
WORD crc16 =CRC16(porgdata,33);
WORD crc162;
memcpy(&crc162,porgdata+33,2);
if(crc162 != crc16)
return;
//顯示數據
int off = 3;
float retVal1 = TransferFloat(porgdata+off); //質量流量
off+=4;
float retVal2 = TransferFloat(porgdata+off); //體積流量
off+=4;
float retVal3 = TransferFloat(porgdata+off); //密度
off+=4;
float retVal4 = TransferFloat(porgdata+off); //溫度
off+=4;
float retVal5 = TransferFloat(porgdata+off); //累積和1
off+=4;
float retVal6 = TransferFloat(porgdata+off); //累積和2
off+=4;
float retVal7 = TransferFloat(porgdata+off); //累積和3
off+=4;
WORD status =0 ;
memcpy(&status,porgdata+off,2);
CString strtemp;
strtemp.Format("%.3f",retVal1);
GetDlgItem(IDC_EDIT1)->SetWindowText(strtemp);
strtemp.Format("%.3f",retVal2);
GetDlgItem(IDC_EDIT2)->SetWindowText(strtemp);
strtemp.Format("%.3f",retVal3);
GetDlgItem(IDC_EDIT3)->SetWindowText(strtemp);
strtemp.Format("%.3f",retVal4);
GetDlgItem(IDC_EDIT4)->SetWindowText(strtemp);
strtemp.Format("%.3f",retVal5);
GetDlgItem(IDC_EDIT5)->SetWindowText(strtemp);
strtemp.Format("%.3f",retVal6);
GetDlgItem(IDC_EDIT6)->SetWindowText(strtemp);
strtemp.Format("%.3f",retVal7);
GetDlgItem(IDC_EDIT7)->SetWindowText(strtemp);
strtemp.Format("%d",status);
GetDlgItem(IDC_EDIT8)->SetWindowText(strtemp);
drawChart(retVal1);
}
CDialog::OnTimer(nIDEvent);
}
5、軟件測試
5.1圖形顯示程序測試
1、根據功能要求設計軟件界面,設計圖形顯示區域的坐標系統和坐標變換公式,要求圖形顯示區域能夠顯示不少于200點數據;
2、選用適當的繪圖工具(包括畫筆、畫刷和字體等)和繪圖函數繪制圖形顯示區域;
3、使用隨機數函數rand()產生數據,實現多點數據采集功能,每次采集一屏數據,手動刷新;
4、使用隨機數函數rand()產生數據,實現單點數據采集功能,每次采集一個數據,引入WM_TIMER消息,實現定時采集并刷新。數據點數超過數據顯示區域范圍時,曲線自動左移,實現滾動顯示;
測試結果如下:
圖5.1為初次運行未進行任何操作時軟件界面。功能區為數據采集方式設置,分別為單點采集、多點采集,以及隨機函數rand()曲線的顯示方式,分別為實線、虛線和點線式。下方為曲線顏色設置,初始參數為紅色 RGB(255,0,0)
選擇單點采集【RGB(255,255,0)】時數據采集如圖5.2所示,多點采集【RGB(255,255,0)】時數據采集如圖5.3所示,并以多點采集為例,分別改變函數曲線的顯示屬性。實線顯示【RGB(255,0,255)】圖5.3,虛線顯示【RGB(0,0,255)】圖5.4,點線顯示【RGB(255,0,0)】圖5.5,并通過改變RGB值來改變曲線顏色。
測試結論:圖形顯示程序初步實現了設計任務中的所有要求,并能方便的改變顯示數據的曲線顏色、采樣方式等屬性。
圖5-1
圖5-2
圖5-3
圖5-4
圖5-5
5.2數據采集程序測試
5、在項目中引入數據源設備DLL庫文件;
6、設計數據源設備控制模塊,包括設備打開、參數設置和設備關閉等模塊;
7、使用數據源設備DLL接口,實現單點數據采集功能,采集正弦波、方波和三角波等信號,每次采集一個數據,引入WM_TIMER消息,實現定時采集并刷新;
測試結果如下:
圖5.6為初次運行未進行任何操作時軟件界面。功能區為信號發生設置、采集設置和顯示設置。首先打開設備,如圖5.7,選擇數據采集并分別方波、正弦波和三角波信號,如圖5.8-5.10所示,可以根據采集需求切換函數曲線顏色、線型。
測試結論:程序可以實現單點采集功能,并分別采集正弦波、方波和三角波信號。并能實現刷新功能。
圖5.6
圖5.7
圖5.8
圖5.9
圖5.10
5.3串口通信程序實測試
8、在項目中引入MSCOMM串口通信控件,設計串口控制功能模塊,包括“打開串口”、“關閉串口”、“參數設置”和串口數據接收處理模塊;
9、根據MODBUS通信協議,實現軟件和模擬流量計的通信接口模塊,包括數據包組包、數據包解包和CRC校驗等模塊;
10、通過串口通信方式,采集模擬流量計的質量流量數據,引入WM_TIMER消息,實現定時采集并顯示質量流量參數曲線。
測試結果如下:
如圖5.11所示,模擬流量計界面分為參數設置、數據顯示和開
作者: missingxu 時間: 2022-4-1 08:22
Hello, MFC 控制的源工程文件方便發一下學習嗎?謝謝!
歡迎光臨 (http://www.zg4o1577.cn/bbs/) |
Powered by Discuz! X3.1 |
主站蜘蛛池模板:
久久亚洲一区二区三区四区
|
国产不卡视频
|
久久国产精品一区二区
|
日韩国产黄色片
|
色999视频|
国产精品91久久久久久
|
岛国毛片|
欧美二区在线
|
在线观看亚洲精品
|
美女视频黄的免费
|
一区二区三区免费在线观看
|
亚洲国产电影
|
欧美一区免费
|
中文字幕亚洲视频
|
欧美一级做性受免费大片免费
|
精品国产一区二区三区久久狼黑人
|
毛片视频免费观看
|
日韩一区二区福利视频
|
亚洲视频手机在线
|
www.久久.com|
中文字幕日韩欧美一区二区三区
|
精品国产一区二区三区久久久蜜月
|
亚洲免费人成在线视频观看
|
欧美一区2区三区3区公司
|
最新中文字幕在线播放
|
黄色成人免费在线观看
|
国产 日韩 欧美 中文 在线播放
|
国产在线播放一区二区三区
|
不卡一区二区在线观看
|
亚洲精品1区
|
国产一区在线看
|
在线成人免费视频
|
www.亚洲国产精品
|
亚洲精品乱码久久久久v最新版
|
久久99精品久久久久
|
欧美精品在线播放
|
中文字幕国产日韩
|
久久中文字幕一区
|
在线播放国产一区二区三区
|
国产视频久久久
|
日本一区二区三区免费观看
|