基于MFC串口编程和曲线图绘制(visual studio2008,Teechart绘图控件)的程序总结
前言
今年剛進(jìn)入公司按經(jīng)理的要求為底盤測(cè)控機(jī)寫了一個(gè)小小的console。這也是第一次教認(rèn)真的完成整個(gè)程序的編寫。程序不大,所用技術(shù)比較基礎(chǔ)也不前衛(wèi),屬于初級(jí)程序員的練手程序(知識(shí)的整理和搬運(yùn))。雖然如此,期間也由于一次選用的方案不正確而推掉重做了一次。要是有什么寫的不對(duì)的地方請(qǐng)留言賜教,謝謝。
介紹
以下是這次用到的主要知識(shí)內(nèi)容: 1)MSCOMM串口控件編程; 2)Window API函數(shù)的串口編程; 3)Teechart繪圖控件編程; 4)Windows 函數(shù)多線程編程; 5)MFC主窗口和子窗口間的值傳遞和控件控制; 6)MFC自定義消息函數(shù)和子窗口的初始化函數(shù); 7)MFC各種控件的使用:Combo box control,edit control,button control,text control,tab control,check-box control,以及兩個(gè)外插控件MScomm串口控件和Teechart繪圖控件。MScomm控件串口編程
第一次的方案選用MScomm控件實(shí)現(xiàn)串口數(shù)據(jù)的傳輸,之后改用windows API串口編程。其是全雙工,封裝性好,不需要太多的了解內(nèi)部的實(shí)現(xiàn)方式和協(xié)議的細(xì)節(jié),只需簡單的調(diào)用相應(yīng)的API就能實(shí)現(xiàn)相應(yīng)的功能。但其缺點(diǎn)是不夠靈活,而且*不支持多線程*(放棄使用的原因),要在其他主機(jī)使用該控件編寫的程序時(shí)需要注冊(cè)該控件(第三方控件都需要如此)。下面做簡單的使用總結(jié),并提及一些個(gè)人使用中在意的細(xì)節(jié)。更詳細(xì)的使用方式其他博客有很多。使用方式如下:創(chuàng)建控件并添加響應(yīng)函數(shù)和控制變量->配置串口參數(shù)并打開串口->在響應(yīng)函數(shù)中實(shí)現(xiàn)數(shù)據(jù)接收和發(fā)送的處理->關(guān)閉串口。
下載好控件并安裝。控件下載路徑
在VS工具欄中,選擇工具(T)->選擇工具選項(xiàng)(x)->com組件->選中控件并點(diǎn)確認(rèn)。
之后工具欄中就會(huì)有一個(gè)電話的圖形控件。
把該控件添加到界面視圖,并添加OnComm()響應(yīng)事件和控制變量。就可以按自己要求實(shí)現(xiàn)相應(yīng)的串口通訊功能了(初始化各屬性和參數(shù)->在OnComm()響應(yīng)函數(shù)中實(shí)現(xiàn)數(shù)據(jù)處理功能)。 在該程序中使用的函數(shù)有: put_CommPort()//選擇串口 put_RThreshold()//設(shè)置觸發(fā)OnComm事件的條件 put_InputMode()//驗(yàn)取數(shù)據(jù)方式 put_InBufferSize()//輸入緩沖區(qū)大小 put_OutBufferSize()//輸出緩沖區(qū)大小 put_Settings()//串口參數(shù)設(shè)置 put_InputLen()//設(shè)置接收區(qū)數(shù)據(jù)長度 put_PortOpen()//打開串口get_PortOpen()//獲取串口狀態(tài) get_Input()//獲取緩沖區(qū)數(shù)據(jù) get_CommEvent()//獲取串口事件OnComm()//響應(yīng)函數(shù)個(gè)人的實(shí)現(xiàn)代碼片段:
//設(shè)置-----------------------------------if(m_CtrlComm.get_PortOpen())m_CtrlComm.put_PortOpen(FALSE);m_CtrlComm.put_CommPort(COM_NUM); //選擇串口號(hào)m_CtrlComm.put_RThreshold(2); //收到兩個(gè)字節(jié)引發(fā)OnComm事件m_CtrlComm.put_InputMode(1); //輸入模式選為二進(jìn)制m_CtrlComm.put_InBufferSize(1024); //設(shè)置輸入緩沖區(qū)的大小,Bytesm_CtrlComm.put_OutBufferSize(1024); //設(shè)置輸出緩沖區(qū)的大小,Bytesm_CtrlComm.put_Settings(SerialPortMessage); //設(shè)置串口參數(shù),波特率,奇偶校驗(yàn),數(shù)據(jù)位,停止位m_CtrlComm.put_InputMode(1); //以二進(jìn)制方法驗(yàn)取數(shù)據(jù) // m_CtrlComm.put_RThreshold(1); //每當(dāng)串口接收緩沖區(qū)中有多于或等于1個(gè)字符時(shí)將引發(fā)一個(gè)接收數(shù)據(jù)的OnComm事件m_CtrlComm.put_InputLen(0); //設(shè)置當(dāng)前接收區(qū)數(shù)據(jù)長度為0 // m_ctrlComm.put_PortOpen(TRUE); //打開串口if(!m_CtrlComm.get_PortOpen())m_CtrlComm.put_PortOpen(TRUE);elseAfxMessageBox("Can not open serial port!");m_CtrlComm.get_Input();//事件處理------------------------------- void Ccomm12Dlg::OnCommMscomm1() {// TODO: 在此處添加消息處理程序代碼VARIANT variant_inp;COleSafeArray safearray_inp;long len,k;BYTE rxdata[2048];BYTE bt = NULL;if(m_CtrlComm.get_CommEvent() == 2) //事件值為2表示接收緩沖區(qū)內(nèi)有字符{//以下你可以根據(jù)自己的通信協(xié)議加入處理代碼UpdateData(TRUE);variant_inp = m_CtrlComm.get_Input(); //讀緩沖區(qū)safearray_inp = variant_inp; //VARIANT型變量轉(zhuǎn)換為ColeSafeArray型變量len = safearray_inp.GetOneDimSize(); //得到有效數(shù)據(jù)長度for(k=0;k<len;k++)safearray_inp.GetElement(&k,rxdata+k); //轉(zhuǎn)換為BYTE型數(shù)組for(k=0;k<len;k++) //將數(shù)組轉(zhuǎn)換為Cstring型變量{bt = *(char*)(rxdata+k); //字符型//strtemp_recive_data.Format("%X",bt); //將字符送入臨時(shí)變量strtemp存放 m_EditRxData+=bt; //加入接收編輯框?qū)?yīng)字符串 strtemp_recive_data+=bt;}............ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
注意事項(xiàng):控件在安裝后有些可能需要注冊(cè)才能正常使用控件。在接收的數(shù)據(jù)中,因?yàn)槠浣邮盏臄?shù)據(jù)類型是VARIANT,需要將其轉(zhuǎn)為ColeSafeArray類型->BYTE類型。特別的,在數(shù)據(jù)需要以十六進(jìn)制輸出去時(shí)要用到CByteArray數(shù)據(jù)類型!這里給出實(shí)現(xiàn)代碼:
// 去除空格并壓縮數(shù)據(jù) int Ccomm12Dlg::String2Hex(CString str, CByteArray& senddata) {int hexdata,lowhexdata;int hexdatalen=0;int len=str.GetLength();senddata.SetSize(len/2);for(int i=0;i<len;){char lstr,hstr=str[i];if(hstr==' '){i++;continue;}i++;if(i>=len)break;lstr=str[i];hexdata=ConvertHexChar(hstr);lowhexdata=ConvertHexChar(lstr);if((hexdata==16)||(lowhexdata==16))break;elsehexdata=hexdata*16+lowhexdata;i++;senddata[hexdatalen]=(char)hexdata;hexdatalen++;}senddata.SetSize(hexdatalen);return hexdatalen; }// 將一個(gè)字符轉(zhuǎn)換為相應(yīng)的十六進(jìn)制 char Ccomm12Dlg::ConvertHexChar(char ch) {if((ch>='0')&&(ch<='9'))return ch-0x30;else if((ch>='A')&&(ch<='F'))return ch-'A'+10;else if((ch>='a')&&(ch<='f'))return ch-'a'+10;else return ch; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
Windows API 串口編程
在放棄MScomm串口控件后,使用API串口編程方式。該方式雖然相比MScomm控件函數(shù)實(shí)現(xiàn)方式比較復(fù)雜一點(diǎn),但其實(shí)現(xiàn)功能更加的靈活,功能更加完善,在其他各戶機(jī)運(yùn)行編程的程序也方便,重要的是它支持多線程,這就使其數(shù)據(jù)處理的效率要高,不需要擔(dān)心因?yàn)槭录亩氯绊懫渌δ艿牟僮骰蚱渌麛?shù)據(jù)的處理。Windows API串口編程主要有*同步通信方式*及*異步通信方式*兩種。考慮到多線程方式的實(shí)現(xiàn),當(dāng)然選著使用異步通信方式。實(shí)現(xiàn)方法:
打開串口->配置串口(設(shè)置驅(qū)動(dòng)類型、配置超時(shí)結(jié)構(gòu)參數(shù)、配置數(shù)據(jù)塊結(jié)構(gòu)參數(shù))->創(chuàng)建監(jiān)聽函數(shù)->使用數(shù)據(jù)讀取、數(shù)據(jù)寫入函數(shù)->關(guān)閉串口。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
個(gè)人的代碼實(shí)現(xiàn)片段:
//-------------------打開串口并設(shè)置串口 void CConsole10Dlg::OnBnClickedButtonPortopen() {// TODO: 在此添加控件通知處理程序代碼.................hCom = CreateFile(strSerialPortText,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING, //設(shè)置產(chǎn)生方式FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL, //異步通信NULL);if(hCom == INVALID_HANDLE_VALUE) //檢測(cè)打開串口操作是否成功{AfxMessageBox("串口打開失敗!");} //SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY); //設(shè)置事件驅(qū)動(dòng)的類型SetupComm(hCom,2048,2048); //設(shè)置輸入、輸出緩沖區(qū)的大小PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); //清干凈輸入、輸出緩沖區(qū)COMMTIMEOUTS CommTimeOuts; //定義超時(shí)結(jié)構(gòu),并填寫該結(jié)構(gòu),總超時(shí)=時(shí)間系數(shù)*要求讀/寫的字符數(shù)+時(shí)間常量CommTimeOuts.ReadIntervalTimeout = 10; //讀間隔超時(shí),無延時(shí)因?yàn)橛蠾aitCommEvent等待數(shù)據(jù)CommTimeOuts.ReadTotalTimeoutMultiplier = 0; //讀時(shí)間系數(shù)CommTimeOuts.ReadTotalTimeoutConstant = 0; //讀時(shí)間常數(shù)CommTimeOuts.WriteTotalTimeoutMultiplier = 50; //寫時(shí)間系數(shù)CommTimeOuts.WriteTotalTimeoutConstant = 50; //寫時(shí)間常量 SetCommTimeouts(hCom,&CommTimeOuts); //設(shè)置讀寫操作所允許的超時(shí)DCB dcb; //定義數(shù)據(jù)控制塊結(jié)構(gòu)GetCommState(hCom,&dcb); //讀串口原來的參數(shù)設(shè)置dcb.BaudRate = atol(strBaudRateText); //波特率dcb.ByteSize = atoi(strDataBitsText); //數(shù)據(jù)位dcb.fParity = TRUE;BYTE wCheck;if(strCheckDigitText == "奇校驗(yàn)")wCheck = ODDPARITY;else if(strCheckDigitText == "偶校驗(yàn)")wCheck = EVENPARITY;else if(strCheckDigitText == "無校驗(yàn)")wCheck = NOPARITY;dcb.Parity = wCheck; //校驗(yàn)位BYTE wStop;if(strStopBitText = "1")wStop = ONESTOPBIT;else if(strStopBitText = "1.5")wStop = ONE5STOPBITS;else if(strStopBitText = "2")wStop = TWOSTOPBITS;dcb.StopBits = wStop; //停止位dcb.fBinary = TRUE;SetCommState(hCom,&dcb); //串口參數(shù)配置if(!SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY))//設(shè)置接收字符和輸出緩沖串口事件需要監(jiān)視AfxMessageBox("SetCommMask failed with error!");//創(chuàng)建事件對(duì)象m_ovRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);m_ovWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);m_ovWait.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);if(!(m_Thread =AfxBeginThread((AFX_THREADPROC)CommWatchProc,this)))//創(chuàng)建監(jiān)聽線程AfxMessageBox("創(chuàng)建監(jiān)聽線程失敗!");......... }............//-------------------------------監(jiān)聽線程,實(shí)現(xiàn)數(shù)據(jù)的讀 UINT CommWatchProc(LPVOID pParam) {CConsole10Dlg *obj = (CConsole10Dlg*)pParam;COMSTAT ComStat = {0};DWORD WaitEvent = 0,Bytes = 0;BOOL Status = FALSE;DWORD Error = 0;BYTE lpBuffer[2048];if(hCom != NULL)ClearCommError(hCom,&Error,&ComStat);while(obj->m_IsOpen){Status = WaitCommEvent(hCom,&WaitEvent,&obj->m_ovRead);if(FALSE == Status&&GetLastError() == ERROR_IO_PENDING)Status = GetOverlappedResult(hCom,&obj->m_ovRead,&Bytes,TRUE); //查詢,等待重疊操作結(jié)束時(shí)返回ClearCommError(hCom,&Error,&ComStat);if(Status == TRUE//等待事件成功 && WaitEvent&EV_RXCHAR//緩存中有數(shù)據(jù)到達(dá) && ComStat.cbInQue > 0)//有數(shù)據(jù){memset(lpBuffer,0,sizeof(lpBuffer));Status = ReadFile(hCom,lpBuffer,sizeof(lpBuffer),&Bytes,&obj->m_ovRead);GetOverlappedResult(hCom,&obj->m_ovRead,&Bytes,TRUE);for(DWORD k = 0;k<Bytes;k++){obj->m_Edit_Rxdata += lpBuffer[k];.........}...........PurgeComm(hCom, PURGE_RXCLEAR|PURGE_RXABORT);}}return 0; }...........//-------------------------實(shí)現(xiàn)數(shù)據(jù)的寫入 BOOL CConsole10Dlg::CommWrite(CString wBuffer) {CByteArray test;int len = String2Hex(wBuffer,test);//轉(zhuǎn)換字符性BYTE temp[1024];for(int i = 0;i<len;i++)temp[i] = test.GetAt(i);LPBYTE buffer = (BYTE *)temp;BOOL rtn = FALSE;DWORD WriteSize = 0;PurgeComm(hCom,PURGE_TXCLEAR|PURGE_TXABORT);rtn = WriteFile(hCom,buffer,len,&WriteSize,&m_ovWrite);len = 0;if(FALSE == rtn && GetLastError() == ERROR_IO_PENDING)//后臺(tái)讀取{//等待數(shù)據(jù)寫入完成if(FALSE == GetOverlappedResult(hCom,&m_ovWrite,&WriteSize,TRUE))return FALSE;}len = WriteSize;return rtn; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
注意事項(xiàng):就如前面所說,Windows API的串口編程比MScomm控件的串口編程要復(fù)雜一點(diǎn)。在創(chuàng)建串口時(shí),特別注意CreateFile()中dwFlagsAndAttributes參數(shù)設(shè)置為異步通信:FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL。之后也要注意檢查DCB結(jié)構(gòu)參數(shù)的設(shè)置。稍難實(shí)現(xiàn)的邏輯部分是,監(jiān)聽函數(shù)中的數(shù)據(jù)讀取(檢測(cè)到有數(shù)據(jù)到達(dá)后,需進(jìn)行狀態(tài)檢測(cè)、錯(cuò)誤判斷之后才能正確讀取數(shù)據(jù)),這可以寫的簡單也可以寫的復(fù)雜但健壯一些(主要是錯(cuò)誤信息的處理方式),由于使用異步通信,ReadFile()時(shí)會(huì)返回ERROR_IO_PENDING錯(cuò)誤,需要用GetOverlappedResult()阻塞查詢狀態(tài)等待重疊操作的結(jié)束。在數(shù)據(jù)讀取時(shí)再次用GetOverlappedResult()阻塞等待數(shù)據(jù)的讀取完成。
Teechart繪圖控件編程
Teechart屬于第三方控件,同樣需要下載并注冊(cè)再添加進(jìn)VS的工具箱才能使用。在其他電腦運(yùn)行編寫的程序也需要再次注冊(cè)才能正常打開。其封裝性好,比起MFC自帶的繪圖功能要簡單好用。繪圖功能比較豐富,在這里我只是用其2D曲線繪圖功能。
使用方式:
下載安裝并注冊(cè)好->把控件添加進(jìn)vs工具箱->添加控件設(shè)置好屬性->按需要實(shí)現(xiàn)功能。
控件下載連接
工具欄中:工具(T)->選擇工具箱項(xiàng)(X)->COM控件中選中Teechart Pro Activex control v5點(diǎn)確定
之后工具箱中出現(xiàn)Teechart控件圖標(biāo),拖入到程序窗口并添加控件變量實(shí)現(xiàn)所需功能。
這里還需添加Teechart的類和控制變量才能實(shí)現(xiàn)相應(yīng)的功能:
在”類視圖“中右鍵->添加類->選中“Typelib中的MFC類”點(diǎn)添加
可用類庫中選擇TeeChart Pro Activex Control v5<1.0>,選擇需要的接口,(這里只需圖中選擇的類)并點(diǎn)完成。之后“類視圖”中就有了相應(yīng)的類,“解決方案資源管理器”中也有了相應(yīng)的頭文件。將需要用到Teechart控件函數(shù)的.ccp文件中添加這些頭文件就可以使用其封裝函數(shù)了。
我的程序里,創(chuàng)建了兩個(gè)Teechart控件,都是實(shí)現(xiàn)實(shí)時(shí)曲線的繪制,圖像在任意點(diǎn)能放大。
其中一個(gè)的配置要求是:Y軸固定顯示范圍,X軸顯示25個(gè)單元數(shù)并隨曲線向左移動(dòng);
另一個(gè)的配置要求是:Y軸固定顯示范圍,X軸隨曲線自動(dòng)變化并保留整個(gè)X軸數(shù)據(jù)。
個(gè)人的實(shí)現(xiàn)代碼片段:
//第一個(gè)圖的繪制函數(shù).......................... //清空之前的繪圖曲線 for(long i = 0;i<m_CtrlTchart1.get_SeriesCount();i++){CSeries serDemo = (CSeries)m_CtrlTchart1.Series(i);serDemo.Clear();}//重繪X軸(只能代碼實(shí)現(xiàn)),屬性頁面只能配置第一次繪圖時(shí)的屬性CAxes chartaxis = (CAxes)m_CtrlTchart1.get_Axis();CAxis chartaxisbottem = (CAxis)chartaxis.get_Bottom();chartaxisbottem.SetMinMax(0,25);//-------------if(!pDlg->CommWrite(ConstData))if(!(OnPaint_Thread =AfxBeginThread((AFX_THREADPROC)OnPaintTchart,pDlg)))//創(chuàng)建繪圖線程AfxMessageBox("創(chuàng)建繪圖線程失敗!");............... }UINT OnPaintTchart(LPVOID pParam) {CConsole10Dlg *pDlg = (CConsole10Dlg*)pParam;pDlg->m_OnPainData.Empty();//開始時(shí)清空繪圖前的數(shù)據(jù)while(pDlg->m_bOnPaint && !pDlg->m_IsTimeTest){.........................//---------------------繪圖函數(shù)是以下幾行代碼CSeries serDemo = (CSeries)pDlg->page1.m_CtrlTchart1.Series(0);serDemo.AddXY(X,Y,strtempX,0);//實(shí)現(xiàn)X軸的左移CAxes chartaxis = (CAxes)pDlg->page1.m_CtrlTchart1.get_Axis();CAxis chartaxisbottem = (CAxis)chartaxis.get_Bottom();chartaxisbottem.Scroll(1.0,TRUE);//---------------------------------其余都是數(shù)據(jù)的處理.................return 0; }//------------第二個(gè)圖的繪制函數(shù) ..............CSeries serDmo = (CSeries)m_CtrlTchart2.Series(0);serDmo.Clear();CAxes chartaxis = (CAxes)m_CtrlTchart2.get_Axis();CAxis chartaxisbottem = (CAxis)chartaxis.get_Bottom();chartaxisbottem.put_Automatic(TRUE);....CString Data = ......if(!(m_TimeThread = AfxBeginThread((AFX_THREADPROC)OnPaintTimeFunction,pDlg)))AfxMessageBox("繪圖線程創(chuàng)建失敗!");pDlg->m_IsTimeTest = true;............. }UINT OnPaintTimeFunction(LPVOID pParam) {CConsole10Dlg *pDlg = (CConsole10Dlg*)pParam;pDlg->m_OnPainData.Empty();//開始時(shí)清空繪圖前的數(shù)據(jù)while((!pDlg->m_bOnPaint)&&pDlg->m_IsTimeTest){................. //----------------------------------圖像的繪制是以下兩行代碼CSeries serDemo = (CSeries)pDlg->page2.m_CtrlTchart2.Series(0);serDemo.AddXY(X,Y,strtempX,0); //----------------------------------其余都是接收數(shù)據(jù)的處理......................return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
注意事項(xiàng):步驟中沒有給出控件控制變量的添加。在曲線的二次繪制時(shí)需要清除之前繪制的圖像,軸的屬性設(shè)置也需要代碼重新設(shè)置一次。其實(shí)現(xiàn)還是很簡單的。
Windows函數(shù)的多線程編程
多線程的實(shí)現(xiàn)還是有好幾種函數(shù)方式的(CreateThread(),_BeginThread(),_beginThreadex()),而在MFC中有兩種線程:用戶界面線程和輔助線程(AfxBeginThread())。 用戶界面線程通常用于處理用戶輸入及響應(yīng)用戶生成的事件和消息。 輔助線程通常用于完成不需要用戶輸入的任務(wù)(如重新計(jì)算)。 Win32 API 不區(qū)分線程類型;它只需要了解線程的起始地址以開始執(zhí)行線程。 MFC 為用戶界面中的事件提供消息泵,從而對(duì)用戶界面線程進(jìn)行專門處理。線程還涉及線程間的數(shù)據(jù)傳輸、同步、安全,線程優(yōu)先級(jí),線程狀態(tài),互斥和臨界區(qū),線程池等。線程的編程和調(diào)試是BUG的常發(fā)區(qū)。我在此用輔助線程和使用了一個(gè)互斥對(duì)象確保數(shù)據(jù)的安全和同步問題。
使用的類和函數(shù)有: class CMutex : public CSyncObject {DECLARE_DYNAMIC(CMutex)// Constructor public:/* explicit */ CMutex(BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL,LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);// Implementation public:virtual ~CMutex();BOOL Unlock(); };//互斥對(duì)象BOOL Lock(DWORD dwTimeout)//鎖函數(shù)BOOL Unlock(void)//解鎖函數(shù)HANDLE AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam,UINT nStackSize = 0,DWORD dwCreateFlags = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL)/*創(chuàng)建線程函數(shù),比CreateThread()安全*/UINT CommWatchProc(LPVOID pParam);//線程函數(shù),遵循UINT FunctionName(LPVOID pParam)形式UINT OnPaintTchart(LPVOID pParam);UINT OnPaintTimeFunction(LPVOID pParam);CloseHandle(Handle)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
個(gè)人的代碼片段:
//聲明全局函數(shù)(要在類外,線程函數(shù)不能屬于類)UINT CommWatchProc(LPVOID pParam);UINT OnPaintTchart(LPVOID pParam);UINT OnPaintTimeFunction(LPVOID pParam);//生成全局互斥類對(duì)象 CMutex hmutex(NULL,FALSE,NULL); //全局互斥對(duì)象//創(chuàng)建線程函數(shù),并傳入窗口類的this指針參數(shù),方便窗口類的變量和函數(shù)的使用 m_Thread =AfxBeginThread((AFX_THREADPROC)CommWatchProc,this)//創(chuàng)建監(jiān)聽線程UINT CommWatchProc(LPVOID pParam) {CConsole10Dlg *obj = (CConsole10Dlg*)pParam;功能.....hmutex.Lock(INFINITE);//鎖定互斥對(duì)象*要保護(hù)數(shù)據(jù)*所在代碼段hmutex.Unlock();//釋放互斥對(duì)象return 0; }//關(guān)閉線程if(NULL != m_Thread){WaitForSingleObject(m_Thread,1000);//等待線程結(jié)束CloseHandle(m_Thread);m_Thread = NULL;}//其余兩個(gè)線程類似- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
注意事項(xiàng):線程創(chuàng)建運(yùn)行完后要記得銷毀,數(shù)據(jù)要初始化防止不必要的內(nèi)存泄露。線程中要盡量少用互斥和臨界區(qū)或使用較高效率的臨界函數(shù),提高運(yùn)行效率。
MFC主窗口和子窗口之間的變量傳遞和控件使用
在編程時(shí),遇到兩個(gè)窗口間的通信問題。其實(shí)也是兩個(gè)類之間的變量和函數(shù)的使用問題。要解決也很簡單:
主窗口類:CConsole10Dlg(有變量(pubilc):bool m_bOnPaint;
函數(shù):void CommWrite(CString Data);
子窗口類:CDlg1(有變量(public):int m_Edit_ConstData;
主窗口獲取子窗口變量和函數(shù)的使用:
在主窗口類的頭文件(Console1.0Dlg.h)中添加子窗口頭文件,并定義子窗口類對(duì)象。通過該對(duì)象就可以調(diào)用子類窗口的變量和函數(shù)了!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
子窗口獲取主窗口變量和函數(shù)的使用:
在子窗口.cpp文件中加入主窗口頭文件,再直接使用GetParent()函數(shù)獲取主窗口句柄,根據(jù)該句柄就可以訪問主窗口變量和函數(shù)了!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
注意事項(xiàng):頭文件的包含不能相互包含
MFC自定義消息函數(shù)和子窗口的初始化函數(shù)
自定義消息函數(shù)要記住宏”WM_USER”,該宏是Windows定義的宏,該宏值以內(nèi)的消息變量都是系統(tǒng)自定義的消息變量,為了自定義消息變量不與系統(tǒng)消息變量發(fā)生沖突,只需在該宏上加一個(gè)正數(shù)值就行,之后要自己定義消息函數(shù),并手動(dòng)添加函數(shù)映射:
//.h文件中 #define WM_MESSAGE WM_USER + 100 //自定義消息IDafx_msg LRESULT OnChangeEdit(WPARAM wParam, LPARAM lParam);//自定義消息函數(shù)//.cpp文件中 BEGIN_MESSAGE_MAP(CConsole10Dlg, CDialog) ... ON_MESSAGE(WM_MESSAGE, &CConsole10Dlg::OnChangeEdit)//添加消息映射 ... END_MESSAGE_MAP()//在需要觸發(fā)自定義消息函數(shù)的地方調(diào)用PostMessage()函數(shù),實(shí)現(xiàn)自定義消息函數(shù)的執(zhí)行PostMessage(obj->m_hWnd,WM_MESSAGE,0,0);//函數(shù)的實(shí)現(xiàn) LRESULT CConsole10Dlg::OnChangeEdit(WPARAM wParam, LPARAM lParam) {所需功能return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
再來,是子窗口的初始化函數(shù)的編寫。創(chuàng)建的子窗口是沒有初始化函數(shù)的,不過初始化函數(shù)是虛函數(shù),子窗口類和主窗口類同是CDialog的派生類。因此,只需將主窗口的初始化函數(shù)復(fù)制到子窗口的實(shí)現(xiàn)文件里(.cpp)就行了!
BOOL CDlg1::OnInitDialog() { CDialog::OnInitDialog();初始化功能return TRUE; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
注意事項(xiàng):子窗口的初始化函數(shù)中要將類名改為所在類的類名。
MFC各種控件的使用
控件的使用挑選在編寫中比較在意的知識(shí)點(diǎn):
關(guān)于Edit control 的內(nèi)容顯示,一是讓滾動(dòng)條停在實(shí)時(shí)內(nèi)容上,二是消除顯示時(shí)的閃爍問題。另外,該控件定義的CString 變量是有存儲(chǔ)容量的,要記得消除過多的內(nèi)容。有以下實(shí)現(xiàn)代碼:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
關(guān)于Static control 控件的內(nèi)容動(dòng)態(tài)顯示:
this->GetDlgItem(IDC_STATIC_1)->SetWindowTextA(m_Static1);- 1
總結(jié)
內(nèi)容積累的太多,沒寫細(xì),后面的內(nèi)容也沒認(rèn)真整理。程序的要求功能基本完成。自己寫完后回去看,還真不乍樣,第一次寫,就這樣吧。之后要出差一段時(shí)間,隔太久來寫,記憶的效率就打折扣了。
總結(jié)
以上是生活随笔為你收集整理的基于MFC串口编程和曲线图绘制(visual studio2008,Teechart绘图控件)的程序总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ST_Curve --- 一个专业的曲线
- 下一篇: DOM 节点类型及属性