Supported platforms: CODESYS 3.5, CODESYS 3.5 SAFETY
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.
|
|
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:
|
|
Positive and negative attenuation signal statuses are probably non-validated DI signals, i.e. simple S_ADCToVoltageOrCurrent and S_VoltageToDI conversions. |
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:
BoomUp as PWFM
BoomDown as PWFM
PosDirAttOn as DI
NegDirAttOn as DI
|
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: |
|
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;
BoomUpMinimumCurrentOld: 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
IF G_CAN1_PAR.BoomDownMinimumCurrent <> BoomDownMinimumCurrentOld OR G_CAN1_PAR.BoomDownMaximumCurrent <> BoomDownMaximumCurrentOld THEN
|
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: |
|
The following code controls non-safe boom with non-safe CurrentValvePairController FB from Sensors and Actuators library.
Code at non-safe PRG: |
|
See also
SafeProportionalValveControl library
S_EndAttenuation (FB)
S_LinearRamp (FB)
S_CurrentRequest (FB)
S_AdaptiveController (FB)
S_CurrentRequestLimiter (Function)
SensorsAndActuators library
Source file topic100380.htm
Last updated 4-Sep-2025