<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          關(guān) 閉

          新聞中心

          EEPW首頁(yè) > 工控自動(dòng)化 > 設(shè)計(jì)應(yīng)用 > 用WindowsAPI設(shè)計(jì)多線程的串行通信ActiveX控件

          用WindowsAPI設(shè)計(jì)多線程的串行通信ActiveX控件

          作者: 時(shí)間:2016-12-22 來(lái)源:網(wǎng)絡(luò) 收藏

          串行通信ActiveX控件的設(shè)計(jì)方法,并給出主要的通信程序代碼。

          本文引用地址:http://www.ex-cimer.com/article/201612/332845.htm

          串行通信 多線程

          信是計(jì)算機(jī)之間及計(jì)算機(jī)及數(shù)字化儀器和設(shè)備的一種重要通信手段,是實(shí)現(xiàn)工業(yè)監(jiān)控的一種主要方式。Windows下的串行通信主要有兩種方法:利用VB的MSCOMM控件和利用Windows API。MSCOMM控件簡(jiǎn)單易用,但由于其對(duì)串口設(shè)備封裝及調(diào)用方式的局限性,不能靈活方便地對(duì)串口設(shè)備進(jìn)行控制。而通過(guò)Windows API則可以實(shí)現(xiàn)對(duì)串口設(shè)備的完全控制,并且可以提供多線程的通信機(jī)制。

          在復(fù)雜應(yīng)用中,通信通常在后臺(tái)完成,需要采用多線程技術(shù)。一個(gè)多線程的應(yīng)用程序?qū)嶋H上是在其內(nèi)部實(shí)現(xiàn)了多任務(wù)擴(kuò)展,為代碼賦予了并行執(zhí)行的特性,適于執(zhí)行一些實(shí)時(shí)性或隨機(jī)性很強(qiáng)的操作,也有利于提高CPU的利用率,加快通信程序的信息處理速度。

          本文以一臺(tái)工業(yè)控制PC機(jī)與多臺(tái)基于單片機(jī)的智能控制單片進(jìn)行串行通信為實(shí)例。PC機(jī)和各智能控制單元通過(guò)RS485總線互聯(lián)。由于RS485的通信方式是半雙工的,只能由作為主節(jié)點(diǎn)的PC機(jī)依次輪詢網(wǎng)絡(luò)上的各智能控制單元子節(jié)點(diǎn)。每次通信都是由PC機(jī)通過(guò)串口向智能控制單元發(fā)布命令,智能控制單元在接收到正確的命令后做出應(yīng)答。

          系統(tǒng)的主節(jié)點(diǎn)應(yīng)用程序是用VB6.0編寫的,為了既能提供多線程的串行通信機(jī)制,又可使應(yīng)用程序易于實(shí)現(xiàn)串行通信功能,利用VC++6.0開發(fā)基于Window API的多線程串行通信ActiveX控件。主節(jié)點(diǎn)的應(yīng)用程序通過(guò)對(duì)串行通信ActiveX控件的調(diào)用完成與各子節(jié)點(diǎn)的通信。

          1 創(chuàng)建ActiveX控件JinRiComm.OCX

          VGC++6.0和MFC是健建ActiveX控件的強(qiáng)大而又靈活的工具。JinRiComm控件創(chuàng)建步驟簡(jiǎn)單概述如下:

          (1)用MFC ActiveX ControlWizard生成ActiveX控件工程,命名為JinRiComm。

          (2)打開ClassWizard窗口,選擇Automation標(biāo)簽,單擊“Add Property”按鈕,命名新的屬性。單擊“AddMethod”按鈕,命名新的方法。選擇ActiveX Event標(biāo)簽,單擊“Add Event”按鈕,命名新的事件。

          (3)向控件工程中添加類CserialPort,為該類添加成員變量和成員函數(shù),該類將完成串行通信工作。

          2 串口通信的基本編程

          用Windows API函數(shù)實(shí)現(xiàn)串行通信,其特點(diǎn)是對(duì)串口的操作如對(duì)文件操作一樣,打開和關(guān)閉串行設(shè)備與打開和關(guān)閉文件使用相同的函數(shù)。

          (1)打開串口

          m_hComm=CreateFile(szPort,GENERIC_READ |GENERIC_WRITE,0,NULL, OPEN_EXISTING,F(xiàn)ILE_FLAG_OVERLAPPED,0);

          (2)獲取當(dāng)前通信信息,設(shè)備通信設(shè)備

          GetCommState(m_hComm,&m_dcb);

          SetCommState(m_hComm,&m_dcb);

          (3)讀、寫串口

          bResult=ReadFile(port->m_hComm,&RXBuff,1,&BytesRead,Port->m_ov);

          bResult=WriteFile(port->m_hComm,&(port-m_Byte)[i],1,&BytesSent,&port->m_ov);

          (4)關(guān)閉串口

          CloseHandle (m_hComm);

          3 設(shè)計(jì)程序中的線程

          MFC執(zhí)行兩種類型的線程:用戶界面線程和工作線程。前者用來(lái)處理用戶輸入,響應(yīng)由用戶產(chǎn)生的事件和消息。后者不處理窗口消息,用于完成后臺(tái)計(jì)算、打印和其它一些沒(méi)有必要強(qiáng)迫用戶來(lái)等待的任務(wù)。在本程序中,用戶界面線程就是程序的主線程,另外再添加兩個(gè)工作線程:通信線程和延時(shí)線程。它們的功能介紹如表1所示。

          表1

          線程名稱主要功能線程函數(shù)
          主線程響應(yīng)用戶對(duì)控件的調(diào)用(設(shè)置控件屬性和調(diào)用控制方法);初始化串口;處理通信線程接收到的數(shù)據(jù),并通知用戶(觸發(fā)控件消息);通知通信線程向串口寫數(shù)據(jù)。 
          通信線程在主線程初始化串口后被創(chuàng)建。CommThread函數(shù)進(jìn)入死循環(huán),線程一直監(jiān)視串口事件,當(dāng)讀串口事件發(fā)生,讀取串口接收到的數(shù)據(jù),向主線程發(fā)自定義消息WM_COMM_RXCHAR,通知主線程處理數(shù)據(jù);收到主線程的寫串口命令時(shí),將緩存中的數(shù)據(jù)寫到串口。CommThread(LPVOID pParam)
          延時(shí)線程在主線程向串口寫數(shù)據(jù)之后被創(chuàng)建。如果主線置為真,延時(shí)線程在檢查到其為真后,線程函數(shù)返回;否則會(huì)延時(shí)10ms再判斷一次。如果超過(guò)八規(guī)定時(shí)間仍沒(méi)檢查到其為真,則向主線程發(fā)自定義消息WM_DELAY_TIMEOUT,通知主線程重發(fā)剛才的命令,然后線程函數(shù)返回。DelayThread(LPVOID pParam)

          應(yīng)用AfxBeginThread函數(shù)來(lái)啟動(dòng)一個(gè)工作線程,用法如下:

          CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam,int nPriority=THREAD_PRIORITY_NORMAL,UINT nStackSize=0,DWORD dwCreateFlage=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL)

          在啟動(dòng)一個(gè)工作線程之前,必須為線程編寫一個(gè)全局的線程函數(shù)。這個(gè)線程函數(shù)接受一個(gè)32位的LPVOID作為參數(shù),返回一個(gè)UINT,線程函數(shù)的結(jié)構(gòu)為:

          UINT ThreadUFunction(LPVOID pParam)

          {

          //線程處理代碼

          return 0;

          }

          終止線程有兩種途徑:當(dāng)線程函數(shù)返回時(shí),線程終止;線程函數(shù)也可以在內(nèi)部調(diào)用AfxEndThread函數(shù)來(lái)終止自己。

          程序流程圖如圖1所示。

          4 線程間的通信

          (1)通過(guò)全局變量

          主線程可以采用多種方式與工作線程進(jìn)行通信,最簡(jiǎn)單的辦法是通過(guò)全局變量,因?yàn)檫M(jìn)程中的所有線程都可以訪問(wèn)所有的全局變量。如:定義全局變量bReceiveSuccess,它表示是否收到了正確的響應(yīng)。在主線程向串口寫數(shù)據(jù)之后它被置為FALSE,然后延時(shí)線程啟動(dòng)。當(dāng)系統(tǒng)收到正確的響應(yīng)后,bReceiveSuccess被主線程改為TRUE。延時(shí)線程根據(jù)bReceiveSuccess的值來(lái)決定是結(jié)束該線程還是給主線程發(fā)消息。

          (2)通過(guò)參數(shù)

          主線程可以向工作線程傳遞一個(gè)4字節(jié)的參數(shù),一種使用該參數(shù)的常見方式是傳遞一個(gè)指針,它指向這個(gè)線程的父類。如:

          UINT CserialPort::CommThread(LPVOID pParam)

          {

          CserialPort port=(CSerialPort)pParam; //取得串口類指針

          //線程處理代碼

          }

          (3)通過(guò)消息

          工作線程獲得主線程的窗口句柄,則可以給主線程發(fā)送消息。如:

          通信線程通知主線程,串口接收到了數(shù)據(jù)

          ::PostMessage ((port ->m_pOwner) ->m_hWnd,WM_COMM_RXCHAR,(WPARAM) RXBuff,(LPARAM)port->m_nPortNr);

          5 線程的同步

          多線程的優(yōu)點(diǎn)之一是所有線程都可以訪問(wèn)相同的全局對(duì)象和共享資源,它提供了程序設(shè)計(jì)的簡(jiǎn)捷性和便利性,提高了對(duì)信息處理的并發(fā)度。但如果不妥善處理好線程的并發(fā)問(wèn)題,也會(huì)帶來(lái)數(shù)據(jù)的錯(cuò)誤或是資源的死鎖。為了避免這些問(wèn)題發(fā)生,線程在使用共享資源或?qū)ο笄氨仨毇@得一個(gè)約束訪問(wèn)同步對(duì)象的權(quán)力,也就是通過(guò)同步的機(jī)制來(lái)控制這種權(quán)力的使用。線程間的同步多種方法。

          (1)臨界區(qū)

          臨界區(qū)是通過(guò)多個(gè)線程的串行化來(lái)訪問(wèn)公共資源或一段代碼。如:

          InitializeCriticalSection(&(port->Lm_csCommunication Sync));

          //初始化臨界區(qū)對(duì)象

          EnterCriticalSection(&prot->m_csCommunicationSyne);

          //使調(diào)用線程等待得臨界區(qū)對(duì)象并在獲得擁有權(quán)時(shí)返回

          Do

          {

          if(!bReceiveSuccess) //訪問(wèn)全局變量

          {

          LeaveCriticalSection(&port->m_csCommunicationSync);

          //釋放對(duì)臨界區(qū)對(duì)象的擁有權(quán)

          //其它處理代碼

          }

          }

          (2)事件

          事件用來(lái)通知線程有一些事件已經(jīng)發(fā)生,比較適合于信號(hào)控制。事件有手動(dòng)復(fù)位和自動(dòng)復(fù)位兩種。手動(dòng)復(fù)位事件是在應(yīng)用程序或系統(tǒng)后臺(tái)控制不改變它的信號(hào)狀態(tài)。當(dāng)手動(dòng)復(fù)位事件處于有信號(hào)狀態(tài)時(shí),所有等待該事件的線程都被激活,事件保留有信號(hào)狀態(tài)直到被一個(gè)應(yīng)用程序復(fù)位為止。當(dāng)一個(gè)自動(dòng)復(fù)位事件處于有信號(hào)狀態(tài)時(shí),只有一個(gè)等待線程被激活,并且事件將復(fù)位成無(wú)信號(hào),其它所有等待著的線程仍將保持掛起狀態(tài)。

          定義3個(gè)事件:

          m_hEventArray[0]=m_hShutdownEvent;

          //結(jié)束通過(guò)線程事件

          m_hEventArray[1]=m_ov.hEvent; //讀事件

          m_hEventArray[2]=m_hWriteEvent;//寫事件

          在通信線程的線程函數(shù)CommThread中等待3個(gè)事件的發(fā)生

          Event=WaitForMultipleObjects (3,port ->m_hEventArray,FALSE,INFINITE);

          switch (Event)

          {

          case 0: //結(jié)束通信線程事件

          {

          port->m_bThreadAlive=FALSE;

          AfxEndThread(100);//結(jié)束通信線程

          Bread;

          }

          case 1://讀事件

          {

          GetCommMask(port->m_hComm,&CommEvent);

          If (CommEvent & EV_RECHAR)

          ReceiveChar(port,comstat); //從串口讀數(shù)

          Break;

          }

          case 2: //寫事件

          {

          WriteChar(port); //向窗口寫數(shù)

          break;

          }

          }//end switch



          關(guān)鍵詞: 串行通

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();