Interfacing Bristol RTU with MPFM Using MODBUS Protocol

C

Thread Starter

colonel

Hi,

I am struggling to understand the code for MODBUS communications with a slave modbus Halley Burton MPFM. There are many parameters and i don't understand why they are required.

Please let me know why the below parameters are required and their significance,

--Modbus type can be 1, 2 or 3. what is this parameter used for
variable name in code: AI_xxx_MBUS_TYPE?

--Modbus call can be between 1 and 20; what's this parameter
variable name in code:AI_xxx_MBUS_CALL?

--Modbus ref can be between 0 and 31;
variable name in code:AI_xxx_MBUS_REF

modbus offset is calculated using modbus call and modbus ref, so it means there are 20 bytes (modbus call) each of 32 bits (modbus ref). Attached below code.

thanks<pre>
(* This POU is used to apply the required processing on Serial AIs

All relevant data is stored within signal lists - see __LISTS.INI
so it all has to be read out of the list according to the passed
in reference.*)

(* First read whether to process this signal at all *)

MUX_EXTRACT_AI_xxx_PROC_BIT(iiInlist := AI_SER_LIST_PROC_EN,
iiSelect := AI_xxx_REF);
AI_xxx_PROC_BIT := REAL_TO_BOOL(MUX_EXTRACT_AI_xxx_PROC_BIT.orOutput);

(* If not, set bland values and jump out *)

IF (NOT AI_xxx_PROC_BIT) OR (FB_Count < AI_SER_TOTAL) THEN

AI_xxx_ROC_ALM := FALSE;
AI_xxx_REAL := 0.0;
AI_xxx_COMMS_STATUS := FALSE;

(* Else do the processing *)
ELSE

(* Read the parameters *)

(* Modbus Address and type information *)
MUX_EXTRACT_AI_xxx_MBUS_LINK(iiInlist := AI_SER_LIST_MBUS_LINK,
iiSelect := AI_xxx_REF);
AI_xxx_MBUS_LINK := REAL_TO_INT(MUX_EXTRACT_AI_xxx_MBUS_LINK.orOutput);

MUX_EXTRACT_AI_xxx_MBUS_CALL(iiInlist := AI_SER_LIST_MBUS_CALL,
iiSelect := AI_xxx_REF);
AI_xxx_MBUS_CALL := REAL_TO_INT(MUX_EXTRACT_AI_xxx_MBUS_CALL.orOutput);

MUX_EXTRACT_AI_xxx_MBUS_REF(iiInlist := AI_SER_LIST_MBUS_REF,
iiSelect := AI_xxx_REF);
AI_xxx_MBUS_REF := REAL_TO_INT(MUX_EXTRACT_AI_xxx_MBUS_REF.orOutput);

MUX_EXTRACT_AI_xxx_MBUS_TYPE(iiInlist := AI_SER_LIST_MBUS_TYPE,
iiSelect := AI_xxx_REF);
AI_xxx_MBUS_TYPE := REAL_TO_INT(MUX_EXTRACT_AI_xxx_MBUS_TYPE.orOutput);

(* Raw signal Min and Max *)
MUX_EXTRACT_AI_xxx_RAW_MIN(iiInlist := AI_SER_LIST_RAW_MIN,
iiSelect := AI_xxx_REF);
AI_xxx_RAW_MIN := MUX_EXTRACT_AI_xxx_RAW_MIN.orOutput;

MUX_EXTRACT_AI_xxx_RAW_MAX(iiInlist := AI_SER_LIST_RAW_MAX,
iiSelect := AI_xxx_REF);
AI_xxx_RAW_MAX := MUX_EXTRACT_AI_xxx_RAW_MAX.orOutput;

(* Span and Zero *)
MUX_EXTRACT_AI_xxx_SPAN(iiInlist := AI_SER_LIST_SPAN,
iiSelect := AI_xxx_REF);
AI_xxx_SPAN := MUX_EXTRACT_AI_xxx_SPAN.orOutput;

MUX_EXTRACT_AI_xxx_ZERO(iiInlist := AI_SER_LIST_ZERO,
iiSelect := AI_xxx_REF);
AI_xxx_ZERO := MUX_EXTRACT_AI_xxx_ZERo_OrOutput;

(* Last value - used for ROC alarm *)
MUX_EXTRACT_AI_xxx_LAST(iiInlist := AI_SER_LIST,
iiSelect := AI_xxx_REF);
AI_xxx_LAST := MUX_EXTRACT_AI_xxx_LAST.orOutput;

(* Rate of Change Alarm Priority *)
MUX_EXTRACT_AI_xxx_ROC_PRI(iiInlist := AI_SER_LIST_ROC_PRI,
iiSelect := AI_xxx_REF);
AI_xxx_ROC_PRI := REAL_TO_INT(MUX_EXTRACT_AI_xxx_ROC_PRI.orOutput);


