Supported platforms: CODESYS 3.5, CODESYS 3.5 SAFETY

 

How to control proportional valves  

This section describes how to control proportional valves.

 

This example uses the following libraries:

SafeProportionalValveControl library

 

, or alternatively non-safe library

SensorsAndActuators library

 

 

Depending on hardware, output group control needs to be activated before controlling outputs. See How to use S series and E series HW diagnostics for more information.

 

 

Currently, the S Series and E Series control unit's code template S_Outputs / Outputs programs provide interface for all output related variables defined in Epec MultiTool Creator. All output related function blocks are implemented by the application.

 

External voltage detection in output pins is provided by safety control unit's safety switch startup test. The startup test execution depends on used product:

After startup test the external voltage is monitored by application library diagnostics.

 

Application level detection is required if external voltage needs to be monitored while operational (application functionality specific). External voltage can be monitored using DOS status of the output when PWM control is not active.

 

Application level implementation is required to monitor expected output voltage when output control is ON.

  • Implementation is possible on control units containing output pin's voltage measurement (e.g. SL8X1-01) by comparing measured voltage to the expected voltage 

  • Output pin's expected voltage during control is PWM ratio request * VIN +/- tolerance

  • Monitoring can be used to detect:

    • shortcuts to other voltage levels than VIN (e.g shortcut to 30V when VIN is 24V)

    • HW FET's internal overcurrent reaction

 

Application level implementation is required to monitor leakage current when control is OFF. Library's overcurrent monitoring is active when control is OFF, but in typical use case the overcurrent limit is parametrized for the control ON situation (e.g. 2A limit). Leakage current monitoring can be used to detect internal hardware defects.

 

S_AdaptiveController (FB) status PI_I_TermError can be used to detect if the target current cannot be reached during control. This may indicate:

  • Error in parameters/configuration

  • Too high current request

  • Hardware fault in output's current measurement

 

Positive and negative attenuation signal statuses are probably non-validated DI signals, i.e. simple S_ADCToVoltageOrCurrent and S_VoltageToDI conversions.

 

Examples

These example covers only proportional valve output control. The joystick control request variable S_JoystickControl is defined but not implemented.

 

The following I/O is defined in MultiTool Creator:

 

In the following examples, the output pin external voltage error is not monitored when control is inactive. See the How to control DO & DO ECO example for more information.

 

 

Example 1: Proportional valve controlExample 1: Proportional valve control


When the following example is used with non-safety I/O, the I/O variables are generated in Inputs and Outputs programs instead of S_Inputs and S_Outputs.




Safe PRG declarations:

VAR

S_BoomCurrentReqUp: EPEC_SPVC.S_CurrentRequest;

S_BoomCurrentReqDown: EPEC_SPVC.S_CurrentRequest;

S_BoomControlRamp: EPEC_SPVC.S_LinearRamp;

S_BoomEndAttenuation: EPEC_SPVC.S_EndAttenuation;

S_BoomUpValve: EPEC_SPVC.S_AdaptiveController;

S_BoomDownValve: EPEC_SPVC.S_AdaptiveController;


initDone: BOOL;

S_JoystickControl: SAFEINT; (*Control signal from joystick, not implemented in this example*)

enableControl: SAFEBOOL; (*Control enable conditions*)


