PLC串行通信和并行通信數據傳輸方式
串行通信和并行通信是兩種不同的數據傳輸方式。
串行通信就是通過一對導線將發送方與接收方進行連接,傳輸數據的每個二進制位,按照規定順序在同一導線上依次發送與接收。例如,常用的優盤USB接口就是串行通信。串行通信的特點是通信控制復雜,通信電纜少,因此與并行通信相比,成本低。
并行通信就是將一個8位數據(或16位、32位)的每一個二進制位采用單獨的導線進行傳輸,并將傳送方和接收方進行并行連接,一個數據的各二進制位可以在同一時間內一次傳送。例如,老式打印機的打印口和計算機的通信就是并行通信。并行通信的特點是一個周期里可以一次傳輸多位數據,其連線的電纜多,因此長距離傳送時成本高。
三菱PLC串口通信開發心得經驗
記得兩年前剛開始從事軟件開發工作時,第一份任務就是開發一個程序能夠實現與三菱PLC 串口通信。所謂通信,其實質主要是對PLC 的D寄存器(dword)讀寫操作。但是因為日本為了保護其產品,并不開發串口通信協議。在不開發通信協議的情況,如果想實現通信,首先需要做的便是通過數據分析,破解其通信協議。這里就不講解如何破解了,主要是介紹下當時博主開發程序的背景。
小編寫這篇文章的主要目的是為了分享過去自己的開發經驗,因為自己在開發的過程中曾經接受過很多開源軟件的幫助,現在這是轉入正題。
涉及字節流數據通信,必然要涉及通信協議。鑒于當時的開發需求,博主僅對D寄存器的讀寫協議分析過。其他寄存器理論上是相似,有興趣的同學可以自行分析數據進行測試。
D寄存器的通信協議相對比較簡單,主要可以分為:
1.問候應答協議
2.狀態查詢協議
3.狀態配置協議
4.數據反饋協議
在PLC通信過程中主要的三個難點在于寄存器的加密解密,數據信息加密和解密,以及字符的校驗。
寄存器地址加密過程:
《span style=“font-size:18px;”》void PLC_dataparse::Encrypt_toPLCaddress( BYTE *parray , const UINT paddress )
{
int encode_address = 0x1000 + paddress * 2;
BYTE encrypt_key = encode_address & 0xf;
parray[3] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
encrypt_key = (encode_address 》》 4) & 0xf;
parray[2] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
encrypt_key = (encode_address 》》 8) & 0xf;
parray[1] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
encrypt_key = (encode_address 》》 12) & 0xf;
parray[0] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
}
《/span》
數據信息的加密過程:
《span style=“font-size:18px;”》void PLC_dataparse::Encrypt_toPLCcontent( BYTE * parray , const UINT pcontent )
{
BYTE encrypt_key = pcontent & 0xf;
parray[1] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
encrypt_key = (pcontent 》》 4) & 0xf;
parray[0] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
encrypt_key = (pcontent 》》 8) & 0xf;
parray[3] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
encrypt_key = (pcontent 》》 12) & 0xf;
parray[2] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
}
《/span》
添加校驗碼:
《span style=“font-size:18px;”》void PLC_dataparse::Add_checkcode( BYTE * pdest , BYTE * psrc , const UINT plenth )
{
int sumtemp = 0;
for ( unsigned int i = 0; i《 plenth; i++)
{
sumtemp += (*(psrc + i));
}
BYTE encrypt_key = sumtemp & 0xf; // get low 4 bit
pdest[1] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
encrypt_key = (sumtemp 》》 4) & 0xf; // get high 4 bit
pdest[0] = (encrypt_key《10) ? (encrypt_key + 0x30) : (encrypt_key + 0x41 - 0xa);
}
《/span》
提取數據信息:
《span style=“font-size:18px;”》double PLC_dataparse::Get_content( BYTE *parray , UINT plenth )
{
BYTE dl_data[4];
BYTE pre_data[4];
double pow_numb;
for (int j = 0; j《4; j++) //剔除雜碼
{
pre_data[j] = parray[j + 1];
}
//////////////////////////////////////////////////////////////////////////
dl_data[1] = (pre_data[0]《0x40) ? (pre_data[0] - 0x30) : (pre_data[0] - 0x41 + 0x0a);
dl_data[0] = (pre_data[1]《0x40) ? (pre_data[1] - 0x30) : (pre_data[1] - 0x41 + 0x0a);
dl_data[3] = (pre_data[2]《0x40) ? (pre_data[2] - 0x30) : (pre_data[2] - 0x41 + 0x0a);
dl_data[2] = (pre_data[3]《0x40) ? (pre_data[3] - 0x30) : (pre_data[3] - 0x41 + 0x0a);
for (int i = 0; i《4; i++)
{
dl_data[i] = dl_data[i] & 0xf;
}
pow_numb = dl_data[3] * pow(16.0, 3.0) + dl_data[2] * pow(16.0, 2.0) + dl_data[1] * 16 + dl_data[0];
return pow_numb;
}
《/span》
校驗接受數據校驗碼:
int PLC_dataparse::Check_checkcode( BYTE *parray , UINT plenth )
{
int error_code = PLC_SUCCESS;
const int legal_lenth = 8; //the define legal lenth
if (plenth != legal_lenth)
{
error_code = PLC_CRCERROR;
return error_code;
}
//////////////////////////////////////////////////////////////////////////
//check code
else
{
BYTE *pbyte = new BYTE[2];
// split out head mark , tail check out
Add_checkcode(&pbyte[0], &parray[1], plenth - 3); //calculate the check code
for (int j = 0; j《2; j++)
{
if (pbyte[j] != parray[plenth - 2 + j])
{
error_code = PLC_CRCERROR;
break;
}
}
// release the pointer and it‘s stack
delete pbyte;
pbyte = NULL;
return error_code;
}
}
上述代碼是使用PLC窗口通信的最大的難點。一旦掌握幾大難點,基本PLC的串口通信就很簡單了。
另附上一份當時自己開發的三菱PLCD寄存器調試程序。
備注:該調試工具僅支持xp系統
PLC-串口通信實例
1、三菱PLC與計算機之間通信協議
串行通信是指外設和計算機間使用一根數據信號線一位一位地傳輸數據,每一位數據都占據一個固定的時間長度。“串行”是指外設與接口電路之間的信息傳送方式,CPU與接口之間仍按并行方式工作。串行通信的四個重要參數:波特率(衡量通信速度的參數)、奇偶校驗位(一種簡單的檢錯方式)、數據位(衡量通信中實際數據位的參數)和停止位(表示單個數據包的最后一位)。
(1)三菱FX2N系列通信數據幀格式
FX2N系列的PLC與計算機之間的通信采用RS-232C標準,其傳輸速率一般設為9 600 bps,實際傳輸過程還可設其它,比如115 200 bps等。奇偶校驗位采用偶校驗。數據以幀為單位發送和接收。一個多字符幀由起始字元、命令號碼、元件首地址、結束字元、和校驗五部分組成,其中和校驗值是將命令碼STX—ETX之間的字符的ASCII碼(十六進制數)相加,取得所得和的最低二位數。STX和ETX分別表示該字符幀的起始標志和結束標志。
起始字元(STX):ASCII碼的起始字元STX對應的16進制數位0x02。無論命令信息還是回應信息,它們的起始字元均為STX,接收方以此來判知傳輸資料的開始;
命令號碼:為兩位16進制數。所謂命令號碼是指上位機要求下位機所執行的動作類別,例如要求讀取或寫入單點狀態、寫入或讀取暫存器資料、強制設定、運行、停止等。在回應信息中,下位機會將上位機接收到的命令號碼隨同其它信息一同發送給上位機;
元件首地址:對應要操作的元件的相應的地址。如從D123單元中讀取數據時,要把它對應的地址:0x10F6發送給PLC;
元件個數:一次讀取位元件或字元件的數量;
結束字元(ETX):ASCII碼的結束字元ETX對應的16進制數為0x03。無論命令信息還是回應信息,它們的結束字元均為ETX,接收方以此來判知此次通訊已結束;
校驗碼(Checksum):校驗碼是將STX—ETX之間的ASCII字元的16進制數值以“LRC(Longitudinal Redundancy Check)”法計算出1個Byte長度(兩個16進制數值00-FFH)的校驗碼。當下位機接收到信息后,用同樣的方法計算出接收信息的校驗碼,如果兩個校驗碼相同,則說明傳送正確。
(2)三菱FX2N系列通信命令
FX2N系列PLC有4個通信命令,它們是讀命令(30H)、寫命令(31 H)、強制通命令(37H)、強制斷命令(38H)。
(3)三菱FX2N系列通信控制字符
ENQ(ASCII代碼05H):計算機向PLC發送請求;
ACK(ASCII代碼06H):PLC對ENQ的確定回答;NAK(ASCII代碼15H):PLC對ENQ的否認回答;
STX(ASCII代碼02H):報文開始;
ETX(ASCII代碼02H):報文結束。
(4)FX2N系列設備地址
①讀寫時的軟設備地址
S0-S7:0000H;X0-X7:0080H;Y0-Y7:00AOH;TO-T7:00COH;M0-M7:0100H;CO-C7:01COH;DO-D7:1000H
②置位/復位時的軟設備地址
S0-S7:0000H;X0-X7:0400H;Y0-Y7:0500H;TO-T7:0600H:MO-M7: 0800H;CO-C7:0E00H;DO-D7:0100H
③傳輸過程
PC機與FX系列PLC之間采用應答方式通信,傳輸出錯則組織重發。其傳輸過程如圖1所示。
PLC根據PC機的命令,在每個循環掃描結束處的END語句后組織自動應答,無需用戶在PLC一方編寫程序。
2、系統功能設計
系統主要實現PLC與計算機的通訊,具體主要完成PC機指令下傳、監測PLC狀態、接收PLC信息等功 能。系統組成:小型PLC一臺、RS232串口、編程電纜、通訊界面。主操作界面在完成系統功能的前提下,力求明了直觀,操作簡單靈活方便。系統以VC++6.0為平臺,設計的界面如圖2所示。
本程序設計了四個串口可供選擇,只有在選擇串口之后才可進行“打開串口,關閉串口”的操作,當打開串口以后,就可以對PLC進行相應的操作了,為了使界面整潔干凈,特別設計了“清空發送區”和“清空接收區”選項,當發送數據和接收數據放滿編輯框時只需點擊這兩個按鈕,數據就會清空。且實現代碼相當簡單,m_sSend.Empty()、m_sReceive.Empty()就可輕松實現這一任務。
PC機與PLC的通訊程序流程圖如圖3所示。
系統通信控制程序采用了MSComm控件。此控件提供了兩種通信方法:①文件驅動,即用MSComm控件的OnComm文件捕獲并處理通信事件和錯誤,它是處理串行端口交互作用的一種非常有效的方法;②查詢方式,通過查詢串口屬性來獲得事件和錯誤,實質上還是屬于事件驅動,但在有些情況下顯得更為便捷。MSComm6.0控件的屬性:①CommPort,設置或返回通信端口號;②Settings,以字符串的形式設置或返回波特率、奇偶校驗、數據位和停止位;③PortOpen,設置或返回通信端口的狀態,也可以打開和關閉端口;④Input,返回和刪除接收緩沖區中的字符;⑤InputMode,設置或返回Input屬性取回的數據的類型,數據取回的形式為字符串或二進制數據的數組;⑥CommEvent返回最近的通信事件或錯誤的數字代碼,通信程序設計時可以根據該屬性值執行不同的操作,在運行時為只寫;⑦Output,將字符串寫入發送緩沖區。
MSComm6.0控件只有一個事件,即Oncomm事件。在通信時如果發生錯誤或者事件,將會引發Oneomm事件并且改變其屬性值,通過GetCommEvent()可獲得Oncomm產生事件或錯誤的代碼。在與PLC進行通信的過程中,使用MSComm6.0控件可以自動完成PLC對計算機發送信息的接收,最終實現PC機對PLC的狀態檢測。
軟件實現過程:FX2N系列的PLC與計算機之間的通信采用RS-232C標準,其傳輸速率固定為9 600bps,奇偶校驗位采用偶校驗。數據以幀為單位發送和接收。PC機向PLC中寫數據時首先需對串口進行初始化,并對波特率、校驗位等進行設置,然后根據通信協議對PLC進行相應的讀寫、復位、置位等操作,PLC根據PC機送來的控制字進行相應的操作。數據發送,采用專用發送指令XMT TABLE,CommPort,其中TABLE為發送緩沖區的首地址,首地址中保存要發送的字節數,即數據長度,最大為255,其后的地址中保存要發送的數據,CommPort指定用于發送的端口。對于數據接收,使用接收指令RCV TABLE,CommPort,接收指令激活初始化或結束接收信息,通過制定端口接收信息并存儲于數據緩沖區中,數據緩沖區的第一個數據指明了接收的字節數。
3、系統功能驗證
將計算機用通訊電纜與PLC相連后,首先發送請求05H以后,驗證計算機與PLC是否可以正常通信,接收區顯示06,表示PLC對ENQ的確定回答,即PLC已準備好,可以進行下面的操作,具體如圖4和圖5。
這里主要對PLC讀值功能進行驗證。讀操作命令格式如下:
STX—CMD0一數據段一ETX—SUMH—SUML
在按上述命令格式發送相應的代碼后,就可直接讀取PLC響應的信息了。響應信息格式如下:
STX—DATA—ETX—SUMH—SUML
圖6和圖7分別是對PLC進行讀值驗證時發送數據和接收數據的顯示。
其中接收數據顯示中的023030033633,是對x軟地址值(0080H)讀取后接收到的數據。具體算法如下:
nSUMLx=(0X30+0X30+0X03)%16=3《9,
nSUMHx=((0X30+0X30+0X03)%256)/16=6《9,
nSUMLx=0x30+nSUMLYl=0X33,
nSUMHx=0X30+nSUMHY2=0X36
故,轉變成兩字節ASCII代碼SUMLx=33;SUMHx=36。
理論分析和實際操作的結果是一致的,即證明了本設計是準確無誤的。
評論
查看更多