基于C++Builder API函數的歐姆龍PLC串行通信
1 引言
本文引用地址:http://dyxdggzs.com/article/201610/306696.htm計算機串行通信是計算機與控制設備(plc)進(jìn)行數據傳送的基本通信方式,也是實(shí)現工業(yè)自動(dòng)控制經(jīng)常用到的通信模式。每一種通信方式都嚴格約定了與其對應的通信協(xié)議,要確保計算機與plc之間能正常通信,就必須遵照其通信協(xié)議編寫(xiě)通信程序。
2 串行通信
串行通信在工業(yè)系統控制的范疇中一直占據著(zhù)極其重要的地位,串行端口(rs-232)是計算機上的標準配置,常用于連接調制解調器來(lái)傳輸數據,在計算機的硬件設備管理器中可以看到,定義為com1、com2等。常用的串行通信方式有兩種,分別是rs-232和rs-485,本文以rs-232方式為例進(jìn)行介紹。
3 上位機編程
3.1 c++builder編程
c++builder是由borland公司推出的產(chǎn)品。它采用c++語(yǔ)言作為開(kāi)發(fā)語(yǔ)言,是面向對象語(yǔ)言,具有可視化編程界面且功能強大。
3.2 c++builder串行通信相關(guān)api函數
c++builder本身并不提供單獨的串行通信組件,而是使用一些windows api的函數來(lái)達到此目的。這些函數是由操作系統所提供,可以為程序設計人員提供相當多的執行功能。api中與串行通信相關(guān)的函數約有20個(gè),本文將對經(jīng)常使用的函數作討論。
(1) 打開(kāi)串行端口
hcomm=createfile(comno,generic_read|generic_write,0,null,open_existing,0,0)
函數參數定義如下:
hcomm:createfile()函數的返回值,程序使用此返回值進(jìn)行相關(guān)的串行端口操作。
comno:定義串行端口號,為com1、com2等。
generic_read|generic_write:對串行端口的讀/寫(xiě)操作。
0:是否共享串行端口,通常不會(huì )將串行端口與其它程序共享,因此設為0,否則為1。
null:函數的返回值hcomm是否可被子程序繼承,此處設為不可繼承。
open_existing:打開(kāi)端口的方式,串行端口是一種設備,必須指定為open_existing方式。
0:使用同步或異步方式傳輸數據,同步方式編程簡(jiǎn)單,速率快,因此設為0,否則為1。
0:由于使用串行端口編程,設為0。
(2) 得到串行端口狀態(tài):
getcommstate(hcomm,dcb)
函數參數定義如下:
hcomm:createfile()函數的返回值。
dcb:串行端口控制塊地址,負責對串行端口參數進(jìn)行設置,具體參數如下:
dcb.baudrate:設置串行端口的波特率,有19200kb/s、9600kb/s、4800kb/s幾種,一般為:9600kb/s。
dcb.bytesize:設置串行端口的數據位數,有5、6、7、8幾種,歐姆龍plc數據位數為7。
dcb.parity:設置串行端口的校驗位檢查,有none、even、odd幾種,設為none。
dcb.stopbits:設置串行端口的停止位數,有1、1.5、2幾種, 歐姆龍plc的停止位數為1。
(3) 設置串行端口狀態(tài):
setcommstate (hcomm,dcb)
函數參數定義與getcommstate()函數相同。
(4) 向串行端口寫(xiě)數據:
writefile(hcomm,senddata,bs,lrc,null)
函數參數定義如下:
hcomm:createfile()函數的返回值。
senddata:寫(xiě)數據的地址。
bs:寫(xiě)入數據的字節數。
lrc:被寫(xiě)入的數據地址。
null:寫(xiě)入數據的同步檢查,串行端口采用同步通信時(shí)可以設為null。
(5) 清除串行端口的錯誤或將串行端口當前的數據狀態(tài)送至輸入緩沖區:
clearcommerror(hcomm,dwerror,cs)
函數參數定義如下:
hcomm:createfile()函數的返回值。
dwerror:返回錯誤信息代碼。
cs:指向串行端口狀態(tài)的結構變量。
(6) 從串行端口的輸入緩沖區讀出數據:
readfile(hcomm,inbuff,cs.cbinque,nbytesread,null);函數參數定義如下:
hcomm:createfile()函數的返回值。
inbuff:指向用來(lái)存儲數據的地址。
cs.cbinque:讀取數據的字節數。
nbytesread:總的讀取字節數。
null:如果不進(jìn)行后臺工作,串行端口設為null。
(7) 關(guān)閉串行端口:
closehandle(hcomm)
函數參數定義如下:
hcomm:createfile()函數的返回值。
4 plc通信數據幀介紹
計算機與歐姆龍plc通信時(shí),按應答方式進(jìn)行。由計算機發(fā)給plc一組ascii碼字符數據,這一組數據成為命令塊。plc收到命令塊后經(jīng)分析認為命令正常,則按照命令進(jìn)行操作,將操作結果返回給計算機。plc返回給計算機的這一組數據稱(chēng)為響應塊。若plc收到命令后經(jīng)分析確認命令不正常,則返回給計算機錯誤命令塊。計算機和plc通信時(shí),歐姆龍plc是被動(dòng)的,必須先由計算機給plc發(fā)出命令塊,plc再給計算機發(fā)出響應塊。命令塊和響應塊以幀(frame)為單位進(jìn)行傳送,一幀最多由131個(gè)字符組成。下面將歐姆龍plc命令幀與響應幀的組成結構介紹如下:
4.1 命令幀
命令幀組成結構如圖1所示。

