PLC串行通信和并行通信數(shù)據(jù)傳輸方式
串行通信和并行通信是兩種不同的數(shù)據(jù)傳輸方式。
串行通信就是通過一對(duì)導(dǎo)線將發(fā)送方與接收方進(jìn)行連接,傳輸數(shù)據(jù)的每個(gè)二進(jìn)制位,按照規(guī)定順序在同一導(dǎo)線上依次發(fā)送與接收。例如,常用的優(yōu)盤USB接口就是串行通信。串行通信的特點(diǎn)是通信控制復(fù)雜,通信電纜少,因此與并行通信相比,成本低。
并行通信就是將一個(gè)8位數(shù)據(jù)(或16位、32位)的每一個(gè)二進(jìn)制位采用單獨(dú)的導(dǎo)線進(jìn)行傳輸,并將傳送方和接收方進(jìn)行并行連接,一個(gè)數(shù)據(jù)的各二進(jìn)制位可以在同一時(shí)間內(nèi)一次傳送。例如,老式打印機(jī)的打印口和計(jì)算機(jī)的通信就是并行通信。并行通信的特點(diǎn)是一個(gè)周期里可以一次傳輸多位數(shù)據(jù),其連線的電纜多,因此長(zhǎng)距離傳送時(shí)成本高。
三菱PLC串口通信開發(fā)心得經(jīng)驗(yàn)
記得兩年前剛開始從事軟件開發(fā)工作時(shí),第一份任務(wù)就是開發(fā)一個(gè)程序能夠?qū)崿F(xiàn)與三菱PLC 串口通信。所謂通信,其實(shí)質(zhì)主要是對(duì)PLC 的D寄存器(dword)讀寫操作。但是因?yàn)槿毡緸榱吮Wo(hù)其產(chǎn)品,并不開發(fā)串口通信協(xié)議。在不開發(fā)通信協(xié)議的情況,如果想實(shí)現(xiàn)通信,首先需要做的便是通過數(shù)據(jù)分析,破解其通信協(xié)議。這里就不講解如何破解了,主要是介紹下當(dāng)時(shí)博主開發(fā)程序的背景。
小編寫這篇文章的主要目的是為了分享過去自己的開發(fā)經(jīng)驗(yàn),因?yàn)樽约涸陂_發(fā)的過程中曾經(jīng)接受過很多開源軟件的幫助,現(xiàn)在這是轉(zhuǎn)入正題。
涉及字節(jié)流數(shù)據(jù)通信,必然要涉及通信協(xié)議。鑒于當(dāng)時(shí)的開發(fā)需求,博主僅對(duì)D寄存器的讀寫協(xié)議分析過。其他寄存器理論上是相似,有興趣的同學(xué)可以自行分析數(shù)據(jù)進(jìn)行測(cè)試。
D寄存器的通信協(xié)議相對(duì)比較簡(jiǎn)單,主要可以分為:
1.問候應(yīng)答協(xié)議
2.狀態(tài)查詢協(xié)議
3.狀態(tài)配置協(xié)議
4.數(shù)據(jù)反饋協(xié)議
在PLC通信過程中主要的三個(gè)難點(diǎn)在于寄存器的加密解密,數(shù)據(jù)信息加密和解密,以及字符的校驗(yàn)。
寄存器地址加密過程:
《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》
數(shù)據(jù)信息的加密過程:
《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》
添加校驗(yàn)碼:
《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》
提取數(shù)據(jù)信息:
《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》
校驗(yàn)接受數(shù)據(jù)校驗(yàn)碼:
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窗口通信的最大的難點(diǎn)。一旦掌握幾大難點(diǎn),基本PLC的串口通信就很簡(jiǎn)單了。
另附上一份當(dāng)時(shí)自己開發(fā)的三菱PLCD寄存器調(diào)試程序。
備注:該調(diào)試工具僅支持xp系統(tǒng)
PLC-串口通信實(shí)例
1、三菱PLC與計(jì)算機(jī)之間通信協(xié)議
串行通信是指外設(shè)和計(jì)算機(jī)間使用一根數(shù)據(jù)信號(hào)線一位一位地傳輸數(shù)據(jù),每一位數(shù)據(jù)都占據(jù)一個(gè)固定的時(shí)間長(zhǎng)度。“串行”是指外設(shè)與接口電路之間的信息傳送方式,CPU與接口之間仍按并行方式工作。串行通信的四個(gè)重要參數(shù):波特率(衡量通信速度的參數(shù))、奇偶校驗(yàn)位(一種簡(jiǎn)單的檢錯(cuò)方式)、數(shù)據(jù)位(衡量通信中實(shí)際數(shù)據(jù)位的參數(shù))和停止位(表示單個(gè)數(shù)據(jù)包的最后一位)。
(1)三菱FX2N系列通信數(shù)據(jù)幀格式
FX2N系列的PLC與計(jì)算機(jī)之間的通信采用RS-232C標(biāo)準(zhǔn),其傳輸速率一般設(shè)為9 600 bps,實(shí)際傳輸過程還可設(shè)其它,比如115 200 bps等。奇偶校驗(yàn)位采用偶校驗(yàn)。數(shù)據(jù)以幀為單位發(fā)送和接收。一個(gè)多字符幀由起始字元、命令號(hào)碼、元件首地址、結(jié)束字元、和校驗(yàn)五部分組成,其中和校驗(yàn)值是將命令碼STX—ETX之間的字符的ASCII碼(十六進(jìn)制數(shù))相加,取得所得和的最低二位數(shù)。STX和ETX分別表示該字符幀的起始標(biāo)志和結(jié)束標(biāo)志。
起始字元(STX):ASCII碼的起始字元STX對(duì)應(yīng)的16進(jìn)制數(shù)位0x02。無論命令信息還是回應(yīng)信息,它們的起始字元均為STX,接收方以此來判知傳輸資料的開始;
命令號(hào)碼:為兩位16進(jìn)制數(shù)。所謂命令號(hào)碼是指上位機(jī)要求下位機(jī)所執(zhí)行的動(dòng)作類別,例如要求讀取或?qū)懭雴吸c(diǎn)狀態(tài)、寫入或讀取暫存器資料、強(qiáng)制設(shè)定、運(yùn)行、停止等。在回應(yīng)信息中,下位機(jī)會(huì)將上位機(jī)接收到的命令號(hào)碼隨同其它信息一同發(fā)送給上位機(jī);
元件首地址:對(duì)應(yīng)要操作的元件的相應(yīng)的地址。如從D123單元中讀取數(shù)據(jù)時(shí),要把它對(duì)應(yīng)的地址:0x10F6發(fā)送給PLC;
元件個(gè)數(shù):一次讀取位元件或字元件的數(shù)量;
結(jié)束字元(ETX):ASCII碼的結(jié)束字元ETX對(duì)應(yīng)的16進(jìn)制數(shù)為0x03。無論命令信息還是回應(yīng)信息,它們的結(jié)束字元均為ETX,接收方以此來判知此次通訊已結(jié)束;
校驗(yàn)碼(Checksum):校驗(yàn)碼是將STX—ETX之間的ASCII字元的16進(jìn)制數(shù)值以“LRC(Longitudinal Redundancy Check)”法計(jì)算出1個(gè)Byte長(zhǎng)度(兩個(gè)16進(jìn)制數(shù)值00-FFH)的校驗(yàn)碼。當(dāng)下位機(jī)接收到信息后,用同樣的方法計(jì)算出接收信息的校驗(yàn)碼,如果兩個(gè)校驗(yàn)碼相同,則說明傳送正確。
(2)三菱FX2N系列通信命令
FX2N系列PLC有4個(gè)通信命令,它們是讀命令(30H)、寫命令(31 H)、強(qiáng)制通命令(37H)、強(qiáng)制斷命令(38H)。
(3)三菱FX2N系列通信控制字符
ENQ(ASCII代碼05H):計(jì)算機(jī)向PLC發(fā)送請(qǐng)求;
ACK(ASCII代碼06H):PLC對(duì)ENQ的確定回答;NAK(ASCII代碼15H):PLC對(duì)ENQ的否認(rèn)回答;
STX(ASCII代碼02H):報(bào)文開始;
ETX(ASCII代碼02H):報(bào)文結(jié)束。
(4)FX2N系列設(shè)備地址
①讀寫時(shí)的軟設(shè)備地址
S0-S7:0000H;X0-X7:0080H;Y0-Y7:00AOH;TO-T7:00COH;M0-M7:0100H;CO-C7:01COH;DO-D7:1000H
②置位/復(fù)位時(shí)的軟設(shè)備地址
S0-S7:0000H;X0-X7:0400H;Y0-Y7:0500H;TO-T7:0600H:MO-M7: 0800H;CO-C7:0E00H;DO-D7:0100H
③傳輸過程
PC機(jī)與FX系列PLC之間采用應(yīng)答方式通信,傳輸出錯(cuò)則組織重發(fā)。其傳輸過程如圖1所示。
PLC根據(jù)PC機(jī)的命令,在每個(gè)循環(huán)掃描結(jié)束處的END語(yǔ)句后組織自動(dòng)應(yīng)答,無需用戶在PLC一方編寫程序。
2、系統(tǒng)功能設(shè)計(jì)
系統(tǒng)主要實(shí)現(xiàn)PLC與計(jì)算機(jī)的通訊,具體主要完成PC機(jī)指令下傳、監(jiān)測(cè)PLC狀態(tài)、接收PLC信息等功 能。系統(tǒng)組成:小型PLC一臺(tái)、RS232串口、編程電纜、通訊界面。主操作界面在完成系統(tǒng)功能的前提下,力求明了直觀,操作簡(jiǎn)單靈活方便。系統(tǒng)以VC++6.0為平臺(tái),設(shè)計(jì)的界面如圖2所示。
本程序設(shè)計(jì)了四個(gè)串口可供選擇,只有在選擇串口之后才可進(jìn)行“打開串口,關(guān)閉串口”的操作,當(dāng)打開串口以后,就可以對(duì)PLC進(jìn)行相應(yīng)的操作了,為了使界面整潔干凈,特別設(shè)計(jì)了“清空發(fā)送區(qū)”和“清空接收區(qū)”選項(xiàng),當(dāng)發(fā)送數(shù)據(jù)和接收數(shù)據(jù)放滿編輯框時(shí)只需點(diǎn)擊這兩個(gè)按鈕,數(shù)據(jù)就會(huì)清空。且實(shí)現(xiàn)代碼相當(dāng)簡(jiǎn)單,m_sSend.Empty()、m_sReceive.Empty()就可輕松實(shí)現(xiàn)這一任務(wù)。
PC機(jī)與PLC的通訊程序流程圖如圖3所示。
系統(tǒng)通信控制程序采用了MSComm控件。此控件提供了兩種通信方法:①文件驅(qū)動(dòng),即用MSComm控件的OnComm文件捕獲并處理通信事件和錯(cuò)誤,它是處理串行端口交互作用的一種非常有效的方法;②查詢方式,通過查詢串口屬性來獲得事件和錯(cuò)誤,實(shí)質(zhì)上還是屬于事件驅(qū)動(dòng),但在有些情況下顯得更為便捷。MSComm6.0控件的屬性:①CommPort,設(shè)置或返回通信端口號(hào);②Settings,以字符串的形式設(shè)置或返回波特率、奇偶校驗(yàn)、數(shù)據(jù)位和停止位;③PortOpen,設(shè)置或返回通信端口的狀態(tài),也可以打開和關(guān)閉端口;④Input,返回和刪除接收緩沖區(qū)中的字符;⑤InputMode,設(shè)置或返回Input屬性取回的數(shù)據(jù)的類型,數(shù)據(jù)取回的形式為字符串或二進(jìn)制數(shù)據(jù)的數(shù)組;⑥CommEvent返回最近的通信事件或錯(cuò)誤的數(shù)字代碼,通信程序設(shè)計(jì)時(shí)可以根據(jù)該屬性值執(zhí)行不同的操作,在運(yùn)行時(shí)為只寫;⑦Output,將字符串寫入發(fā)送緩沖區(qū)。
MSComm6.0控件只有一個(gè)事件,即Oncomm事件。在通信時(shí)如果發(fā)生錯(cuò)誤或者事件,將會(huì)引發(fā)Oneomm事件并且改變其屬性值,通過GetCommEvent()可獲得Oncomm產(chǎn)生事件或錯(cuò)誤的代碼。在與PLC進(jìn)行通信的過程中,使用MSComm6.0控件可以自動(dòng)完成PLC對(duì)計(jì)算機(jī)發(fā)送信息的接收,最終實(shí)現(xiàn)PC機(jī)對(duì)PLC的狀態(tài)檢測(cè)。
軟件實(shí)現(xiàn)過程:FX2N系列的PLC與計(jì)算機(jī)之間的通信采用RS-232C標(biāo)準(zhǔn),其傳輸速率固定為9 600bps,奇偶校驗(yàn)位采用偶校驗(yàn)。數(shù)據(jù)以幀為單位發(fā)送和接收。PC機(jī)向PLC中寫數(shù)據(jù)時(shí)首先需對(duì)串口進(jìn)行初始化,并對(duì)波特率、校驗(yàn)位等進(jìn)行設(shè)置,然后根據(jù)通信協(xié)議對(duì)PLC進(jìn)行相應(yīng)的讀寫、復(fù)位、置位等操作,PLC根據(jù)PC機(jī)送來的控制字進(jìn)行相應(yīng)的操作。數(shù)據(jù)發(fā)送,采用專用發(fā)送指令XMT TABLE,CommPort,其中TABLE為發(fā)送緩沖區(qū)的首地址,首地址中保存要發(fā)送的字節(jié)數(shù),即數(shù)據(jù)長(zhǎng)度,最大為255,其后的地址中保存要發(fā)送的數(shù)據(jù),CommPort指定用于發(fā)送的端口。對(duì)于數(shù)據(jù)接收,使用接收指令RCV TABLE,CommPort,接收指令激活初始化或結(jié)束接收信息,通過制定端口接收信息并存儲(chǔ)于數(shù)據(jù)緩沖區(qū)中,數(shù)據(jù)緩沖區(qū)的第一個(gè)數(shù)據(jù)指明了接收的字節(jié)數(shù)。
3、系統(tǒng)功能驗(yàn)證
將計(jì)算機(jī)用通訊電纜與PLC相連后,首先發(fā)送請(qǐng)求05H以后,驗(yàn)證計(jì)算機(jī)與PLC是否可以正常通信,接收區(qū)顯示06,表示PLC對(duì)ENQ的確定回答,即PLC已準(zhǔn)備好,可以進(jìn)行下面的操作,具體如圖4和圖5。
這里主要對(duì)PLC讀值功能進(jìn)行驗(yàn)證。讀操作命令格式如下:
STX—CMD0一數(shù)據(jù)段一ETX—SUMH—SUML
在按上述命令格式發(fā)送相應(yīng)的代碼后,就可直接讀取PLC響應(yīng)的信息了。響應(yīng)信息格式如下:
STX—DATA—ETX—SUMH—SUML
圖6和圖7分別是對(duì)PLC進(jìn)行讀值驗(yàn)證時(shí)發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的顯示。
其中接收數(shù)據(jù)顯示中的023030033633,是對(duì)x軟地址值(0080H)讀取后接收到的數(shù)據(jù)。具體算法如下:
nSUMLx=(0X30+0X30+0X03)%16=3《9,
nSUMHx=((0X30+0X30+0X03)%256)/16=6《9,
nSUMLx=0x30+nSUMLYl=0X33,
nSUMHx=0X30+nSUMHY2=0X36
故,轉(zhuǎn)變成兩字節(jié)ASCII代碼SUMLx=33;SUMHx=36。
理論分析和實(shí)際操作的結(jié)果是一致的,即證明了本設(shè)計(jì)是準(zhǔn)確無誤的。
評(píng)論
查看更多