日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于MFC串口编程和曲线图绘制(visual studio2008,Teechart绘图控件)的程序总结

發(fā)布時(shí)間:2025/3/12 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于MFC串口编程和曲线图绘制(visual studio2008,Teechart绘图控件)的程序总结 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言


今年剛進(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)閉串口。

所使用變量結(jié)構(gòu)和函數(shù): typedef struct _COMMTIMEOUTS {DWORD ReadIntervalTimeout; /* Maximum time between read chars. */DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */ } COMMTIMEOUTS,*LPCOMMTIMEOUTS;//超時(shí)配置結(jié)構(gòu)typedef struct _DCB {DWORD DCBlength; /* sizeof(DCB) */DWORD BaudRate; /* Baudrate at which running */DWORD fBinary: 1; /* Binary Mode (skip EOF check) */DWORD fParity: 1; /* Enable parity checking */DWORD fOutxCtsFlow:1; /* CTS handshaking on output */DWORD fOutxDsrFlow:1; /* DSR handshaking on output */DWORD fDtrControl:2; /* DTR Flow control */DWORD fDsrSensitivity:1; /* DSR Sensitivity */DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */DWORD fOutX: 1; /* Enable output X-ON/X-OFF */DWORD fInX: 1; /* Enable input X-ON/X-OFF */DWORD fErrorChar: 1; /* Enable Err Replacement */DWORD fNull: 1; /* Enable Null stripping */DWORD fRtsControl:2; /* Rts Flow control */DWORD fAbortOnError:1; /* Abort all reads and writes on Error */DWORD fDummy2:17; /* Reserved */WORD wReserved; /* Not currently used */WORD XonLim; /* Transmit X-ON threshold */WORD XoffLim; /* Transmit X-OFF threshold */BYTE ByteSize; /* Number of bits/byte, 4-8 */BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */char XonChar; /* Tx and Rx X-ON character */char XoffChar; /* Tx and Rx X-OFF character */char ErrorChar; /* Error replacement char */char EofChar; /* End of Input character */char EvtChar; /* Received Event character */WORD wReserved1; /* Fill for now. */ } DCB, *LPDCB;//數(shù)據(jù)塊配置結(jié)構(gòu),串口參數(shù)配置typedef struct _COMSTAT {DWORD fCtsHold : 1;DWORD fDsrHold : 1;DWORD fRlsdHold : 1;DWORD fXoffHold : 1;DWORD fXoffSent : 1;DWORD fEof : 1;DWORD fTxim : 1;DWORD fReserved : 25;DWORD cbInQue;DWORD cbOutQue; } COMSTAT, *LPCOMSTAT;//包含串口信息的結(jié)構(gòu)typedef struct _OVERLAPPED {ULONG_PTR Internal;ULONG_PTR InternalHigh;union {struct {DWORD Offset;DWORD OffsetHigh;};PVOID Pointer;};HANDLE hEvent; } OVERLAPPED, *LPOVERLAPPED;/*包含了用于異步輸入輸出的信息的結(jié)構(gòu)體,只有異步通信時(shí)才使用到*/
  • 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
HANDLE CreateFile( __in LPCSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile)//打開串口函數(shù),并配置串口信息BOOL SetupComm( __in HANDLE hFile, __in DWORD dwInQueue, __in DWORD dwOutQueue)//該函數(shù)初始化一個(gè)指定的通信設(shè)備的通信參數(shù)BOOL PurgeComm( __in HANDLE hFile, __in DWORD dwFlags)//清空緩沖區(qū)BOOL SetCommTimeouts( __in HANDLE hFile, __in LPCOMMTIMEOUTS lpCommTimeouts)//windows系統(tǒng)利用此函數(shù)設(shè)定通訊設(shè)備讀寫時(shí)的超時(shí)參數(shù)BOOL GetCommState( __in HANDLE hFile, __out LPDCB lpDCB)//讀取串口設(shè)置(波特率,校驗(yàn),停止位,數(shù)據(jù)位等)BOOL SetCommState( __in HANDLE hFile, __in LPDCB lpDCB)//設(shè)置串口設(shè)置(波特率,校驗(yàn),停止位,數(shù)據(jù)位等)BOOL GetCommMask( __in HANDLE hFile, __in DWORD dwEvtMask)//設(shè)置需要監(jiān)視的串口事件HANDLE CreateEvent( __in_opt LPSECURITY_ATTRIBUTES lpEventAttributes, __in BOOL bManualReset, __in BOOL bInitialState, __in_opt LPCSTR lpName)//用來創(chuàng)建或打開一個(gè)命名的或無名的事件對(duì)象BOOL ClearCommError( __in HANDLE hFile, __out_opt LPDWORD lpErrors, __out_opt LPCOMSTAT lpStat)//Windows系統(tǒng)利用此函數(shù)清除硬件的通訊錯(cuò)誤以及獲取通訊設(shè)備的當(dāng)前狀態(tài)BOOL WaitCommEvent( __in HANDLE hFile, __inout LPDWORD lpEvtMask, __inout_opt LPOVERLAPPED lpOverlapped)//為一個(gè)特指的通信設(shè)備等待一個(gè)事件發(fā)生,該函數(shù)所監(jiān)控的事件是與該設(shè)備句柄相關(guān)聯(lián)的一系列事件DWORD GetLastError(VOID)//該函數(shù)返回調(diào)用線程最近的錯(cuò)誤代碼值,錯(cuò)誤代碼以單線程為基礎(chǔ)來維護(hù)的,多線程不重寫各自的錯(cuò)誤代碼值。用于錯(cuò)誤檢測(cè)BOOL GetOverlappedResult( __in HANDLE hFile, __in LPOVERLAPPED lpOverlapped, __out LPDWORD lpNumberOfBytesTransferred, __in BOOL bWait)//判斷一個(gè)重疊操作當(dāng)前的狀態(tài)BOOL ReadFile( __in HANDLE hFile, __out_bcount_part_opt(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer, __in DWORD nNumberOfBytesToRead, __out_opt LPDWORD lpNumberOfBytesRead, __inout_opt LPOVERLAPPED lpOverlapped)//從文件指針指向的位置開始將數(shù)據(jù)讀出到一個(gè)文件中, 且支持同步和異步操作BOOL WriteFile( __in HANDLE hFile, __in_bcount_opt(nNumberOfBytesToWrite) LPCVOID lpBuffer, __in DWORD nNumberOfBytesToWrite, __out_opt LPDWORD lpNumberOfBytesWritten, __inout_opt LPOVERLAPPED lpOverlapped)//從文件指針指向的位置開始將數(shù)據(jù)寫入到一個(gè)文件中, 且支持同步和異步操作BOOL CloseHandle( __in HANDLE hObject)//關(guān)閉一個(gè)內(nèi)核對(duì)象。其中包括文件、文件映射、進(jìn)程、線程、安全和同步對(duì)象等

個(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ù)了!

//Console1.0Dlg.h中 #include "Dlg1.h"CDlg1 page1;//Console1.0Dlg.cpp中 page1.m_Edit_ConstData = xx; page1.成員函數(shù)名(參數(shù),..);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

子窗口獲取主窗口變量和函數(shù)的使用:
在子窗口.cpp文件中加入主窗口頭文件,再直接使用GetParent()函數(shù)獲取主窗口句柄,根據(jù)該句柄就可以訪問主窗口變量和函數(shù)了!

//Dlg1.cpp中 #include "Console1.0Dlg.h"CConsole10Dlg *pDlg = (CConsole10Dlg*)GetParent()->GetParent();pDlg->CommWrite(Data); pDlg->m_bOnPaint = false;
  • 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)代碼:

//滾動(dòng)到當(dāng)前內(nèi)容 CEdit* pedit = (CEdit*)GetDlgItem(IDC_EDIT_RXDATA);pedit->LineScroll(pedit->GetLineCount());if(pedit->GetLineCount()>50){m_Edit_Rxdata = "";UpdateData(FALSE);}//用SendMessage()代替UpdateData()函數(shù)消除閃爍 GetDlgItem(IDC_EDIT_RXDATA)->SendMessage(EM_SETSEL,-1,0);GetDlgItem(IDC_EDIT_RXDATA)->SendMessage(EM_REPLACESEL,NULL,(LPARAM)(LPTSTR)(LPCTSTR)m_Edit_Rxdata);m_Edit_Rxdata.Empty();
  • 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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。