栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

西门子RS485自由口通信Modbus RTU 通信协议

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

西门子RS485自由口通信Modbus RTU 通信协议

部分西门子的RS485模块不能使用西门子自带的Modbus RTU通信指令,需要通过自己拼接、发送、接收和解析报文完成Modbus RTU通信。通过多次尝试完成该功能:

#R_TRIG_Instance(CLK := #触发,
                 Q => #触发上升沿);
IF #触发上升沿 THEN
    // 触发后先将读取完成复位
    #读写完成 := 0;
END_IF;
CASE #功能码 OF
    1:  // 线圈状态读取
        ;
    2:  // 离散量输入读取
        ;
    3:  //多个保持寄存器读取
        //拼接报文
        #临时报文[0] := UINT_TO_BYTE(#从站号);//从站地址
        #临时报文[1] := UINT_TO_BYTE(16#03);//功能码
        #临时报文[2] := UINT_TO_BYTE(#起始地址 / 256);//起始寄存器高位
        #临时报文[3] := UINT_TO_BYTE(#起始地址 MOD 256);//起始寄存器低位
        #临时报文[4] := UINT_TO_BYTE(#读写长度 / 256);//寄存器数量高位
        #临时报文[5] := UINT_TO_BYTE(#读写长度 MOD 256);//寄存器数量低位
        IF #读写长度 <= 0 THEN
            // 如果写入长度为0报错
            #校验信息 := 16#6005;
            RETURN;
        END_IF;
        "CRC16校验"(校验字节数 := 6,
                  校验数据 := #临时报文);
        //截取报文
        #截取结果 := MOVE_BLK_VARIANT(SRC := #临时报文,
                                  COUNT := 8,
                                  SRC_INDEX := 0,
                                  DEST_INDEX := 0,
                                  DEST => #读取发送报文);

        #Send_P2P_Instance(REQ := #触发,
                           "PORT" := #硬件标识符,
                           BUFFER := #读取发送报文,
                           LENGTH := 8,
                           DONE => #发送完成,
                           ERROR => #发送错误,
                           STATUS => #发送信息);
        IF #发送错误 THEN
            // 发送错误
            #校验信息 := #发送信息;
        END_IF;
        #截取结果 := MOVE_BLK_VARIANT(SRC := #零数组,
                                  COUNT := 105,
                                  SRC_INDEX := 0,
                                  DEST_INDEX := 0,
                                  DEST => #读取接收报文);
        //接收报文
        #Receive_P2P_Instance("PORT" := #硬件标识符,
                              BUFFER := #读取接收报文,
                              NDR => #接收完成,
                              ERROR => #接收错误,
                              STATUS => #接收信息,
                              LENGTH => #接收长度);
        IF #接收错误 THEN
            // 接收错误
            #校验信息 := #接收信息;
            RETURN;
        END_IF;
        
        #截取结果 := MOVE_BLK_VARIANT(SRC := #读取接收报文,
                                  COUNT := #接收长度 - 2,
                                  SRC_INDEX := 0,
                                  DEST_INDEX := 0,
                                  DEST => #校验报文);

        //校验报文
        IF #读取接收报文[0] <> 0 THEN
            // Statement section IF
            IF #读取接收报文[0] <> UINT_TO_BYTE(#从站号) THEN
                // 接收报文从站号错误
                #校验信息 := 16#6001;
                RETURN;
            END_IF;
            IF #读取接收报文[1] <> UINT_TO_BYTE(16#03) THEN
                // 接收报文功能码错误
                #校验信息 := 16#6002;
                RETURN;
            END_IF;
            IF #读取接收报文[2] <> UINT_TO_BYTE(#读写长度 * 2) THEN
                // 接收报文字节计数错误
                #校验信息 := 16#6003;
                RETURN;
            END_IF;
            //CRC校验发过来的数据
            "CRC16校验"(校验字节数 := #接收长度 - 2,
                      校验数据 := #校验报文);
            IF (#读取接收报文[#接收长度 - 2] <> #校验报文[#接收长度 - 2]) OR (#读取接收报文[#接收长度 - 1] <> #校验报文[#接收长度 - 1]) THEN
                // 接收报文字节校验码错误
                #校验信息 := 16#6004;
                RETURN;
            END_IF;
            #校验信息 := 16#6000;
            #读写完成 := 1;
            //数据解析
            #循环计数 := 0;
            FOR #循环计数 := 0 TO #读写长度 - 1 BY 1 DO
                "test".cgc := #读写长度;
                // 将高位字节和低位字节
                #读写数据[#循环计数].%B1 := #读取接收报文[#循环计数 * 2 + 3];
                #读写数据[#循环计数].%B0 := #读取接收报文[#循环计数 * 2 + 1 + 3];
            END_FOR;
            FOR #循环计数 := #读写长度 TO 99 BY 1 DO
                // 将其余数据未读取数据清零
                #读写数据[#循环计数] := 16#0000;
            END_FOR;
        END_IF;
    4:  //输入寄存器读取
        ;
    5:  // 单个线圈写入
        ;
    6:  // 单个保持寄存器写入
        ;
    15:  // 多个线圈写入
        ;
    16:  // 多个保持寄存器写入
        //拼接报文
        #临时报文[0] := UINT_TO_BYTE(#从站号);//从站地址
        #临时报文[1] := UINT_TO_BYTE(16#10);//功能码
        #临时报文[2] := UINT_TO_BYTE(#起始地址 / 256);//起始寄存器高位
        #临时报文[3] := UINT_TO_BYTE(#起始地址 MOD 256);//起始寄存器低位
        #临时报文[4] := UINT_TO_BYTE(#读写长度 / 256);//寄存器数量高位
        #临时报文[5] := UINT_TO_BYTE(#读写长度 MOD 256);//寄存器数量低位
        #临时报文[6] := UINT_TO_BYTE(#读写长度 * 2);//寄存器数量低位
        IF #读写长度 <= 0 THEN
            // 如果写入长度为0报错
            #校验信息 := 16#6005;
            RETURN;
        END_IF;
        
        //数据解析
        #循环计数 := 0;
        FOR #循环计数 := 0 TO #读写长度 - 1 BY 1 DO
            // 将写入的Word转换为两个BYTE
            #临时报文[#循环计数 * 2 + 7] := #读写数据[#循环计数].%B1;
            #临时报文[#循环计数 * 2 + 8] := #读写数据[#循环计数].%B0;
        END_FOR;
        "CRC16校验"(校验字节数 := 7 + #读写长度 * 2,
                  校验数据 := #临时报文);
        //截取报文
        #截取结果 := MOVE_BLK_VARIANT(SRC := #临时报文,
                                  COUNT := #读写长度 * 2 + 9,
                                  SRC_INDEX := 0,
                                  DEST_INDEX := 0,
                                  DEST => #写入发送报文);

        #Send_P2P_Instance(REQ := #触发,
                           "PORT" := #硬件标识符,
                           BUFFER := #写入发送报文,
                           LENGTH := #读写长度 * 2 + 9,
                           DONE => #发送完成,
                           ERROR => #发送错误,
                           STATUS => #发送信息);
        IF #发送错误 THEN
            // 发送错误
            #校验信息 := #发送信息;
        END_IF;
        //接收报文
        #Receive_P2P_Instance("PORT" := #硬件标识符,
                              BUFFER := #写入接收报文,
                              NDR => #接收完成,
                              ERROR => #接收错误,
                              STATUS => #接收信息,
                              LENGTH => #接收长度);
        IF #接收错误 THEN
            // 接收错误
            #校验信息 := #接收信息;
            RETURN;
        END_IF;
        
        #截取结果 := MOVE_BLK_VARIANT(SRC := #写入接收报文,
                                  COUNT := #接收长度 - 2,
                                  SRC_INDEX := 0,
                                  DEST_INDEX := 0,
                                  DEST => #校验报文);

        IF #写入接收报文[0] <> 0 THEN
            // 校验报文
            IF #写入接收报文[0] <> UINT_TO_BYTE(#从站号) THEN
                // 接收报文从站号错误
                #校验信息 := 16#6001;
                RETURN;
            END_IF;
            IF #写入接收报文[1] <> UINT_TO_BYTE(16#10) THEN
                // 接收报文功能码错误
                #校验信息 := 16#6002;
                RETURN;
            END_IF;
            IF (#写入接收报文[2] <> UINT_TO_BYTE(#起始地址 / 256)) OR (#写入接收报文[3] <> UINT_TO_BYTE(#起始地址 MOD 256)) THEN
                // 接收报文起始地址错误
                #校验信息 := 16#6003;
                RETURN;
            END_IF;
            IF (#写入接收报文[4] <> UINT_TO_BYTE(#读写长度 / 256)) OR (#写入接收报文[5] <> UINT_TO_BYTE(#读写长度 MOD 256)) THEN
                // 接收报文数量错误
                #校验信息 := 16#6003;
                RETURN;
            END_IF;
            
            #截取结果 := MOVE_BLK_VARIANT(SRC := #写入接收报文,
                                      COUNT := #接收长度 - 2,
                                      SRC_INDEX := 0,
                                      DEST_INDEX := 0,
                                      DEST => #校验报文);

            //CRC校验发过来的数据
            "CRC16校验"(校验字节数 := #接收长度 - 2,
                      校验数据 := #校验报文);
            IF (#写入接收报文[#接收长度 - 2] <> #校验报文[#接收长度 - 2]) OR (#写入接收报文[#接收长度 - 1] <> #校验报文[#接收长度 - 1]) THEN
                // 接收报文字节校验码错误
                #校验信息 := 16#6004;
                RETURN;
            END_IF;
            #校验信息 := 16#6000;
            #读写完成 := 1;
        END_IF;
    ELSE  // Statement section ELSE
        ;
END_CASE;

利用西门子博图创建了一个FB块,采用西门子SCL语言编写,在编写过程中有几个问题需要注意:
1、使用的指令:

2、在调用这两个指令时我才用的是多重实例,同时该指令在FB块中多次使用也不需要再创建背景数据块了,否则会出错。


在FB块中我只有一个SEND和RECEIVE的背景数据块
3、Send的触发必须要使用上升沿触发否则会报错。


FB块的变量定义

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/679118.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号