(*ec_controlUp can be replaced with Joystick's event code if it exists in same control unit*)

ec_controlUp: EPEC_DITF.EventCode;

ec_controlDown: EPEC_DITF.EventCode;


(*Current controller parameters *)

ccParams:ARRAY [1..S_CURRENT_CONTROLLER_PAR_COUNT] OF UINT :=

[1359,1307,1231,1172,1137,1113,1103,1093,1088,1076,1075,1075,1075,1078,1076,1075,1034,1014,1014,1014];

ccParamsImpUp: ARRAY [1..S_CURRENT_CONTROLLER_PAR_COUNT] OF UINT :=

[ 1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,950,950,950,950,950,1000,1000,1000,1000,1000  ];

ccParamsImpDown:ARRAY [1..S_CURRENT_CONTROLLER_PAR_COUNT] OF UINT:=

[ 1200,1200,1100,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000];

END_VAR

VAR CONSTANT

S_CURRENT_CONTROLLER_PAR_COUNT : SAFEBYTE := BYTE#20;

S_CURRENT_CONTROLLER_PAR_STEP: SAFEBYTE := BYTE#50;

BOOM_VALVE_FREQ: DWORD := 200;

END_VAR



Init method:

IF NOT initDone THEN

initDone:=TRUE;


(*Application specific parameters*)


(*ec_controlUp can be replaced with Joystick's event code if it exists in same control unit*)

S_BoomEndAttenuation.Init(

S_i_PosDirAttenuationLimit := UINT#90,

S_i_NegDirAttenuationLimit := UINT#90,

i_pEventCode := ADR(ec_controlUp),

i_pEventCodePosDirAtt := ADR(S_Inputs.o_EventCode_PosDirAttOn),

i_pEventCodeNegDirAtt := ADR(S_Inputs.o_EventCode_NegDirAttOn)

);


S_BoomControlRamp.Init(

S_i_PosDirAscendRampTime := UINT#1000,

S_i_PosDirDescendRampTime := UINT#1000,

S_i_NegDirAscendRampTime := UINT#1000,

S_i_NegDirDescendRampTime := UINT#1000,

S_i_EnableCounterControl := FALSE,

i_pEventCode := ADR(ec_controlUp)

);


S_BoomCurrentReqUp.Init(

S_i_MinimumCurrent := UINT#150,

S_i_MaximumCurrent := UINT#600,

S_i_PositiveControlDir := TRUE,

i_pEventCode := ADR(ec_controlUp)

);


S_BoomUpValve.Init(

S_i_CoilResistance := UDINT#22000,

S_i_OverCurrentLimit := UINT#1100,

S_i_WireBrokenLimit := UINT#50,

S_i_DiagnosticDelay := UINT#100,

i_pCorrectionParameters := ADR(ccParams),

i_pImpulseUpParameters := ADR(ccParamsImpUp),

i_pImpulseDownParameters := ADR(ccParamsImpDown),

S_i_ParameterCount := S_CURRENT_CONTROLLER_PAR_COUNT,

S_i_ParStep := S_CURRENT_CONTROLLER_PAR_STEP,

i_pEventCode := ADR(ec_controlUp)

);


S_BoomCurrentReqDown.Init(

S_i_MinimumCurrent := UINT#150,

S_i_MaximumCurrent := UINT#600,

S_i_PositiveControlDir := FALSE,

i_pEventCode := ADR(ec_controlDown)

);


S_BoomDownValve.Init(

S_i_CoilResistance := UDINT#22000,

S_i_OverCurrentLimit := UINT#1100,

S_i_WireBrokenLimit := UINT#50,

S_i_DiagnosticDelay := UINT#100,

i_pCorrectionParameters := ADR(ccParams),

i_pImpulseUpParameters := ADR(ccParamsImpUp),

i_pImpulseDownParameters := ADR(ccParamsImpDown),

S_i_ParameterCount := S_CURRENT_CONTROLLER_PAR_COUNT,

S_i_ParStep := S_CURRENT_CONTROLLER_PAR_STEP,

i_pEventCode := ADR(ec_controlDown)

);


END_IF



The following code is common for both control directions.


Code at safe PRG:



The following code controls boom up direction.


Code at safe PRG:



The following code controls boom down direction.


Code at safe PRG:



 

Example 2: Proportional valve control, Safe I/O, Non-safe min-max current parametersExample 2: Proportional valve control, Safe I/O, Non-safe min-max current parameters

 

 

This example shows how to control safe I/O with non-safe minimum and maximum current parameters and the safety aspect still considered. The adjustment of non-safe parameters is quicker (control unit reboot not required) than the adjustment of the safe parameters. So, this approach makes the adjustment of minimum and maximum current values more easy.

 

S_CurrentRequestLimiter function call is added after the S_CurrentRequest FB. The minimum and maximum current parameters of S_CurrentRequest FB can be non-safe parameters. The minimum and maximum current parameters of S_CurrentRequestLimiter function shall be safe parameters. 

 


Requires SafeProportionalValve library V2.0.1.0 or newer.


Safe PRG declarations:

VAR

S_BoomCurrentReqUp: EPEC_SPVC.S_CurrentRequest;

S_BoomCurrentReqDown: EPEC_SPVC.S_CurrentRequest;

S_BoomControlRamp: EPEC_SPVC.S_LinearRamp;

S_BoomEndAttenuation: EPEC_SPVC.S_EndAttenuation;

S_BoomUpValve: EPEC_SPVC.S_AdaptiveController;

S_BoomDownValve: EPEC_SPVC.S_AdaptiveController;

S_BoomUpCurrentRequest: SAFEUINT;
S_BoomDownCurrentRequest: SAFEUINT;
BoomUpCurrentLimitValid: BOOL;    
BoomDownCurrentLimitValid: BOOL;

 

BoomUpMinimumCurrentOld: UINT;
BoomUpMaximumCurrentOld: UINT;
BoomDownMinimumCurrentOld: UINT;
BoomDownMaximumCurrentOld: UINT;

 

initDone: BOOL;

S_JoystickControl: SAFEINT; (*Control signal from joystick, not implemented in this example*)

enableControl: SAFEBOOL; (*Control enable conditions*)

 

(*ec_controlUp can be replaced with Joystick's event code if it exists in same control unit*)

ec_controlUp: EPEC_DITF.EventCode;

ec_controlDown: EPEC_DITF.EventCode;

 

(*Current controller parameters *)

ccParams:ARRAY [1..S_CURRENT_CONTROLLER_PAR_COUNT] OF UINT :=

[1359,1307,1231,1172,1137,1113,1103,1093,1088,1076,1075,1075,1075,1078,1076,1075,1034,1014,1014,1014];

ccParamsImpUp: ARRAY [1..S_CURRENT_CONTROLLER_PAR_COUNT] OF UINT :=

[ 1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,950,950,950,950,950,1000,1000,1000,1000,1000  ];

ccParamsImpDown:ARRAY [1..S_CURRENT_CONTROLLER_PAR_COUNT] OF UINT:=

[ 1200,1200,1100,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000];

END_VAR

VAR CONSTANT

S_CURRENT_CONTROLLER_PAR_COUNT : SAFEBYTE := BYTE#20;

S_CURRENT_CONTROLLER_PAR_STEP: SAFEBYTE := BYTE#50;

BOOM_VALVE_FREQ: DWORD := 200;

END_VAR

 

 

Init method:

IF NOT initDone THEN

initDone:=TRUE;

 

(*Application specific parameters*)

 

(*ec_controlUp can be replaced with Joystick's event code if it exists in same control unit*)

S_BoomEndAttenuation.Init(

S_i_PosDirAttenuationLimit := UINT#90,

S_i_NegDirAttenuationLimit := UINT#90,

i_pEventCode := ADR(ec_controlUp),

i_pEventCodePosDirAtt := ADR(S_Inputs.o_EventCode_PosDirAttOn),

i_pEventCodeNegDirAtt := ADR(S_Inputs.o_EventCode_NegDirAttOn)

);

 

S_BoomControlRamp.Init(

S_i_PosDirAscendRampTime := UINT#1000,

S_i_PosDirDescendRampTime := UINT#1000,

S_i_NegDirAscendRampTime := UINT#1000,

S_i_NegDirDescendRampTime := UINT#1000,

S_i_EnableCounterControl := FALSE,

i_pEventCode := ADR(ec_controlUp)

);

 

// Note! The minimum and maximum current paremeters can be non-safety parameters

S_BoomCurrentReqUp.Init(

S_i_MinimumCurrent := G_CAN1_PAR.BoomUpMinimumCurrent,

S_i_MaximumCurrent := G_CAN1_PAR.BoomUpMaximumCurrent,

S_i_PositiveControlDir := TRUE,

i_pEventCode := ADR(ec_controlUp)

);

 

S_BoomUpValve.Init(

S_i_CoilResistance := UDINT#22000,

S_i_OverCurrentLimit := UINT#1100,

S_i_WireBrokenLimit := UINT#50,

S_i_DiagnosticDelay := UINT#100,

i_pCorrectionParameters := ADR(ccParams),

i_pImpulseUpParameters := ADR(ccParamsImpUp),

i_pImpulseDownParameters := ADR(ccParamsImpDown),

S_i_ParameterCount := S_CURRENT_CONTROLLER_PAR_COUNT,

S_i_ParStep := S_CURRENT_CONTROLLER_PAR_STEP,

i_pEventCode := ADR(ec_controlUp)

);

 

// Note! The minimum and maximum current paremeters can be non-safety parameters

S_BoomCurrentReqDown.Init(

S_i_MinimumCurrent := G_CAN1_PAR.BoomDownMinimumCurrent,

S_i_MaximumCurrent := G_CAN1_PAR.BoomDownMaximumCurrent,

S_i_PositiveControlDir := FALSE,

i_pEventCode := ADR(ec_controlDown)o_

);

 

S_BoomDownValve.Init(

S_i_CoilResistance := UDINT#22000,

S_i_OverCurrentLimit := UINT#1100,

S_i_WireBrokenLimit := UINT#50,

S_i_DiagnosticDelay := UINT#100,

i_pCorrectionParameters := ADR(ccParams),

i_pImpulseUpParameters := ADR(ccParamsImpUp),

i_pImpulseDownParameters := ADR(ccParamsImpDown),

S_i_ParameterCount := S_CURRENT_CONTROLLER_PAR_COUNT,

S_i_ParStep := S_CURRENT_CONTROLLER_PAR_STEP,

i_pEventCode := ADR(ec_controlDown)

);

 

END_IF

 

 

UpdateCurrentParameters method:

 

IF G_CAN1_PAR.BoomUpMinimumCurrent <> BoomUpMinimumCurrentOld OR G_CAN1_PAR.BoomUpMaximumCurrent <> BoomUpMaximumCurrentOld THEN
    
    // Non-safe current parameters updated, run init again. 
    S_BoomCurrentReqUp.Init(
        S_i_MinimumCurrent := G_CAN1_PAR.BoomUpMinimumCurrent,    
        S_i_MaximumCurrent := G_CAN1_PAR.BoomUpMaximumCurrent,    
        S_i_PositiveControlDir := TRUE,    
        i_pEventCode := ADR(ec_controlUp)
    );
    
    BoomUpMinimumCurrentOld := G_CAN1_PAR.BoomUpMinimumCurrent;
    BoomUpMaximumCurrentOld := G_CAN1_PAR.BoomUpMaximumCurrent;
END_IF

 

IF G_CAN1_PAR.BoomDownMinimumCurrent <> BoomDownMinimumCurrentOld OR G_CAN1_PAR.BoomDownMaximumCurrent <> BoomDownMaximumCurrentOld THEN
    
    // Non-safe current parameters updated, run init again. 
    S_BoomCurrentReqDown.Init(
        S_i_MinimumCurrent := G_CAN1_PAR.BoomDownMinimumCurrent,    
        S_i_MaximumCurrent := G_CAN1_PAR.BoomDownMaximumCurrent,    
        S_i_PositiveControlDir := FALSE,    
        i_pEventCode := ADR(ec_controlDown)
    );
    
    BoomDownMinimumCurrentOld := G_CAN1_PAR.BoomDownMinimumCurrent;
    BoomDownMaximumCurrentOld := G_CAN1_PAR.BoomDownMaximumCurrent;
END_IF

 

 

The following code is common for both control directions.

 

Code at safe PRG:

 

 

The following code controls boom up direction. 

 

Code at safe PRG:

 

 

The following code controls boom down direction.

 

Code at safe PRG:

 

 

 

Example 3: Non-safe I/O control with Sensors and Actuators libraryExample 3: Non-safe I/O control with Sensors and Actuators library

The following code controls non-safe boom with non-safe CurrentValvePairController FB from Sensors and Actuators library.


Code at non-safe PRG:



 

See also

 

 

Source file topic100380.htm

Last updated 4-Sep-2025