幀結構解析:
@:在起始處必須放置
節點(diǎn)號:有效值為00—31, 表示pc機最多可同32臺plc通信
頭代碼:plc的命令代碼
發(fā)送文本:pc機發(fā)送的命令參數
fcs(frame check sequence) :幀檢查順序代碼(幀校驗碼)
幀校驗碼是2位(bit) 十六進(jìn)制數。它是由幀數據包含的所有字符的ascii碼進(jìn)行位異或運算的結果。
終止符:“*”號和回車(chē)符“cr”
舉例如下:
讀h區命令幀結構如圖2所示。

4.2 響應幀
響應幀結構如圖3所示。

幀結構解析:
@ :返回命令頭
節點(diǎn)號 :有效值為00—31,返回數據的plc節點(diǎn)號
頭代碼 :plc的命令代碼
尾代碼 : 返回命令完成狀態(tài)碼
接收文本: 在有數據時(shí)返回的數據
fcs :幀檢查順序代碼
終止符:“*”號和回車(chē)符“cr”
舉例如下:
讀h區響應幀結構圖4所示。

4.3 fcs(幀數據冗余校驗碼)的計算
為了降低串行通信的誤碼率,在接收和發(fā)送端都必須對數據進(jìn)行校驗,常用的方法是進(jìn)行fcs校驗。對幀數據進(jìn)行冗余校驗計算時(shí),應對幀數據中各個(gè)字符的ascii碼進(jìn)行位異或運算,然后將結果轉為2位十六進(jìn)制字符。
5 c++builder api函數應用
5.1 通信主程序的設計架構
通信主程序的主要功能:實(shí)現計算機對plc的運行控制和狀態(tài)監視,即構成一個(gè)閉環(huán)監控系統,程序設計架構如圖5所示。

5.2 打開(kāi)串信端口
(1) 打開(kāi)通信端口,對端口進(jìn)行初始化設置,工作流程如圖6示。

(2) 打開(kāi)通信端口程序源代碼:
void__fastcall tform1::button1click(tobject *sender)
{
char *comno;
dcb dcb;
string temp;
temp=“com”+inttostr(rdcom-》itemindex+1);
comno=temp.c_str() ;
hcomm=createfile(comno,generic_read|generic_write,
0,null,open_existing,1,0);
if(hcomm==invalid_handle_value)
{
messagebox(0,“打開(kāi)通信端口錯誤,請檢查端口是否被占用!!” ,“comm error”,mb_ok);
return;
}
getcommstate(hcomm,dcb);
dcb.baudrate=cbr_9600;
dcb.bytesize =7;
dcb.parity =evenparity;
dcb.stopbits =onestopbit;
setcommstate(hcomm,dcb);
if(!setcommstate(hcomm,dcb))
{
messagebox(0,“通信端口設置錯誤!!!”,“set error”,mb_ok);
closehandle(hcomm);
return;
}
}
5.3 寫(xiě)plc內存數據
(1) 將計算機發(fā)出的命令寫(xiě)入plc,實(shí)現計算機對plc的控制功能。工作流程如圖7示。

(2) 寫(xiě)plc內存函數程序源代碼:
string tform1::write(string address,string value)
{
unsigned long lrc,bs;
string temp;
char *senddata;
char inbuff[1024];
int ln,i=0;
string word,check;
dword nbytesread,dwevent,dwerror;
comstat cs;
word=“@00wd”+address+value;
if(hcomm==0)
{
messagebox(0,“串口未打開(kāi)!!!”,“錯誤信息”,mb_ok);
return(0);
}
temp=outchecksum(word);
senddata=temp.c_str() ;
bs=strlen(senddata);
loop:
if(++i《=3)
{
writefile(hcomm,senddata,bs,lrc,null);
sleep(100);
if(hcomm==invalid_handle_value) return(0);
clearcommerror(hcomm,dwerror,cs);
if(cs.cbinque》sizeof(inbuff))
{
purgecomm(hcomm,purge_rxclear);
return(0);
}
readfile(hcomm,inbuff,15,nbytesread,null);
check=inbuff;
if(check.substring(6,2)!=“00”)
{
goto loop;
}
}
else
{
messagebox(0,“數據寫(xiě)錯誤”,“通信錯誤”,mb_ok);
}
}
5.4 讀plc內存數據
(1)從plc中讀取數據,監視plc的運行數據,工作流程如圖8示。

(2) 讀plc內存函數程序源代碼:
string tform1::read(string address,string value)
{
string readdata,readdata1,readdata2;
string temp;
unsigned long lrc,bs;
char *senddata;
int ln,i=0,len;
dword nbytesread,dwevent,dwerror;
comstat cs;
char inbuff[1024];
string word;
word=“@00rd”+address+value;
if(hcomm==0) return(0);
temp=outchecksum(word);
senddata=temp.c_str();
bs=temp.length();
loop:
if(++i《=3)
{
writefile(hcomm,senddata,bs,lrc,null);
sleep(100);
if(hcomm==invalid_handle_value) return(0);
clearcommerror(hcomm,dwerror,cs);
if(cs.cbinque》sizeof(inbuff))
{
purgecomm(hcomm,purge_rxclear);
return(0);
}
cs.cbinque=4*strtoint(value)+11;
readfile(hcomm,inbuff,cs.cbinque,nbytesread,null);
inbuff[cs.cbinque]=`国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放