
danfoss_V15.zip - Example SIEMENS TIA Portal V15
danfoss_TIA_14_SP1.zap14 - Example SIEMENS TIA Portal V14 SP1

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 |