[汇川] PLC使用IO口通讯ST代码实现

[复制链接]
查看127 | 回复0 | 2025-4-9 07:34:34 | 显示全部楼层 |阅读模式

.前言

当晶体管输出的PLC间的通讯口不足时,可考虑使用IO口建立点对点通讯。笔者曾见过一种第三方西门子200IO拓展模块,就是使用这种方式和CPU主机连接。不过现在技术更成熟了,第三方拓展模块都能直接兼容原机拓展接口了。

1.1 优点

PLC的IO用于通讯时,省去了通讯专用模块,或者不需要为使用编程口尤其是OMRON不敞开的协议而烦恼,IO口号任意指定,更无PLC互联的品牌要求,星型外接的数量可任意增加。

1.2 局限

PLC由于扫描周期相对较长,所以通讯速度有限,在成本允许或者数据流量大的场合,还是应该采用PLC厂商各自提供的通讯途径。

继电器输出的PLC,由于通讯高速切换输出,切不可使用本方法,否则PLC会在短时间内损坏。

二.IO通讯原理

串行通讯是按时间序列送出一串0/1编码,接收方需要进行同步以正确接收和解释代码来完成通讯。

PLC由于扫描周期长、定时不够精确而不宜采用时间同步的方法。数据传输除需要一根数据线外还需要一根同步信号线,用于告诉发送方新数据收到,否则将无法分辨连续发送的00…或者11…数据。

2.1.   PLC-PLC

2.1.1. 双向(2-2线)

以发送4位数据为例,例中A、B机Q0.1口做数据口,Q0.0口做同步口。A机为主机,发送1001(9),从机B发送0101(5),先低位后高位。

1)      A机发送启动同步A-Q0.0=1;

2)      B收到同步,也响应发送同步B-Q0.0=1;

3)     A机收到B机同步响应后,发送数据A-Q0.1=1,并交变同步A-Q0.0=0

4) B机收到A机同步交变,记录数据A0=1,并发送数据B-Q0.1=1,同时交变同步B-Q0.0=0;

5) A机收到B机同步交变,记录数据B0=1,并发送数据A-Q0.1=0,同时交变同步A-Q0.0=1;

6) 4,B机记录数据A1=0,发送0,并交变B-Q0.0=1;

7) A机记录数据B1=0,发送0,并交变A-Q0.0=0;

8) ~10)同上,A机发送A2=0,A3=1,B机发送B2=1,B3=0;

11) A机交变同步,完成通讯;

12)B机收到A完成信号,也交变同步,结束通讯。

此方法通讯2周期1位,速度较快,但通讯的帧间隔需要用一定延时来保证后续帧的同步,连绵不断是无法确定当前传输的数据位号的。

通讯的同步信号设计应偶数次交变,以返回初始的状态,可以不要起始信号而第一次就发数据和同步信号,但一定要有结束同步,否则最后1位收完,对方机不知道,会处在多余的等待状态。

编程时因注意由于PLC的2个输出不会严格同步,应先建立数据位,再交变同步,以免对方误收。

2.1.2. 单向(1-2线)

单向通讯较双向为简单,接收机只要交变送出确认信号即可,因此发送机需要2输出、1输入,接收机反之。此方法也是2周期1位的速度。

2.2.   快慢机通讯

由于PLC速度较慢(10ms级),当外接快速的数据设备(如MCU、DSP等μs级)时,可只用更少的输入和输出来完成通讯过程。原理籍于PLC一经送出,在下一个扫描周期的时候对方早已收到并准备了新数据,省去了确认对方是否收到这一步骤,所以PLC每次扫描在发出交变确认信号时均能收到一个新数据,步骤省却的同时也增加了通讯速度。PLC发出交变同步是让接收机甄别出发送的位来。

2.2.1. 双向协议(1-2线)

PLC用1个输入和2个输出, PLC先接收对方数据,再用两个输出表达同步和数据,在下一个扫描周期时也是先接收再发送数据和交变同步,每个周期交换1位数据。

2.2.2 单向协议(1-1线)

PLC作接收机,举例2位数据接收过程如下:

只使用1-1线无法实现快慢机双向通讯。

.双向(2-2线)PLC程序实现

硬件使用汇川Easy501 PLC,编程软件使用汇川AutoShop,使用ST语言编写。程序功能为AB机之间发送和接收两个字节的数据。

