計算機與PLC的通訊格式和實現(xiàn)方法
本文簡要介紹了PLC編程數(shù)據(jù)口的操作命令格式及其VC++語言實現(xiàn)通訊方法,并給出了四類操作的VC++語言實現(xiàn),為監(jiān)控和管理PLC的運行提供了一種良好的方法。
關鍵詞:PLC;編程口;串口通訊;VC++語言
一、引言
可編程序控制器(PLC)都有一個編程口。以日本三菱公司生產(chǎn)的PLC為例(包括FX系列和A系列),其編程口為RS-422格式,根據(jù)PLC型號不同又分為8針座編程口和25針座編程口。對于后者,可直接將SC—08編程電纜將PLC的編程口和微型計算機的RS—232口連接起來;對于后者,則還需要一根轉(zhuǎn)換電纜將PLC 的8針座編程口和25針編程電纜相連。無論何種情況,一旦將PLC用戶程序由微型計算機編程環(huán)境傳到PLC 用戶程序區(qū),其編程口大多就沒有被再利用。其實,這是一種浪費。也就是說,可利用此編程口實現(xiàn)微型計算機和PLC 的數(shù)據(jù)通訊,將PLC的工作狀態(tài)納入微型計算機管理之下。
二、編程口操作命令類型與通訊端口初始化
串行通訊是計算機與其它機器之間進行通訊的一種常用方法,在WINDOWS操作系統(tǒng)中提供了實現(xiàn)各種串行通訊的API函數(shù)。通過SC—08編程電纜或FX232AW模塊,可將微型計算機的串行通訊口RS—232和PLC 的編程口連接起來,這樣微型計算機就可對PLC的RAM區(qū)數(shù)據(jù)進行讀、寫操作。由PLC本身所具有的特性,可對PLC進行以下四種類型的操作:
?。?)位元件或字元件狀態(tài)讀操作(CMD0);
(2)位元件或字元件狀態(tài)寫操作(CMD1);
?。?)位元件強制ON操作(CMD7);
?。?)位元件強制OFF操作(CMD8)。
另外,在進行上述四類操作以前,首先要對端口進行初始化操作,即設定通訊協(xié)議(包括設置通訊波特率、數(shù)據(jù)位數(shù)、數(shù)據(jù)停止位及奇偶校驗)。在WINDOWS的SDK中定義了一個結構DCB,該結構詳細地說明了如何對通訊端口進行控制,所以通訊端口的初始化也是圍繞著對這個結構的正確設置為中心進行的。用VC++語言實現(xiàn)端口初始化如下:
BOOL CSerial::Open(int nPort)
{
//nPort 為微型計算機串行通訊口端口號。nport=1為端口1;nPort=2為端口2。
char szPort\[15\];
DCB dob;
m_hIDComDev=CreateFile(szPort, GENERIC_READ│GENERIC_WRITE,O,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL│FILE_FLAG_OVERLAPPED,NULL);
dcb.DCB1ength=sizeof(DCB);
GetCommState(m_hIDComDev,&dcb);//取得通訊資源當前設置
dcb.BaudRate=9600;//設定波特率為9600
dcb.ByteSize=7;//7數(shù)據(jù)位
dcb.Parity=2;//偶校驗
dcb.StopBits=0;//設定1個停止位
if(SetCommState(m_hIDComDev,&dcb)return(TRUE);
else return(FALSE);//設置端口,若設置成功則返回TRUE,否則返回FALSE
}
需要說明的是CSerial是一個用于串行通訊的類,它包含了進行串行通訊的所需的函數(shù)。除上述端口初始化成員函數(shù)Open外,還包括另兩個重要成員函數(shù):一個是endData,把數(shù)據(jù)從一個緩沖區(qū)發(fā)送到串行端口。另一個是ReadData,從端口的接收緩沖區(qū)中讀入數(shù)據(jù)。
其次,在每進行一次上述四類操作中的一種操作以前,還要進行握手聯(lián)絡。對PLC發(fā)請求訊號ENQ(代碼為OX05),然后讀PLC 的響應訊號。如果讀到的響應訊號為ACK(代碼為OX06),則表示PLC已準備就緒,等待接收通訊數(shù)據(jù)。握手聯(lián)絡VC++語言示PLC已準備就緒,等待接收通訊數(shù)據(jù)。握手聯(lián)絡VC++語言實現(xiàn)為:
BOOL CNTJD1g::ReadFromPLC(char *Read_char char *Read_address,int Read_bytes)
{
CSerial Serial;//用于串行通訊的類
char read_BUFFER;
if(Serial.Open(2)//初始化串行口通訊口COM2
{ Serial.SendData(&ENQ_request,1);//發(fā)送聯(lián)絡訊號
Sleep(1000);//等待1秒鐘
Serial.ReadData(&read_BUFFER,1);//讀取PLC響應訊號
if(read_BUFFER==ACK)
{ 如果PLC響應訊號等于ACK,則進行上述四種操作:}}
Serial.Close()://操作完畢后,關閉通訊口
}
三、編程口命令操作
?。?)位元件或字元件狀態(tài)讀操作
操作對象元件:PLC內(nèi)部的X、Y、M、S、T、C、D元件;命令格式:
說明:①為讀命令起始標志STX,代碼為OX02;
?、跒槲辉蜃衷顟B(tài)讀命令CMDO,命令代碼為OX30;
?、蹫樽x位元件或字元件的4位起始地址,高位先發(fā),低位后發(fā),且是以ASCII碼的形式發(fā)送;
?、転橐淮巫x取位元件或字元件的個數(shù),最多一次可讀取OXff個字節(jié)的元件,以ASCII碼的形式發(fā)送;
⑤為停止位標志ETX,代碼為OX03;
?、逓?位和校驗,和累計為②、③、④項代碼,取其和最低兩位轉(zhuǎn)化成ASCII碼,高位先發(fā),低位后發(fā)。
在發(fā)送完上述命令格式代碼后,就可直接讀取PLC響應的信息。響應信息格式如下:
VC++語言實現(xiàn):
BOOL CNTJDlg::ReadFromPLC(char *Read_char char
*Read_address, int Read_bytes)
{
char senddatasum_CHECK\[2\];char readdatasum_CHECK\[2\]; char total_DATABYTES\[2\];
char readdatasum_check\[2\];int readdata_sum;
int datasum_check=0; int i;
Serial.SendData(&STX_start,1);/向PLC發(fā)送“開始”標志代碼
Serial.SendData(&CMDO_read,1);//發(fā)送“讀”命令代碼datasum_check+=CMDO_read;
for(i=0;i<4;i++){Serial.SendData(&Read_address\[i\],1);//發(fā)送起始元件地址的ASCII代碼datasum_check+=Read_address\[i\];}
Change to ASCII(total DATABYTES,Read_bytes);//將字節(jié)數(shù)轉(zhuǎn)化成ASCII代碼
for (i=0;i<2;i++){Serial.SendData(&total_DATABYTES\[i\],1);//發(fā)送元件字節(jié)數(shù)的ASCII代碼)datasum_check+total_DATABYTES\[i\];}
Serial.SendData(&ETX_end,1);//發(fā)送“結束”標志代碼senddatasum_CHECK+ETX_end;
Change_to_ASCII(senddatasum_CHECK,senddatasum_CHECK);//將“和”轉(zhuǎn)化成ASCII碼
for (i=0;i<2;i++) Serial.SendData(&senddatasum_CHECK\[i\],1);
Sleep(1000);//等待PLC響應
Serial.ReadData(&read_BUFFER,1);
if(read_BUFFER==STX_start){
readdata_sum=0;
for(i=0;i<2*Read_bytes;i++){Serial.ReadData(&Read_char\[i\],1);//讀Read_bytes個字節(jié)readdata_sum+Read_char\[i\];}
Serial.ReadData(&read_BUFFER,1);
if(read_BUFFER==ETX_end){Serial.ReadData(readdatasum_CHECK,2);//讀入的“和”的低2位ASCII碼Readdata_sum+=ETX_end;}
Change_to_ASCII(readdatasum_check,readdata_sum);//將計算得到的“和”轉(zhuǎn)化成ASCII碼
if(*readdatasum_CHECK==*readdatasum_check)//“和”校驗
{ AfxMessageBox(“數(shù)據(jù)讀出成功!”)return TRUE;}
else { AfxMessageBox(“校驗錯誤”)return FALSE.}
}
?。?)位元件或字元件狀態(tài)寫操作
操作對象元件:同3(1);命令格式:
說明:①為寫命令起始標志STX,代碼為OX02;
?、跒槲辉蜃衷顟B(tài)寫命令CMD1,命令代碼為OX31;
③為寫位元件或字元件的4位起始地址,高位先發(fā),低位后發(fā),且是以ASCII碼的形式發(fā)送;
?、転橐淮螌懭胛辉蜃衷膫€數(shù),以ASCII碼的形式發(fā)送;
?、轂榇龑懙絇LC RAM區(qū)的數(shù)據(jù)DATA,以ASCII碼形式發(fā)送;
?、逓橥V刮粯酥綞TX,代碼為OX03;
?、邽?位和校驗,和累計為②、③、④項代碼,取其和最低兩位轉(zhuǎn)化成ASCII碼,高位先發(fā),低位后發(fā)。
VC++語言實現(xiàn):
BOOL CNTJDlg::WritePLC(char *data_ADDRESS,char *Write_ASC,int bytesnumber)
{
char total_BYTES\[2\];char senddatasum_CHECK\[2\];
char read_BUFFER;char read_finishBUFFER;
int datasum_check=0; int i=0;
Serial.SendData(&STX_start,1);//向PLC發(fā)送“開始”標志代碼
datasum_check=0;Serial.SendData(&CMD1_write,1);//發(fā)送“寫”命令代碼
datasum_check+CMD1_write;
for(i=0;i<4;i++) {Serial.SendData(&data_ADDRESS\[i\],1);//發(fā)送起始元件地址的ASCII碼
datasum_check+=data_ADDRESS\[i\];
Change_to_ASCII(total_DATABYTES,bytesnumber);//將字節(jié)數(shù)轉(zhuǎn)化成ASCII碼
for(i=0;i<2;i++)
{
Serial.SendData(&total_BYTES\,1);//發(fā)送元件字節(jié)數(shù)的ASCII代碼
datasum_check+=total_BYTES\[i\];}
for {i=0;i
Serial.SendData(&Write_ASC\[i\],1);//發(fā)送要寫入的數(shù)據(jù)的ASCII碼
datasum_check+=Write_ASC\[i\];}
Serial.SendData(&ETX_end,1);//發(fā)送“結束”標志代碼
datasum_check+=ETX_end;
Change_to_ASCII(senddatasum_CHECK,datasum_check);//將“和”轉(zhuǎn)化成ASCII碼
Serial.SendData(&senddatasum_CHECK,2);
Sleep(1000); Serial.ReadData(&read_finishBUFFER,1);
if (read_finishBUFFER==ACK_reply)
{AfxMessageBox(“數(shù)據(jù)寫入 OK”)return TRUE;}
else {AfxMessageBox(“數(shù)據(jù)寫入失敗”)return FALSE。}
?。?)位元件強制ON操作
操作對象:X、Y、M、S、T、C元件;
命令格式:
說明:①為強制ON命令起始標志STX,代碼為OX02;
?、跒閺娭芆N命令CMD7,命令代碼為OX37;
?、蹫閺娭芆N位元件4位起始地址,高位先發(fā),低位后發(fā),是以ASCII碼形式發(fā)送;
④為停止位標志ETX, 代碼為OX03;
⑤為2位和校驗,和累計為②、③、④項代碼,取其和低兩位轉(zhuǎn)化成ASCII碼,高位先發(fā),低位后發(fā)。
VC++語言實現(xiàn):
void NTJDlg::ForceOnOperation (char *ON_Address)
{ int i;
char│syn_Check\[2\];
char read_buffer;
int Sum=0;
Serial.SendData(&STX_start,1);//向PLC發(fā)送“開始”標志代碼
Serial.SendData(&CMD7_ForceON,1);//發(fā)送“ON”命令代碼
Sum+=CMD7 ForceON;
for (i=0; i<4; i++) {Serial.SendData(&ON_Address\[i\],1)//發(fā)送起始元件地址的ASCII碼
Sum+=ON_Address\[i\];}
Serial.SendData(&ETX_end,1);//發(fā)送“結束”標志代碼Sum+=ETX_end;
Change_to_ASCII(Sum_Check,Sum);//將“和”轉(zhuǎn)化成ASCII碼
Serial.SendData(&Sum_Check,2);
Sleep(1000);
Serial.ReadData(&read_buffer,1);
if(read_finishBUFFER==ACK_reply) AfxMessageBox(“ON 操作 OK”);
else AfxMessageBox(“ON 操作失敗”)。
}
(4)位元件強制OFF操作
操作對象L同3(3);命令格式:
說明:①為強制OFF命令起始標志STX,代碼為OX02;
?、跒閺娭芆FF命令CMD8,命令代碼為OX38H;
?、蹫閺娭芆FF位元件4位起始地址,高位先發(fā),低位后發(fā),以ASCII碼形式發(fā)送;
?、転橥V刮粯酥綞TX,代碼為OX03;
?、轂?位和校驗,和累計為②、③、④項代碼,取其和最低兩位轉(zhuǎn)化成ASCII碼,高位先發(fā),低位后發(fā)。
VC++語言實現(xiàn):
void NTJDlg::ForceOffOperation (char *OFF_Address)
{
int i;
char Sum_Check\[2\];
char read_buffer;
int Sum=0;
Serial.SendData(&STX_start,1);//向PLC發(fā)送“開始”標志代碼
Serial.SendData(&CMD8_ForceOFF,1);//發(fā)送“OFF”命令代碼
Sum=CMD8_ForceOFF;
for (i=0;i<4;i++) {
Serial.SendData(&OFF_Address\[i\],1);//發(fā)送起始元件地址的ASCII碼
Sum+=OFF_Address\[i\];}
Serial.SendData(&ETX_end,1);//發(fā)送“結束”標志代碼
Sum+=ETX_end;
Change_to_ASCII(Sum_Check,Sum);//將“和”轉(zhuǎn)化成ASCII碼
Serial.SendData(&Sum_Check,2);
Skeeo(1000);
Serial.ReadData(&read_buffer,1);
if(read_fininhBUFFER==ACK_reply) AfxMessageBox(“OFF 操作 OK ”);
else AfxMessageBox(“OFF 操作失敗”)。
?。?br> 注意:必須嚴格按照上述四種操作命令格式進行發(fā)送,在發(fā)送前,起始地址、數(shù)據(jù)、數(shù)據(jù)個數(shù)、校驗和都必須按位轉(zhuǎn)換成ASCII碼。從PLC讀到的數(shù)據(jù)亦是ASCII碼形式,需要經(jīng)過適當轉(zhuǎn)換才能利用。另外,要注意強制命令地址與讀寫地址的順序不是一樣,且一次最多只能傳送64個字節(jié)數(shù)據(jù)。
四、結論
利用上述四種操作命令,就可對PLC的RAM區(qū)數(shù)據(jù)進行管理操作。將PLC的工作狀態(tài)納入微型計算機管理之下。在此基礎上,用戶可以應用VC很方便地設計自己的PLC人機接口界面,為監(jiān)控與管理PLC的運行提供一種良好的方法。
評論
查看更多