一.前言 当晶体管输出的PLC间的通讯口不足时,可考虑使用IO口建立点对点通讯。笔者曾见过一种第三方西门子200的IO拓展模块,就是使用这种方式和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; 四.运行演示 硬件接线:A机Y0与B机X0连接,A机Y1与B机X1连接,B机Y0与A机X0连接,B机Y1与A机X1连接。 将PLC处于停止模式,把程序分别下载到A机和B机,然后切换到运行模式。对A机发送变量赋值8102,对B机发送变量赋值2018,通过程序监控截图可以看到数据传输成功。 A机程序运行监控 B机程序运行监控 五.后语 本方法试图在物理层上建立PLC的IO与外界通讯的途径,仅供爱好者参考。 数据层以上请参考标准的串行通讯,比如对于通讯过程中发生的干扰,由于缺少内建的位滤波,可在帧尾增加效验和使用帧重发机制。 参考文献 1.电子工程世界论坛用户"totopper"的“用IO口使PLC通讯(上)”和“用IO口使PLC通讯(下)”讨论帖 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |