天天看點

--序列槽通訊方法(WINAPI實作)(

介紹

介紹工業控制領域利用序列槽和外圍裝置進行通訊。 正文

前言:

    總所周之,利用序列槽進行資料通訊在在通訊通訊領域重占有着重要的地位。利用RS232-RS485進行資料信号的采集和傳遞是VC程式設計的又一大熱點。序列槽通訊在通訊軟體重有着十分廣泛的應用。如電話、傳真、視訊和各種控制等。在各種開發工具中間,VC由于功能強大和靈活,同時也得到了Microsoft的最大支援,是以在一般進行涉及硬體操作的通訊程式設計重,大都推薦使用VC作為開發工具。然而工業控制序列槽通訊這個又不同于一般的序列槽通訊程式,因為控制外圍裝置傳送的大都是十六進制資料(BYTE類型),是以,為了提高程式的運作穩定性,我們在編寫程式進行通訊時可以不考慮傳送BYTE類型資料的工作。

    序列槽通訊目前流行的方法大概有兩種:一是利用Microsoft提供的CMSCOMM控件進行通訊,不過現在很多程式員都覺應該放棄這種方式。二是利用WINAPI函數進行程式設計,這種程式設計的難度最高,要求你要掌握很多的API函數。三是利用現在網絡上面提供的一些序列槽通訊控件進行編寫,比如CSerial類等。

程式實作:

    我在經過許多的項目的開發和實踐中發現,采用WIN API函數進行序列槽的開發能夠給程式員很大的控件,并且程式運也很穩定。是以我将與序列槽接觸的函數進行封裝,然後在各個工程中進行調用,效果還是比較好的,現将各個函數和調用方法列舉出來,希望對各位有所幫助。

    一、設定序列槽相關工作

#define         MAXBLOCK 2048

#define         XON 0x11

#define         XOFF 0x13

BOOL SetCom(HANDLE &m_hCom, const char *m_sPort, int BaudRate, int Databit, CString parity, CString stopbit)

{

     COMMTIMEOUTS TimeOuts;                                 ///序列槽輸出時間 逾時設定

     DCB dcb;                                               ///與端口比對的裝置    

    m_hCom=CreateFile(m_sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,

         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,

         NULL); // 以重疊方式打開序列槽

     if(m_hCom==INVALID_HANDLE_VALUE)

     {  

        AfxMessageBox("設定序列槽部分,序列槽打開失敗");   /重疊方式 異步通信(INVALID_HANDLE_VALUE)函數失敗。

         return FALSE;

     }    

    SetupComm(m_hCom,MAXBLOCK,MAXBLOCK);               //設定緩沖區

    memset(&TimeOuts,0,sizeof(TimeOuts));    

    TimeOuts.ReadIntervalTimeout=MAXDWORD;           // 把間隔逾時設為最大,把總逾時設為0将導緻ReadFile立即傳回并完成操作

    TimeOuts.ReadTotalTimeoutMultiplier=0;           //讀時間系數

    TimeOuts.ReadTotalTimeoutConstant=0;             //讀時間常量  

    TimeOuts.WriteTotalTimeoutMultiplier=50;       //總逾時=時間系數*要求讀/寫的字元數+時間常量

    TimeOuts.WriteTotalTimeoutConstant=2000;       //設定寫逾時以指定WriteComm成員函數中的                                              

    SetCommTimeouts(m_hCom, &TimeOuts);           //GetOverlappedResult函數的等待時間*/

     if(!GetCommState(m_hCom, &dcb))               序列槽打開方式、端口、波特率 與端口比對的裝置

     {

        AfxMessageBox("GetCommState Failed");

         return FALSE;

     }

    dcb.fParity=TRUE;                           //允許奇偶校驗        

    dcb.fBinary=TRUE;

     if(parity=="NONE")

        dcb.Parity=NOPARITY;

     if(parity=="ODD")

        dcb.Parity=ODDPARITY;

     if(parity=="EVEN")

        dcb.Parity=EVENPARITY;

     if(stopbit=="1")//設定波特率

        dcb.StopBits=ONESTOPBIT;

     //if(stopbit=="0")//設定波特率

     //    dcb.StopBits=NONESTOPBIT;

     if(stopbit=="2")//設定波特率

        dcb.StopBits=TWOSTOPBITS;                            

     BOOL m_bEcho=FALSE;                         ///

    int m_nFlowCtrl=0;

     BOOL m_bNewLine=FALSE;                     ///

    dcb.BaudRate=BaudRate;                     // 波特率

    dcb.ByteSize=Databit;                     // 每位元組位數    

     // 硬體流控制設定

    dcb.fOutxCtsFlow=m_nFlowCtrl==1;

    dcb.fRtsControl=m_nFlowCtrl==1     ?RTS_CONTROL_HANDSHAKE:RTS_CONTROL_ENABLE;    

     // XON/XOFF流控制設定(軟體流控制!)

    dcb.fInX=dcb.fOutX=m_nFlowCtrl==2;

    dcb.XonChar=XON;

    dcb.XoffChar=XOFF;

    dcb.XonLim=50;

    dcb.XoffLim=50;    

     if(SetCommState(m_hCom, &dcb))    

         return TRUE;           com的通訊口設定    

     else

     {

        AfxMessageBox("序列槽已打開,設定失敗");

         return FALSE;

     }

}

二、讀序列槽操作:

int ReadCom(HANDLE hComm, BYTE inbuff[], DWORD &nBytesRead, int ReadTime)

{

     DWORD lrc;                                 ///縱向備援校驗

     DWORD endtime;                             /jiesuo

     static OVERLAPPED ol;

    int ReadNumber=0;    

    int numCount=0 ;                             //控制讀取的數目

     DWORD dwErrorMask,nToRead;  

     COMSTAT comstat;    

    ol.Offset=0;                             ///相對檔案開始的位元組偏移量

    ol.OffsetHigh=0;                         ///開始傳送資料的位元組偏移量的高位字,管道和通信時調用程序可忽略。

    ol.hEvent=NULL;                         ///辨別事件,資料傳送完成時設為信号狀态

    ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);    

    endtime=GetTickCount()+ReadTime;//GetTickCount()取回系統開始至此所用的時間(毫秒)

     for(int i=0;i<2000;i++)

        inbuff[i]=0;    

     Sleep(ReadTime);

    ClearCommError(hComm,&dwErrorMask,&comstat);

    nToRead=min(2000,comstat.cbInQue);  

     if(int(nToRead)<2)

        goto Loop;

     if(!ReadFile(hComm,inbuff,nToRead,&nBytesRead,&ol))

     {    

         if((lrc=GetLastError())==ERROR_IO_PENDING)

         {

             ///

            endtime=GetTickCount()+ReadTime;//GetTickCount()取回系統開始至此所用的時間(毫秒)

             while(!GetOverlappedResult(hComm,&ol,&nBytesRead,FALSE))//該函數取回重疊操作的結果

             {

                 if(GetTickCount()>endtime)

                     break;

             }    

         }        

     }

     return 1;    

Loop:   return 0;

}