A机程序

//IO通讯程序_A机

CASE iASteps OF

K0://建立同步,建立成功后A同步为1

yASendSyncPort := TRUE;

IF xARecvSyncPort THEN

D1 := K0;

D0 := iASendWord;

iASteps :=  iASteps + K1;

END_IF;

K1://发送第一个位

yASendDataPort := D0.0;

yASendSyncPort := FALSE;

IF NOT xARecvSyncPort THEN

D1.0 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K2://发送第二个位

yASendDataPort := D0.1;

yASendSyncPort := TRUE;

IF xARecvSyncPort THEN

D1.1 := xARecvDataPort;        

iASteps :=  iASteps + K1;

END_IF;

K3:

yASendDataPort := D0.2;

yASendSyncPort := FALSE;

IF NOT xARecvSyncPort THEN        

D1.2 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K4:

yASendDataPort := D0.3;

yASendSyncPort := TRUE;

IF xARecvSyncPort THEN

D1.3 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K5:

yASendDataPort := D0.4;

yASendSyncPort := FALSE;

IF NOT xARecvSyncPort THEN

D1.4 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K6:

yASendDataPort := D0.5;

yASendSyncPort := TRUE;

IF xARecvSyncPort THEN

D1.5 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K7:

yASendDataPort := D0.6;

yASendSyncPort := FALSE;        

IF NOT xARecvSyncPort THEN

D1.6 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K8:

yASendDataPort := D0.7;

yASendSyncPort := TRUE;        

IF xARecvSyncPort THEN

D1.7 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K9:

yASendDataPort := D0.8;

yASendSyncPort := FALSE;        

IF NOT xARecvSyncPort THEN

D1.8 := xARecvDataPort;        

iASteps :=  iASteps + K1;

END_IF;

K10:

yASendDataPort := D0.9;

yASendSyncPort := TRUE;        

IF xARecvSyncPort THEN

D1.9 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K11:

yASendDataPort := D0.10;

yASendSyncPort := FALSE;        

IF NOT xARecvSyncPort THEN

D1.10 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K12:

yASendDataPort := D0.11;

yASendSyncPort := TRUE;        

IF xARecvSyncPort THEN

D1.11 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K13:

yASendDataPort := D0.12;

yASendSyncPort := FALSE;        

IF NOT xARecvSyncPort THEN

D1.12 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K14:

yASendDataPort := D0.13;

yASendSyncPort := TRUE;        

IF xARecvSyncPort THEN

D1.13 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K15:

yASendDataPort := D0.14;

yASendSyncPort := FALSE;        

IF NOT xARecvSyncPort THEN

D1.14 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K16:

yASendDataPort := D0.15;

yASendSyncPort := TRUE;        

IF xARecvSyncPort THEN

D1.15 := xARecvDataPort;

iASteps :=  iASteps + K1;

END_IF;

K17://开始下一轮通讯

yASendSyncPort := FALSE;        

IF NOT xARecvSyncPort THEN

yASendSyncPort := FALSE;

iARecvWord := d1;

iASteps :=  K0;

END_IF;

ELSE

;

END_CASE;

//IO映射

Y0 := yASendSyncPort;

Y1 := yASendDataPort;

xARecvSyncPort := X0;

xARecvDataPort := X1;

B机程序

//IO通讯程序_B

CASE iBSteps OF

K0://建立同步,建立成功后B同步为1

IF xBRecvSyncPort THEN

yBSendSyncPort := TRUE;        

D1 := K0;

D0 := iBSendWord;

iBSteps := iBSteps + K1;

END_IF;

K1://等待A机同步信号为0,开始接收数据,并交变B机同步信号

IF NOT xBRecvSyncPort THEN

D1.0 := xBRecvDataPort;

yBSendDataPort := D0.0;

yBSendSyncPort := FALSE;        

iBSteps := iBSteps + K1;

END_IF;

K2://接收第二个位

IF xBRecvSyncPort THEN

yBSendDataPort := D0.1;

D1.1 := xBRecvDataPort;        

yBSendSyncPort := TRUE;

iBSteps := iBSteps + K1;

END_IF;

K3:

IF NOT xBRecvSyncPort THEN

yBSendDataPort := D0.2;

D1.2 := xBRecvDataPort;        

yBSendSyncPort := FALSE;

iBSteps := iBSteps + K1;                

END_IF;

