介紹
介紹工業控制領域利用序列槽和外圍裝置進行通訊。 正文
前言:
總所周之,利用序列槽進行資料通訊在在通訊通訊領域重占有着重要的地位。利用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);
//進行湘陰的解包處理
}