Reputation: 3
The timing can be used to create a delay in triggering or switching off the same motor or to create a delay in switching on or off between different motors. This is a feature used to protect the motors, avoiding drives with very short intervals. In alarm situations the motors are automatically disabled(priority over time) to avoid further damage.
Start my code by creating a sub routine to read the states of the machines this will tell me if it is on or off. Then create a subroutine that reads the machines that are in the alarm state, then I did a check if the machine is in an alarm state. Below this code I created a routine that drives the motors, and then create a routine to trigger the outputs on the motors must be connected and I finished my code with an else, where it turns off the motors.
VAR_INPUT
ENABLE : BOOL := FALSE; (*ENABLES THE BLOCK OPERATION*)
DEV_STS1 : BOOL := FALSE; (*REPRESENTS MOTOR STATUS 1 ON / OFF*)
DEV_STS2 : BOOL := FALSE; (*REPRESENTS MOTOR STATUS 2 ON / OFF*)
DEV_STS3 : BOOL := FALSE; (*REPRESENTS MOTOR STATUS 3 ON / OFF*)
DEV_STS4 : BOOL := FALSE; (*REPRESENTS MOTOR STATUS 4 ON / OFF*)
DEV_STS5 : BOOL := FALSE; (*REPRESENTS MOTOR STATUS 5 ON / OFF*)
DEV_STS6 : BOOL := FALSE; (*REPRESENTS MOTOR STATUS 6 ON / OFF*)
DEV_ALA1 : BOOL := FALSE; (*REPRESENTS MOTOR ALARM CONDITION 1*)
DEV_ALA2 : BOOL := FALSE; (*REPRESENTS MOTOR ALARM CONDITION 2*)
DEV_ALA3 : BOOL := FALSE; (*REPRESENTS MOTOR ALARM CONDITION 3*)
DEV_ALA4 : BOOL := FALSE; (*REPRESENTS MOTOR ALARM CONDITION 4*)
DEV_ALA5 : BOOL := FALSE; (*REPRESENTS MOTOR ALARM CONDITION 5*)
DEV_ALA6 : BOOL := FALSE; (*REPRESENTS MOTOR ALARM CONDITION 6*)
T_MIN_ON : REAL := 0.0; (*MINIMUM TIME ON ONE SAME MOTOR / RANGE 0.0 ~ 9999.0 **)
T_MIN_OFF : REAL := 0.0; (*MINIMUM TIME OFF OF SAME MOTOR / RANGE 0.0 ~ 9999.0*)
T_ON_ON : REAL := 0.0; (*MINIMUM TIME BETWEEN TWO PARTS OF THE SAME MOTOR / RANGE 0.0 ~ 9999.0*)
T_ON_OTHER : REAL := 0.0; (*TIME BETWEEN TURN ON DIFFERENT MOTORS / RANGE 0.0 ~ 9999.0*)
T_OFF_OTHER : REAL := 0.0; (*TIME BETWEEN TURN OFF DIFFERENT MOTORS / RANGE 0.0 ~ 9999.0*)
END_VAR
VAR_OUTPUT
REQ_DEV1 : BOOL := FALSE; (*STATUS D0 MOTOR 1 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV2 : BOOL := FALSE; (*STATUS D0 MOTOR 2 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV3 : BOOL := FALSE; (*STATUS D0 MOTOR 3 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV4 : BOOL := FALSE; (*STATUS D0 MOTOR 4 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV5 : BOOL := FALSE; (*STATUS D0 MOTOR 5 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV6 : BOOL := FALSE; (*STATUS D0 MOTOR 6 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
END_VAR
VAR
DEV_STS : ARRAY[1..6] OF BOOL; (*MOTOR STATUS READING ARRAY*)
DEV_ALA : ARRAY[1..6] OF BOOL; (*ARRAY READING OF MOTORS ALARMS*)
REQ_DEV : ARRAY[1..6] OF BOOL; (*ARRAY TO MANIPULATE MOTORS STATES*)
FLAG_STS : ARRAY[1..6] OF BOOL; (*ARRAY FOR PREVIOUS STATUS CONTROL OF MOTORS*)
IDX : USINT := 0; (*GENERIC INDEX TO HANDLE ARRAY*)
DEV_ON : USINT := 0; (*AMOUNT OF MOTORS MUST BE TURN ON*)
T_ON_INT : ARRAY[1..6] OF REAL; (*INTERNAL TIME ON A SAME MOTOR*)
T_OFF_INT : ARRAY[1..6] OF REAL; (*INTERNAL TIME OFF A SAME MOTOR*)
T_CYCLE : ARRAY[1..6] OF REAL; (*CYCLE TIME OF SAME MOTOR*)
END_VAR
IF ENABLE THEN
(*==================================================================================*)
(*READINGS OF MOTORS STATUS*)
(*==================================================================================*)
DEV_STS[1] := DEV_STS1;
DEV_STS[2] := DEV_STS2;
DEV_STS[3] := DEV_STS3;
DEV_STS[4] := DEV_STS4;
DEV_STS[5] := DEV_STS5;
DEV_STS[6] := DEV_STS6;
(*==================================================================================*)
(*READINGS OF THE MOTORS ALARM STATUS*)
(*==================================================================================*)
DEV_ALA[1] := DEV_ALA1;
DEV_ALA[2] := DEV_ALA2;
DEV_ALA[3] := DEV_ALA3;
DEV_ALA[4] := DEV_ALA4;
DEV_ALA[5] := DEV_ALA5;
DEV_ALA[6] := DEV_ALA6;
(*==================================================================================*)
(*CHECK IF ANY MOTOR IS ALARMED*)
(*==================================================================================*)
FOR IDX := 0 TO 6 BY 1 DO
IF DEV_ALA[IDX] = TRUE THEN
REQ_DEV[IDX] := FALSE;
END_IF;
END_FOR;
(*==================================================================================*)
(*CHECKING WHAT MOTOR SHOULD BE TURN ON*)
(*==================================================================================*)
FOR IDX := 0 TO 6 BY 1 DO
IF DEV_STS[IDX] = TRUE THEN
DEV_ON := DEV_ON + 1;
END_IF;
END_FOR;
(*==================================================================================*)
(*ACTING A MOTOR*)
(*==================================================================================*)
FOR IDX := 0 TO 6 DO
T_CYCLE[IDX] := T_ON_INT[IDX] + T_OFF_INT[IDX];
IF DEV_STS[IDX] = TRUE AND FLAG_STS[IDX] = FALSE THEN
IF T_CYCLE[IDX] > T_ON_ON THEN
IF T_ON_INT[IDX] < T_MIN_OFF THEN
REQ_DEV[IDX] := TRUE;
END_IF;
END_IF;
END_IF;
IF DEV_STS[IDX] = FALSE AND FLAG_STS[IDX] = TRUE THEN
IF T_ON_INT[IDX] >= T_MIN_ON THEN
REQ_DEV[IDX] := FALSE;
END_IF;
END_IF;
IF DEV_STS[IDX] = TRUE AND FLAG_STS[IDX] = TRUE THEN
T_ON_INT[IDX] := T_ON_INT[IDX] + 1.0;
END_IF;
END_FOR;
(*==================================================================================*)
(*LEADING OUTPUTS*)
(*==================================================================================*)
REQ_DEV1 := REQ_DEV[1] ;
REQ_DEV2 := REQ_DEV[2] ;
REQ_DEV3 := REQ_DEV[3] ;
REQ_DEV4 := REQ_DEV[4] ;
REQ_DEV5 := REQ_DEV[5] ;
REQ_DEV6 := REQ_DEV[6] ;
(*==================================================================================*)
(*FLAG*)
(*==================================================================================*)
FLAG_STS[1] := REQ_DEV1;
FLAG_STS[2] := REQ_DEV2;
FLAG_STS[3] := REQ_DEV3;
FLAG_STS[4] := REQ_DEV4;
FLAG_STS[5] := REQ_DEV5;
FLAG_STS[6] := REQ_DEV6;
ELSE
REQ_DEV1 := FALSE;
REQ_DEV2 := FALSE;
REQ_DEV3 := FALSE;
REQ_DEV4 := FALSE;
REQ_DEV5 := FALSE;
REQ_DEV6 := FALSE;
END_IF;
I have not tested the code yet. But I usually use CFC to test.
Upvotes: 0
Views: 106
Reputation: 3080
For CFC it might be OK but not for ST. In ST you have to use a different concept. I have a lot of questions to your code, but let me show you how I understood it and you will ask questions later.
First, create a type.
TYPE MOTOR : STRUCT
State: BOOL; (* State of the motor translated to DO *)
Task: BOOL; (* Do we want to turn this motor off or on *)
Alarm: BOOL; (* Motor alarm *)
TimerOnMax: TP; (* Timer to maximum work for motor *)
TimerOnMin: TP; (* Timer to maximum work for motor *)
TimerOff: TP; (* Timer for minimum pause between work *)
TimeOnMax: TIME; (* Maximum time for motor to work *)
TimeOnMin: TIME; (* Minimum time for motor to work *)
TimeOff: TIME; (* Minimum time for motor to rest *)
END_STRUCT
END_TYPE
Now define global variables
VAR_GLOBAL
(* Array of motors to manage *)
stMotors: ARRAY[1.._MOTORS_NUM] OF MOTOR := [
_MOTORS_NUM(TimeOnMax := T#1h, TimeOnMin := T#10m, TimeOff := T#30m)
];
END_VAR
VAR_GLOBAL CONSTANT
_MOTORS_NUM: INT := 6; (* Number of motors in array *)
END_VAR
Initialization might be different depending on CoDeSys version
Now our function block
FUNCTION_BLOCK ManageMotors
VAR_INPUT
ENABLE: BOOL; (* Enable motor management *)
M_NUM: INT; (* Number of motors to be working *)
END_VAR
VAR
iCount: INT; (* Index for circle *)
iNumOfMotors: INT; (* Number of currently working motors *)
END_VAR
IF NOT ENABLE THEN
actTurnOffAll();
actApply();
RETURN;
END_IF;
actCountWroking();
FOR iCount := 1 TO _MOTORS_NUM DO
(* If motor in alarm state turn it off *)
IF stMotors[iCount].Alarm AND stMotors[iCount].State THEN
stMotors[iCount].Task := FALSE;
iNumOfMotors := iNumOfMotors - 1;
END_IF;
(* If motor works longer that allowed time turn it off *)
IF stMotors[iCount].State AND
stMotors[iCount].Task AND
NOT stMotors[iCount].TimerOnMax.Q
THEN
stMotors[iCount].Task := FALSE;
iNumOfMotors := iNumOfMotors - 1;
END_IF;
(* If amout of working motors more that allowed number turn one off *)
IF iNumOfMotors > M_NUM AND
stMotors[iCount].State AND
stMotors[iCount].Task AND
NOT stMotors[iCount].TimerOnMin.Q
THEN
stMotors[iCount].Task := FALSE;
iNumOfMotors := iNumOfMotors - 1;
END_IF;
(* If amount of working motors less then required turn one motor on *)
IF iNumOfMotors < M_NUM AND
NOT stMotors[iCount].State AND
NOT stMotors[iCount].Task AND
NOT stMotors[iCount].TimerOff.Q
THEN
stMotors[iCount].Task := TRUE;
iNumOfMotors := iNumOfMotors + 1;
END_IF;
stMotors[iCount].TimerOnMax(
IN := (stMotors[iCount].Task AND NOT stMotors[iCount].State),
PT := stMotors[iCount].TimeOnMax
);
stMotors[iCount].TimerOnMin(
IN := (stMotors[iCount].Task AND NOT stMotors[iCount].State),
PT := stMotors[iCount].TimeOnMin
);
stMotors[iCount].TimerOff(
IN := (NOT stMotors[iCount].Task AND stMotors[iCount].State),
PT := stMotors[iCount].TimeOff
);
END_FOR;
actApply();
ACTION actCountWroking:
iNumOfMotors := 0;
FOR iCount := 1 TO _MOTORS_NUM DO
IF stMotors[iCount].State THEN
iNumOfMotors := iNumOfMotors + 1;
END_IF;
END_FOR;
END_ACTION;
ACTION actTurnOffAll:
FOR iCount := 1 TO _MOTORS_NUM DO
stMotors[iCount].Task := FALSE;
END_FOR;
END_ACTION;
ACTION actApply:
FOR iCount := 1 TO _MOTORS_NUM DO
stMotors[iCount].State := stMotors[iCount].Task;
END_FOR;
END_ACTION;
END_FUNCTION_BLOCK
I added some comments but the rest of the code have to be clear. I used ACTION
as it is available as in CDS 2.3 as in CDS 3.5 but if you have 3.5 version, you can use METHOD
instead.
Upvotes: 1