K4:

IF xBRecvSyncPort THEN

yBSendDataPort := D0.3;

D1.3 := xBRecvDataPort;        

yBSendSyncPort := TRUE;

iBSteps := iBSteps + K1;                

END_IF;

K5:

IF NOT xBRecvSyncPort THEN

yBSendDataPort := D0.4;

D1.4 := xBRecvDataPort;        

yBSendSyncPort := FALSE;

iBSteps := iBSteps + K1;                

END_IF;

K6:

IF xBRecvSyncPort THEN

yBSendDataPort := D0.5;

D1.5 := xBRecvDataPort;        

yBSendSyncPort := TRUE;

iBSteps := iBSteps + K1;                

END_IF;

K7:

IF NOT xBRecvSyncPort THEN

yBSendDataPort := D0.6;

D1.6 := xBRecvDataPort;        

yBSendSyncPort := FALSE;

iBSteps := iBSteps + K1;                

END_IF;

K8:

IF xBRecvSyncPort THEN

yBSendDataPort := D0.7;

D1.7 := xBRecvDataPort;        

yBSendSyncPort := TRUE;

iBSteps := iBSteps + K1;                

END_IF;

K9:

IF NOT xBRecvSyncPort THEN

yBSendDataPort := D0.8;

D1.8 := xBRecvDataPort;        

yBSendSyncPort := FALSE;

iBSteps := iBSteps + K1;                

END_IF;

K10:

IF xBRecvSyncPort THEN

yBSendDataPort := D0.9;

D1.9 := xBRecvDataPort;        

yBSendSyncPort := TRUE;

iBSteps := iBSteps + K1;                

END_IF;

K11:

IF NOT xBRecvSyncPort THEN

yBSendDataPort := D0.10;

D1.10 := xBRecvDataPort;        

yBSendSyncPort := FALSE;

iBSteps := iBSteps + K1;                

END_IF;

K12:

IF xBRecvSyncPort THEN

yBSendDataPort := D0.11;

D1.11 := xBRecvDataPort;        

yBSendSyncPort := TRUE;

iBSteps := iBSteps + K1;                

END_IF;

K13:

IF NOT xBRecvSyncPort THEN

yBSendDataPort := D0.12;

D1.12 := xBRecvDataPort;        

yBSendSyncPort := FALSE;

iBSteps := iBSteps + K1;                

END_IF;

K14:

IF xBRecvSyncPort THEN

yBSendDataPort := D0.13;

D1.13 := xBRecvDataPort;        

yBSendSyncPort := TRUE;

iBSteps := iBSteps + K1;                

END_IF;

K15:

IF NOT xBRecvSyncPort THEN

yBSendDataPort := D0.14;

D1.14 := xBRecvDataPort;        

yBSendSyncPort := FALSE;

iBSteps := iBSteps + K1;                

END_IF;

K16:

IF xBRecvSyncPort THEN

yBSendDataPort := D0.15;

D1.15 := xBRecvDataPort;        

yBSendSyncPort := TRUE;

iBSteps := iBSteps + K1;                

END_IF;

K17://开始下一轮通讯

IF NOT xBRecvSyncPort THEN

yBSendSyncPort := FALSE;

iBRecvWord := D1;

iBSteps := K0;                

END_IF;

ELSE

;

END_CASE;

//IO映射

Y0 := yBSendSyncPort;

Y1 := yBSendDataPort;

xBRecvSyncPort := X0;

xBRecvDataPort := X1;

.运行演示

硬件接线:AY0BX0连接,AY1BX1连接,BY0AX0连接,BY1AX1连接。

PLC处于停止模式,把程序分别下载到A机和B机,然后切换到运行模式。对A机发送变量赋值8102,对B机发送变量赋值2018,通过程序监控截图可以看到数据传输成功。

A机程序运行监控

B机程序运行监控

五.后语

本方法试图在物理层上建立PLC的IO与外界通讯的途径,仅供爱好者参考。

数据层以上请参考标准的串行通讯,比如对于通讯过程中发生的干扰,由于缺少内建的位滤波,可在帧尾增加效验和使用帧重发机制。

参考文献

1.电子工程世界论坛用户"totopper"的“用IO口使PLC通讯(上)”和“用IO口使PLC通讯(下)”讨论帖


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册哦

x
您需要登录后才可以回帖 登录 | 注册哦

本版积分规则