三、寫序列槽指令

int WriteCom(HANDLE hComm, BYTE Outbuff[], int size, int bWrite[])

{

     DWORD nBytesWrite,endtime,lrc;

     static OVERLAPPED ol;

     DWORD dwErrorMask,dwError;

     COMSTAT comstat;

    ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

    ol.Offset=0;              

    ol.OffsetHigh=0;

    ol.hEvent=NULL;               ///辨別事件,資料傳送完成時,将它設為信号狀态

    ClearCommError(hComm,&dwErrorMask,&comstat);

     if(!WriteFile(hComm,Outbuff,size,&nBytesWrite,&ol))

     {

         if((lrc=GetLastError())==ERROR_IO_PENDING)

         {

            endtime=GetTickCount()+1000;

             while(!GetOverlappedResult(hComm,&ol,&nBytesWrite,FALSE))

             {  

                dwError=GetLastError();

                 if(GetTickCount()>endtime)

                 {    

                    AfxMessageBox("寫序列槽時間過長,目前序列槽發送緩沖區中的資料數目為空");

                     break;

                 }

                 if(dwError=ERROR_IO_INCOMPLETE)

                     continue;           //未完全讀完時的正常傳回結果

                 else

                 {

                     //    發生錯誤,嘗試恢複!

                    ClearCommError(hComm,&dwError,&comstat);

                     break;

                 }

             }

         }      

     }    

    FlushFileBuffers(hComm);

    PurgeComm(hComm,PURGE_TXCLEAR);                

    bWrite=0;

     return 1;

}

四、調用方法很簡單,隻需要将你的序列槽參數進行簡單的設定就可以了。比如:

BOOL Main_OpenCom()//設定COM

{

    int Boundrate=9600;//波特率

    CString StopBits="1";//停止位

    int DataBits=8;//資料位

    CString Parity="ODD";//奇偶校驗

    CString m_Port="COM1";

     return SetCom(m_hCom1,m_Port,Boundrate,DataBits,Parity,StopBits);

}

void Main()

{

int SIZE;

     DWORD BytestoRead=52*Count+6;//要11個位元組

    int   BWRITE[2];    

    int ReadTime=2000;

     BYTE Outbuff[12]={0xff,0x00,0xea,0xff,0xea,0xff,0,0,0,0,0,0};

     SIZE=sizeof(Outbuff);

    WriteCom(m_hCom,Outbuff,SIZE,BWRITE);

    ReadCom(m_hCom,m_Inbuff,BytestoRead,ReadTime);

         //進行湘陰的解包處理

}