PLC, HMI, SCADA, INVERTERS, TUTORIALS, INDUSTRIAL AUTOMATION

09:18
Danfoss VLT Modbus RTU TCP example SIEMENS S7-1500 TIA Portal CM PtP RS422/485 BA_1

Danfoss Modbus RTU TCP example SIEMENS TIA Portal

danfoss_V15.zip - Example SIEMENS TIA Portal V15

danfoss_TIA_14_SP1.zap14 - Example SIEMENS TIA Portal V14 SP1 

Danfoss Modbus RTU TCP example SIEMENS TIA Portal

6ES7 540-1AB00-0AA0 SIEMENS S7-1500 TIA Portal CM PtP RS422/485 BA_1


mb_internal_f FC1


// Internal function - master footer

IF #mb_query.stat.done OR #mb_query.stat.error THEN
    // Make sure that a query is not exec. twice.
    IF #data_len <> 0 THEN
        #mb_query.z.q.data_len := #data_len;
        #data_len := 0;
    END_IF;
    
    IF NOT #mb_query.z.run.write THEN // (read)
        #mb_query.z.buffer := #buffer.buffer;
    END_IF;
    
    // Communication error
    // (Handled different from other errors)
    CASE WORD_TO_UINT(#mb_query.stat.status) OF
        16#80C8, // Timeout
        16#8380: // Bad CRC 
            #comm_error := true;
    END_CASE;
END_IF;

#mb_query.stat.error_comm := #mb_query.stat.error AND #comm_error;


mb_internal_h FC2


// Internal function - master header

IF #mb_query.z.run.insert2 THEN
    #mb_query.z.run.insert2 := false;
    #mode := 0;
    #data_len := #mb_query.z.q.data_len;
    // mb_master use modicon convention address.
    CASE #mb_query.z.q.mode OF
        0..2: // Read and write
            #mode := #mb_query.z.q.mode;
            #data_addr := #mb_query.z.q.data_addr;
        101:  // Read output bits
            #data_addr := 1 + #mb_query.z.q.data_addr;
        102:  // Read input bits
            #data_addr := 10001 + #mb_query.z.q.data_addr;
        103:  // Read holding reg.
            #data_addr := 400001 + #mb_query.z.q.data_addr;
        104:  // Read input word
            IF #mode104 THEN
                #mode := 104;
                #data_addr := #mb_query.z.q.data_addr;
            ELSE
                #data_addr := 30001 + #mb_query.z.q.data_addr;
            END_IF;
        105:  // Writing a single output bit
            #mode := 1;
            #data_addr := 1 + #mb_query.z.q.data_addr;
        106:  // Writing a single holding register
            #mode := 1;
            #data_addr := 400001 + #mb_query.z.q.data_addr;
        115: // Writing multiple output bits
            #mode := 2;
            #data_addr := 1 + #mb_query.z.q.data_addr;
        116: // Writing multiple holding registers
            #mode := 2;
            #data_addr := 400001 + #mb_query.z.q.data_addr;
        ELSE // (The else statement may be removed in the future)
            #mode := #mb_query.z.q.mode;
            #data_addr := #mb_query.z.q.data_addr;
    END_CASE;
    IF #mb_query.z.run.write THEN
        #buffer.buffer := #mb_query.z.buffer;
    END_IF;
END_IF;

// Automatically adjust timeout.
#resp_to := SEL(G := #timeout = T#0s,
                IN0 := TIME_TO_UINT(#timeout),
                IN1 := UDINT_TO_UINT(3200000 / #baud));
 


mb_station_block_footer FC5


REGION Recive
    IF #mb_query.z.sb.head_recive THEN
        // If there is a query that bellong to this device that has
        // recvied a resault. (with or without error)
        
        // Default is 1 query for each loop, but can be changed.
        #sb.z.exec_cnt += 1;
        
        // If this is the first succesfylly query after communication error,
        // then log the time this happen. (also exceptions)
        IF #sb.out.error_comm AND (
            #mb_query.stat.done
            OR #mb_query.stat.error AND NOT #mb_query.stat.error_comm)
        THEN
            #e := RD_LOC_T(#sb.log.connected_time);
        END_IF;
        
        IF #mb_query.stat.done THEN // Logging successfully query        
            #sb.log.done_cnt += 1;
            // One successfully query -> reset 
            #sb.out.error_comm := false;
            #sb.z.comm_err_cnt := 0;
            
        ELSIF #mb_query.stat.error THEN // Logging - failed query        
            
            #sb.log.error_status := #mb_query.stat.status;
            #sb.log.error_data_addr := #mb_query.z.sb.recived_data_addr;
            IF #mb_query.stat.error_comm THEN
                #sb.log.error_comm_cnt += 1;
            ELSE
                #sb.log.error_exception_cnt += 1;
            END_IF;
            
            // Increase timeout cnt. if it's a timeout error.
            // if it's not a timeout error then reset the cnt.  
            IF #mb_query.stat.error_comm THEN
                IF #sb.z.comm_err_cnt < 255 THEN
                    #sb.z.comm_err_cnt += 1;
                END_IF;
            ELSE
                #sb.z.comm_err_cnt := 0;
            END_IF;
            
            // The communication_error flag is only set if x repeating timeout
            // errors has occurred.
            #err := #sb.z.comm_err_cnt > #sb.conf.max_comm_error;
            IF #err AND NOT #sb.out.error_comm THEN
                // Log the time, if this is the first error resualting in
                // communication_error. 
                #e := RD_LOC_T(#sb.log.disconnected_time);
            END_IF;
            #sb.out.error_comm := #err;
            
            // A timeout error will not be displayed befor the
            // communication_error flag is set. Other ettors will be
            // diplayed once. (NOT timeout -> normal error)
            #sb.out.error := #sb.out.error OR NOT #mb_query.stat.error_comm
            OR #sb.out.error_comm;
            
            #sb.z.err_hold := #sb.out.error;
        END_IF;
        
        // - If it's this devices that has the retry-token, and a retry has
        //   been executed. Then set the retry_finished flag.
        // - The retry-token will be passed on to the next device with
        //   communication problems when the query loop has reach
        //   the end. (Inside the controller)
        // - The purpose of the flag is make sure that a retry is only done
        //   once for each query loop revolution.
        #mb_query.z.sb.retry_finished :=
        #mb_query.z.sb.did_retry = #mb_query.z.sb.did_cnt
        AND #mb_query.stat.error_comm;
        
        // Only one query for each device for each loop is allowed to be
        // executed. offset_qid is incread by one when a query has executed
        // given last query didn't resualt in a timeout.
        #sb.z.offset_qid += BOOL_TO_UINT(NOT #mb_query.stat.error_comm);
    END_IF;
END_REGION

REGION Middle
    // If all queries for this device has been executed,
    // then reset offset_qid.
    IF #sb.z.offset_qid >
        #mb_query.z.run.qid_cnt - #sb.z.header_qid
    THEN
        #sb.z.offset_qid := 1;
        
        // Also reset error falg
        IF NOT #sb.z.err_hold THEN
            #sb.out.error := false;
        END_IF;
        #sb.z.err_hold := false;
    END_IF;
    
    // If the retry-token point at this device (did_retry=did_cnt), and there is
    // no communication problems then pass on the retry-token to the next device.
    // The retry token tell which device with communication problem that should
    // retry establish communication.
    #mb_query.z.sb.did_retry +=
    BOOL_TO_UINT(#mb_query.z.sb.did_retry = #mb_query.z.sb.did_cnt
                 AND NOT #sb.out.error_comm);
END_REGION


REGION Finnish
    // Several condition that resault in passing on the query-token on to the
    // next device. Skipping all or the rest of the queries for this device:
    // - If this device has one query that have received a result. (One query
    //   from each device for each loop, regardsless successfully or failed
    //   query)
    // - If #station.out.comm_error is set, and isn't this device turn to
    //   try again.
    // - If the device is disabled. (skip all queries)
    #sb.out.finnish := #mb_query.z.sb.head_recive AND (
    #sb.z.exec_cnt >= #sb.conf.exec_x_quries
    OR #mb_query.stat.error_comm)
    
    OR #mb_query.z.sb.head_insert AND (
    #sb.out.error_comm
    AND (#mb_query.z.sb.did_retry <> #mb_query.z.sb.did_cnt
    OR #mb_query.z.sb.retry_finished)
    // (retry_finished will be reset in *_ctrl)
    OR #sb.conf.disable
    OR #sb.conf.exec_x_quries = 0
    OR #sb.conf.read_only AND #mb_query.z.run.write);
    
    IF #sb.out.finnish THEN // Reset
        #mb_query.z.run.insert := true;
        #mb_query.z.run.qid := #mb_query.z.run.qid_cnt + 1;
        #sb.z.exec_cnt := 0;
    END_IF;
END_REGION


mb_station_block_header FC4


#mb_query.data_len := 0; // Default is auto-length.
#mb_query.conf.buffer_handler := true;

// If device blocks are executeded in diffrent order, a wide wind
// up effect will occur, (in same OB) this will prevent that.
IF #mb_query.z.sb.reset THEN
    #sb.z.header_qid := #mb_query.z.run.qid_cnt + 1;
END_IF;

// Device id cnt. The first device has id "1". Increased in every
// mb_device_header, reseted in mb_client_ctrl or mb_master_ctrl.
#mb_query.z.sb.did_cnt += 1;

// mb_query set the same bellow variables to true, at insert or recive.
// This informastion is later used at the footer.
#mb_query.z.sb.head_insert := false;
#mb_query.z.sb.head_recive := false;

// When #mb.mb.q.qid = #mb.mb.run.qid_cnt => "This" device has the
// query-token !
// - If the query-token points at this device header, or it point to a
//   query in the previous device, that is surrounded by a if-statement
//   and the statement is now false.
// - A gid change can only be done if not "busy", this is to avoid a
//   resualt from one query being intercepted by another
IF #mb_query.z.run.qid > #mb_query.z.run.qid_cnt
    AND #mb_query.z.run.qid <= #sb.z.header_qid
    AND NOT #mb_query.z.run.busy
THEN
    // Reset.
    #mb_query.z.run.insert := true;
    
    // At first the offset_qid points at the first query of this
    // device. When it has executed the offset will point at the
    // second query. When all queries has executed the offset will
    // be reset back to the first query. (footer) 
    #mb_query.z.run.qid :=
    #sb.z.header_qid + #sb.z.offset_qid;
END_IF;

// - The code below is reduce the impact of setting a if-statement
//   around queries, by setting the qid_cnt to a fixed value at this 
//   point.
// - mb_device_header will occupy one query-id.  
#sb.z.header_qid :=
MAX(IN1 := #mb_query.z.run.qid_cnt + 1,
    IN2 := #sb.z.header_qid);
#mb_query.z.run.qid_cnt := #sb.z.header_qid;

// *)
 


mb_client_ctrl FB6



#tcon_ip_v4.InterfaceId := #interface;
#tcon_ip_v4.ID := #conn_id;
#tcon_ip_v4.ConnectionType := 11; // tcp
#tcon_ip_v4.ActiveEstablished := true;
#tcon_ip_v4.RemoteAddress.ADDR := #ip_addr;
#tcon_ip_v4.RemotePort := #tcp_port;

// If mb_client does not establish a connection it will not
// throw timeout error. Timeout is handled manually for that
// case. 16#7002: Intermediate call. Connection is being
// established.
#ton_timeout(IN := #client.STATUS = 16#7002,
             PT := #timeout,
             Q => #ton_timeout_q);

IF #mb_query.z.run.insert2 THEN
    #mb_query.z.run.insert2 := false;
    #client.MB_DATA_LEN := #mb_query.z.q.data_len;
END_IF;

#client.MB_Unit_ID := UINT_TO_BYTE(#mb_query.z.q.mb_addr);
#client.Rcv_Timeout :=
UDINT_TO_REAL(TIME_TO_UDINT(#timeout)) * 0.001;
#client.Retries := #conf.retries;
#client(REQ := NOT #client.BUSY
        AND NOT #mb_query.z.run.insert
        AND NOT #client.DISCONNECT
        AND #mb_query.z.run.qid <> 0,
        DISCONNECT := #conf.disconnect
        OR #tdiscon.BUSY
        OR #timeout_state = 1,
        MB_MODE := #mb_query.z.q.mode,
        MB_DATA_ADDR := #mb_query.z.q.data_addr,
        MB_DATA_LEN := #client.MB_DATA_LEN,
        BUSY => #mb_query.z.run.busy,
        DONE => #mb_query.stat.done,
        ERROR => #mb_query.stat.error,
        STATUS => #mb_query.stat.status,
        MB_DATA_PTR := #mb_query.z.buffer,
        CONNECT := #tcon_ip_v4);

IF #client.DONE OR #client.ERROR THEN
    // Make sure that a query is not exec. twice.
    IF #client.MB_DATA_LEN <> 0 THEN
        #mb_query.z.q.data_len := #client.MB_DATA_LEN;
        #client.MB_DATA_LEN := 0;
    END_IF;
    
    // Timeout handeling.
    #mb_query.stat.error_comm := #mb_query.stat.error
    // blocked_proc_timeout or rcv_timeout
    AND (#mb_query.stat.status = 16#818C
    // No response from the server
    OR #mb_query.stat.status = 16#80C8
    OR #timeout_state = 2); // Reconnect after disconnect.
    
    #timeout_state := 0; // Reset
ELSE
    CASE #timeout_state OF
        0: // Normal opperation (no ton_timeout)
            #timeout_state := BOOL_TO_SINT(#ton_timeout_q);
        1: // Disconnect and reconnect mb_client.
            #timeout_state := 2;
    END_CASE;
END_IF;

// If a new download has taken place, and this block is
// re-initialize, then the mb_client block will output the
// error code saying that: "Attempt being made to
// re-establish an existing connection." (16#80A3). The
// solution to this problem is to disconnect and then
// reconnect.
#tdiscon(REQ := #client.STATUS = 16#80A3,
         ID := #conn_id);

// End is comman for both tcp and rtu.
#end(mb_query := #mb_query,
     Error => #Error);

// *)


mb_delay FB8


// For use with modbus tcp, so the client dosen't jam a network.

#mb_query.z.run.qid_cnt += 1;  // The timer occupy one qid.
#ton(IN := #ton_in,
     PT := #delay,
     Q => #ton_q);

IF #ton_in THEN // If the timer has been activated.
    IF #ton_q THEN
        #mb_query.z.run.qid := #sQid + 1;
        RESET_TIMER(#ton);
    END_IF;
    
ELSIF #mb_query.z.run.qid_cnt = #mb_query.z.run.qid
    AND NOT #mb_query.z.run.busy
THEN
    #sQid := #mb_query.z.run.qid; // Store the value
    #mb_query.z.run.qid := 0; // Stop "the loop".
    #mb_query.z.run.insert := true; // Stop exec.
    #ton_in := true;
END_IF;


mb_internal_b FB3


// Buffer for mb_master. (modbus rtu)
// (Can be deleted for modbus tcp)


mb_internal_e FB4


// Internal function - common for both rtu and tcp.

// insert_msg is only written here, while insert will be 
// changed inside the queries.
#mb_query.z.sb.insert_msg := #mb_query.z.run.insert;

// Anit wind up effect for qid. msg. to devices that they should
// reset if qid grow bigger then 60000. (See mb_station_header)
#mb_query.z.sb.reset := false;
IF #mb_query.z.run.qid > 60000 THEN
    #mb_query.z.sb.reset := true;
END_IF;

// #error will be keeped true as long there is at least one query that
// generate a error. 
IF #mb_query.stat.error THEN
    #Error := true;
    #err_hold := true;
END_IF;

IF #mb_query.z.run.qid > #mb_query.z.run.qid_cnt THEN
    
    // Error handeling (see above)
    IF NOT #err_hold THEN
        #Error := false;
    END_IF;
    #err_hold := false;
    
    // If all queries has been executed, then start over again on the
    // first query.
    #mb_query.z.run.qid := 1;
    
    // The retry-id is the device that get to have retry to restoring
    // communication after the qComError flag is set for the device.
    // When one query loop has occurred the retry id is incremented. 
    #mb_query.z.sb.did_retry := #mb_query.z.sb.did_retry + 1;
    
    // retry_finished is set after one retry has taken place. 
    // The device that has the retry-token only has one try to make 
    // successfully query.
    #mb_query.z.sb.retry_finished := false;
    IF #mb_query.z.sb.did_retry > #mb_query.z.sb.did_cnt THEN
        #mb_query.z.sb.did_retry := 1;
    END_IF;
    
    // Device id
    #mb_query.z.sb.did := #mb_query.z.sb.did + 1;
    IF #mb_query.z.sb.did > #mb_query.z.sb.did_cnt THEN
        #mb_query.z.sb.did := 1;
    END_IF;
END_IF;

#mb_query.z.run.qid_cnt := 0; // (Incremented in mb_query)
#mb_query.z.sb.did_cnt := 0; // (Incremented in mb_device_header)


mb_master_1500_ctrl FB7


// RTU master for S7-1500
// 
// This master blocks works for both S7-1200 and S7-1500.
// For S7-1200 it's recommended to use the other master 
// block because it use less memory.
// --- --- ---

// master header
"mb_internal_h"(mode104 := true,
                timeout := #timeout,
                baud := #baud,
                resp_to := #comm_load.RESP_TO,
                mode := #master.MODE,
                data_addr := #master.DATA_ADDR,
                data_len := #master.DATA_LEN,
                buffer := #buffer,
                mb_query := #mb_query);

// Initiate the hardware.
#comm_load.RETRIES := #conf.retries;
#comm_load.MODE := #operating_mode;
#comm_load(REQ := NOT #done XOR #conf.comm_load_req,
           "PORT" := #hardware_id,
           BAUD := #baud,
           PARITY := #conf.parity,
           RESP_TO := #comm_load.RESP_TO,
           MB_DB := #master.MB_DB);
IF #comm_load.DONE THEN
    #done := true;
ELSIF #comm_load.ERROR THEN
    #done := false;
END_IF;

// The core master block.
#master(REQ := #done
        AND NOT #master.BUSY
        AND NOT #mb_query.z.run.insert
        AND #mb_query.z.run.qid <> 0,
        MB_ADDR := #mb_query.z.q.mb_addr,
        MODE := #master.MODE,
        DATA_ADDR := #master.DATA_ADDR,
        DATA_LEN := #master.DATA_LEN,
        DONE => #mb_query.stat.done,
        BUSY => #mb_query.z.run.busy,
        STATUS => #mb_query.stat.status,
        DATA_PTR := #buffer.buffer);
#mb_query.stat.error := #master.ERROR OR #comm_load.ERROR;

// master footer
"mb_internal_f"(data_len := #master.DATA_LEN,
                buffer := #buffer,
                mb_query := #mb_query);

// End is comman for both tcp and rtu.
#end(mb_query := #mb_query,
     Error => #Error);


mb_query FB2


// mb_query use de-/serialize for data_ptr and hence the function
// will not work for a discrete queries where the number of bits 
// dosen't add up to whole bytes. See mb_query_bits.
//
// --- --- --- 
//
// Rexhip - Extended modbus library 
// --------------------------------
// Version:  2.3.3
// License:  MIT-license
// Author:   Ola Bjornli
// Web:      https://github.com/rexhip/rexhip
//
// --- --- --- 
//
// Before dive into the code below, please read to the documentation 
// page at the url provided above.

REGION Header        
    #Done := false;
    #Error := false;
    #z.run.qid_cnt += 1;
    // qid_cnt is increased for every query (above) and reset back to
    // zero in mb_client_ctrl or in mb_master_ctrl. qid (query id) is
    // only increased when a query finnish. A query will execute when
    // qit_cnt equal qid. run.qid is reset back to "1" when all
    // queryies has been executed.
    IF #z.run.qid_cnt <> #z.run.qid
        OR #z.run.busy // mb_master.busy or mb_client.busy
    THEN
        RETURN;
    END_IF;
END_REGION

REGION Auto length
    // data_len=0 => Automatically calculate the length.
    IF #data_len = 0 THEN
        #z.misc.err := Serialize(SRC_VARIABLE := #data_ptr,
                                 DEST_ARRAY => #tBuffer,
                                 POS := #tPos);
        CASE #mode OF
            101, 102, 105, 115:
                #tDiscrete := true;
            0..2:
                CASE #data_addr OF
                    1..9999, 10001..19999:
                        #tDiscrete := true;
                END_CASE;
        END_CASE;
        #tLen := SEL(G := #tDiscrete,
                     // if "half word", then data_len plus one. 
                     IN0 := DINT_TO_UINT((#tPos + 1) / 2),
                     IN1 := DINT_TO_UINT(#tPos * 8));
    ELSE
        #tLen := #data_len;
    END_IF;
END_REGION

REGION Insert and recive
    IF #z.run.insert THEN // Insert query.
        CASE #mode OF
            1, 2, 105, 106, 115, 116: // write  
                #z.run.write := true;
            ELSE // read
                #z.run.write := false;
        END_CASE;
        // The params is transfered over to the udt. They are later
        // transfered from the udt to mb_master or mb_client.
        #z.q.mb_addr := #mb_addr;
        #z.q.mode := #mode;
        #z.q.data_addr := #data_addr;
        #z.q.data_len := #tLen;
        IF #conf.buffer_handler AND #z.run.write THEN
            // Copy the content of #data_ptr to the buffer.
            #tPos := 0;
            #z.misc.err := Serialize(SRC_VARIABLE := #data_ptr,
                                     DEST_ARRAY => #z.buffer,
                                     POS := #tPos);
        END_IF;
        #z.run.insert := false; // false while waiting for the resualt.
        #z.run.insert2 := #z.sb.head_insert := true;
        
        // If the query is finnished. (with or without error)  
    ELSIF #stat.done OR #stat.error THEN
        
        // Make sure that the query resault really belong to this query.
        // This can be false if an other query before is surrounded by
        // a if-statement
        IF #z.q.mb_addr = #mb_addr
            AND #z.q.mode = #mode
            AND #z.q.data_addr = #data_addr
            AND #z.q.data_len = #tLen
        THEN
            IF #stat.done // Successfull query (no error)
                AND NOT #z.run.write  // If it's a read query
                AND #conf.buffer_handler
            THEN
                // Copy the content from the buffer to #data_ptr
                #tPos := 0;
                #z.misc.err := Deserialize(SRC_ARRAY := #z.buffer,
                                           DEST_VARIABLE => #data_ptr,
                                           POS := #tPos);
            END_IF;
            #Done := #stat.done;
            #Error := #stat.error;
            // Temperary store the data_addr for debugging. 
            // mb_station_footer will save it in #...log.error_data_addr
            #z.sb.recived_data_addr := #data_addr;
            // Msg. to mb_station_footer.
            #z.sb.head_recive := true;
        END_IF;
        #z.run.qid += 1; // Move on to next query
        #z.run.insert := true; // Prepare for the next query.
    END_IF;
END_REGION

Категория: Danfoss | Просмотров: 169 | Добавил: gt7600 | Рейтинг: 0.0/0
Всего комментариев: 0
avatar