(* Check if signal needs to be scaled - if both raw max and min are the same, it doesn't! *)

AI_xxx_SCALE_BIT := NOT (AI_xxx_RAW_MIN = AI_xxx_RAW_MAX);

(* Check if Modbus information is valid *)

AI_xxx_MBUS_PROC_BIT := ((AI_xxx_MBUS_LINK = 1) OR (AI_xxx_MBUS_LINK = 2) OR (AI_xxx_MBUS_LINK = 3)) AND
((AI_xxx_MBUS_TYPE >= 1) AND (AI_xxx_MBUS_TYPE <= 3)) AND
((AI_xxx_MBUS_CALL >= 1) AND (AI_xxx_MBUS_CALL <= 20)) AND
((AI_xxx_MBUS_REF >= 0) AND (AI_xxx_MBUS_REF <= 31));

(* If it is Ok then Read the Modbus Data from the arrays *)

IF (AI_xxx_MBUS_PROC_BIT = TRUE) THEN

AI_xxx_MBUS_OFFSET := ((AI_xxx_MBUS_CALL - 1) * 32) + AI_xxx_MBUS_REF;

IF (AI_xxx_MBUS_LINK = 1) AND (AI_xxx_MBUS_TYPE = 1) THEN
AI_xxx_RAW_INT := WORD_TO_INT (MB_01_WORD[AI_xxx_MBUS_OFFSET]);
AI_xxx_RAW_REAL := INT_TO_REAL(AI_xxx_RAW_INT);
END_IF;

IF (AI_xxx_MBUS_LINK = 2) AND (AI_xxx_MBUS_TYPE = 1) THEN
AI_xxx_RAW_INT := WORD_TO_INT (MB_02_WORD[AI_xxx_MBUS_OFFSET]);
AI_xxx_RAW_REAL := INT_TO_REAL(AI_xxx_RAW_INT);
END_IF;

IF (AI_xxx_MBUS_LINK = 3) AND (AI_xxx_MBUS_TYPE = 1) THEN
AI_xxx_RAW_INT := WORD_TO_INT (MB_03_WORD[AI_xxx_MBUS_OFFSET]);
AI_xxx_RAW_REAL := INT_TO_REAL(AI_xxx_RAW_INT);
END_IF;

IF (AI_xxx_MBUS_LINK = 1) AND (AI_xxx_MBUS_TYPE = 2) THEN
AI_xxx_RAW_REAL := MB_01_REAL[AI_xxx_MBUS_OFFSET];
END_IF;

IF (AI_xxx_MBUS_LINK = 2) AND (AI_xxx_MBUS_TYPE = 2) THEN
AI_xxx_RAW_REAL := MB_02_REAL[AI_xxx_MBUS_OFFSET];
END_IF;

IF (AI_xxx_MBUS_LINK = 3) AND (AI_xxx_MBUS_TYPE = 2) THEN
AI_xxx_RAW_REAL := MB_03_REAL[AI_xxx_MBUS_OFFSET];
END_IF;

IF (AI_xxx_MBUS_LINK = 1) AND (AI_xxx_MBUS_TYPE = 3) THEN
AI_xxx_RAW_UINT := WORD_TO_UINT (MB_01_WORD[AI_xxx_MBUS_OFFSET]);
AI_xxx_RAW_REAL := UINT_TO_REAL(AI_xxx_RAW_UINT);
END_IF;

IF (AI_xxx_MBUS_LINK = 2) AND (AI_xxx_MBUS_TYPE = 3) THEN
AI_xxx_RAW_UINT := WORD_TO_UINT (MB_02_WORD[AI_xxx_MBUS_OFFSET]);
AI_xxx_RAW_REAL := UINT_TO_REAL(AI_xxx_RAW_UINT);
END_IF;

IF (AI_xxx_MBUS_LINK = 3) AND (AI_xxx_MBUS_TYPE = 3) THEN
AI_xxx_RAW_UINT := WORD_TO_UINT (MB_03_WORD[AI_xxx_MBUS_OFFSET]);
AI_xxx_RAW_REAL := UINT_TO_REAL(AI_xxx_RAW_UINT);
END_IF;

IF (AI_xxx_SCALE_BIT ) THEN
AI_xxx_REAL := ((
(AI_xxx_RAW_REAL - AI_xxx_RAW_MIN) / (
AI_xxx_RAW_MAX -
AI_xxx_RAW_MIN) *
AI_xxx_SPAN) +
AI_xxx_ZERO);
ELSE
AI_xxx_REAL :=
AI_xxx_RAW_REAL;
END_IF;


(* Calculate the actual Modbus call being indirectly referred to *)

IF (AI_xxx_MBUS_TYPE = 2) THEN
ACTUAL_CALL_REF := AI_xxx_MBUS_CALL + 40;
ELSE
ACTUAL_CALL_REF := AI_xxx_MBUS_CALL + 20;
END_IF;


(* Depending on link, extract actual Modbus call status *)

IF (AI_xxx_MBUS_LINK = 1) THEN
MUX_EXTRACT_AI_xxx_COMMS_BAD(iiInlist := MB_01_Comms_Error_List,
iiSelect := ACTUAL_CALL_REF);
ELSIF (AI_xxx_MBUS_LINK = 2) THEN
MUX_EXTRACT_AI_xxx_COMMS_BAD(iiInlist := MB_02_Comms_Error_List,
iiSelect := ACTUAL_CALL_REF);
ELSIF (AI_xxx_MBUS_LINK = 3) THEN
MUX_EXTRACT_AI_xxx_COMMS_BAD(iiInlist := MB_03_Comms_Error_List,
iiSelect := ACTUAL_CALL_REF);
END_IF;

(* If succesfully extracted, store the actual Modbus Call status *)

IF (MUX_EXTRACT_AI_xxx_COMMS_BAD.odiStatus < DINT#0) THEN
AI_xxx_COMMS_STATUS := FALSE;
ELSE
AI_xxx_COMMS_STATUS := REAL_TO_BOOL(MUX_EXTRACT_AI_xxx_COMMS_BAD.orOutput);
END_IF;

(* or else set it to Zero *)

ELSE

AI_xxx_REAL := 0.0;
AI_xxx_COMMS_STATUS := FALSE;

END_IF;


(* This section evaluates the Rate of Change [ROC] alarm *)

(* Check that FB has executed twice for each AI to ensure that a non-zero *)
(* value has been stored at least once before enabling ROC alarm and thus *)
(* preventing a spurious alarm on download or reset - Mr.B May'08 *)

IF (AI_xxx_ROC_PRI < 4) AND NOT (FB_Count < (AI_SER_TOTAL * 2)) THEN

(* Get the time elapsed since we last ran this FB - The _PLC_SYS_TICK_CNT system
Variable is a DINT that ranges from 0 to 2147483647 to -2147483648 to 0.
No need to be concerned by rollover as the internal maths handles it. *)

MUX_EXTRACT_AI_xxx_LAST_TIME(iiInlist := AI_SER_LIST_LAST_SYS_TIME,
iiSelect := AI_xxx_REF);
AI_xxx_LAST_SYS_TIME := MUX_EXTRACT_AI_xxx_LAST_TIME.odiOutput;

Sys_Clk_Elapsed := _PLC_SYS_TICK_CNT - AI_xxx_LAST_SYS_TIME;

Secs_Btwn := DINT_TO_REAL(Sys_Clk_Elapsed) / INT_TO_REAL(_PLC_TICK_PER_SEC);

(* Extract the ROC setpoint *)

MUX_EXTRACT_AI_xxx_ROC(iiInlist := AI_SER_LIST_RoC_Db,
iiSelect := AI_xxx_REF);
AI_xxx_RoC_Db := MUX_EXTRACT_AI_xxx_ROC.orOutput;

(* Evaluate Alarm *)

AI_xxx_ROC_ALM := ((ABS(
AI_xxx_LAST -
AI_xxx_REAL) /
Secs_Btwn) >
AI_xxx_RoC_Db);

ELSE

AI_xxx_ROC_ALM := FALSE;

END_IF;

END_IF;


(* Code to be processed regardless of AI_xxx_Proc_En *)
(* Put all the results of processing back into their lists *)

(* Store the current PLC time for next time *)

Sys_Clk_Last := _PLC_SYS_TICK_CNT;

DEMUX_AI_xxx_LAST_TIME(ianyInput := Sys_Clk_Last,
iiSelect := AI_xxx_REF,
iiOutlist := AI_SER_LIST_LAST_SYS_TIME);


(* Store the ROC alarm *)

DEMUX_AI_xxx_STORE_ROC_ALM(ianyInput := AI_xxx_ROC_ALM,
iiSelect := AI_xxx_REF,
iiOutlist := AI_SER_LIST_ROC_ALM);


(* Pass final processed value back into storage list *)

DEMUX_AI_xxx_STORE(ianyInput := AI_xxx_Real,
iiSelect := AI_xxx_REF,
iiOutlist := AI_SER_LIST);


(* Store signal quality based upon comms status *)

DEMUX_AI_xxx_STORE_COMMS_BAD(ianyInput := AI_xxx_COMMS_STATUS,
iiSelect := AI_xxx_REF,
iiOutlist := AI_SER_LIST_COMMS_BAD);


(* Run increment counter to ensure each channel in AI_Total has a stored last time. *)

IF (FB_Count < (AI_SER_TOTAL * 2)) THEN
FB_Count := FB_Count + 1;
END_IF;</pre>
 
> I am struggling to understand the code for MODBUS communications with a slave modbus Halley Burton MPFM. There
> are many parameters and i don't understand why they are required.

> Please let me know why the below parameters are required and their significance,<pre>
> AI_xxx_SCALE_BIT := NOT (AI_xxx_RAW_MIN = AI_xxx_RAW_MAX);</pre>
Where did the (Pascal?) code come from? To me it looks like the "xxx" should be replaced with the point that you are trying to read.

I couldn't find a manual on this thing so that's my shot in the dark. But in general, you shouldn't have to write any code to collect data from Modbus.
 
Top