Add s7-1500/demo-advance-scl-contol.md

This commit is contained in:
Dejan 2026-02-24 11:34:36 +00:00
parent 1f1d9a933e
commit 2cf2ab3814

View file

@ -0,0 +1,531 @@
# README — S7-1500 SCL Sequencer (AUTO / INCR) with Pause vs Stop, Stop Categories, Skip/Disable, Safety-Locked Steps
This project shows a Siemens S7-1500 step sequencer in SCL with:
- AUTO mode (advance automatically)
- INCR mode (advance only on operator NEXT pulse)
- Pause vs Stop
- Stop categories (immediate / end-of-step / end-of-cycle / no-stop)
- Per-step UDT configuration (timeout + operator text)
- Skip/disable steps (optional steps can be disabled)
- Prevent disabling safety steps (PLC forces critical steps enabled)
You can paste the TYPES into "PLC data types" and the FB code into an SCL FB.
------------------------------------------------------------
1) Concepts / How it works
------------------------------------------------------------
1. Step machine (FSM)
- Variable "Step" defines the state:
Step = 0 -> IDLE
Step = 1..10 -> execute step logic
- Each step has:
- Actions (outputs to command)
- Done condition (when it is allowed to advance)
- Timeout (if not done within time -> Error)
2. Modes
We use enum E_Mode:
- MODE_STOP = 0 (not running, safe outputs)
- MODE_AUTO = 1 (auto advance)
- MODE_INCR = 2 (manual advance with NEXT)
Mode selection priority:
- If Enable=FALSE OR Cmd_Stop=TRUE OR ErrorActive=TRUE => MODE_STOP
- Else if Mode_Auto=TRUE => MODE_AUTO
- Else if Mode_Incr=TRUE => MODE_INCR
- Else MODE_STOP
3. Pause vs Stop
- STOP:
- forces safe outputs (FALSE)
- typically sets Step=0 (IDLE)
- PAUSE:
- freezes step progression and timer
- keeps outputs frozen at last values (freeze outputs)
Pause is implemented by:
- PauseLatched toggled by Cmd_Pause edge
- when pause becomes TRUE, capture current outputs into Frozen_* variables
- while paused, outputs are forced to Frozen_* values
4. Stop categories (StopReq + StopCat)
Enum E_StopCat:
- STOPCAT_IMMEDIATE:
stop instantly (Step=0 + safe outputs)
- STOPCAT_END_OF_STEP:
finish current step then stop
- STOPCAT_END_OF_CYCLE:
continue until the configured end-of-cycle step completes, then stop
- STOPCAT_NO_STOP:
info only (never stops)
Stop requests are latched:
- STOPCAT_IMMEDIATE / END_OF_STEP / END_OF_CYCLE -> latched in PLC
- STOPCAT_NO_STOP -> not latched (only displayed as info)
5. Per-step configuration via UDT array Cfg[1..10]
Each step stores:
- Text: shown to operator
- Timeout: step timeout
- IsEndOfCycle: marks the “cycle end” step
- ReqEnabled: HMI requested enable
- Enabled: PLC effective enable used by sequencer
- SafetyLocked: if TRUE, cannot be disabled
- NextStep: optional jump target (0 means normal next)
The PLC NEVER trusts ReqEnabled directly.
PLC computes Enabled every scan:
- if SafetyLocked => Enabled := TRUE (and optionally ReqEnabled forced TRUE too)
- else Enabled := ReqEnabled
6. Skip/Disable steps
Instead of Step := Step + 1, we compute "next enabled step":
- If operator disables a step, sequence jumps over it
- A helper FC_NextEnabledStep finds the next enabled step
- In AUTO mode we wrap 10->1, in INCR mode we can end at 0
Optional routing:
- If Cfg[Step].NextStep <> 0 then jump to that step (if enabled).
------------------------------------------------------------
2) PLC Data Types (copy into "PLC data types")
------------------------------------------------------------
TYPE E_Mode : USINT (
MODE_STOP := 0,
MODE_AUTO := 1,
MODE_INCR := 2
);
END_TYPE
TYPE E_StopCat : USINT (
STOPCAT_NONE := 0,
STOPCAT_IMMEDIATE := 1,
STOPCAT_END_OF_STEP := 2,
STOPCAT_END_OF_CYCLE := 3,
STOPCAT_NO_STOP := 4
);
END_TYPE
TYPE UDT_StepCfg :
STRUCT
Text : STRING[80]; // Operator text for HMI
Timeout : TIME; // Per-step timeout
IsEndOfCycle : BOOL; // Marks end-of-cycle step (e.g., step 10)
ReqEnabled : BOOL; // From HMI/DB (operator request)
Enabled : BOOL; // PLC effective enable (used by sequencer)
SafetyLocked : BOOL; // If TRUE, PLC forces Enabled := TRUE always
NextStep : INT; // 0 = normal next enabled step, else jump to 1..10
END_STRUCT
END_TYPE
------------------------------------------------------------
3) Helper FC: Next enabled step (skip disabled steps)
------------------------------------------------------------
FUNCTION FC_NextEnabledStep : INT
{ S7_Optimized_Access := 'TRUE' }
VAR_INPUT
CurrentStep : INT;
MaxStep : INT; // 10
Wrap : BOOL; // TRUE in AUTO (10->1), FALSE in INCR (end->0)
Cfg : ARRAY[1..10] OF UDT_StepCfg;
END_VAR
VAR
s : INT;
i : INT;
END_VAR
BEGIN
s := CurrentStep;
FOR i := 1 TO MaxStep DO
s := s + 1;
IF s > MaxStep THEN
IF Wrap THEN
s := 1;
ELSE
FC_NextEnabledStep := 0;
RETURN;
END_IF;
END_IF;
IF Cfg[s].Enabled THEN
FC_NextEnabledStep := s;
RETURN;
END_IF;
END_FOR;
FC_NextEnabledStep := 0; // none enabled
END_FUNCTION
------------------------------------------------------------
4) Main FB: Sequencer (AUTO/INCR + Pause/Stop + Stop categories)
------------------------------------------------------------
FUNCTION_BLOCK FB_Seq10_Adv
{ S7_Optimized_Access := 'TRUE' }
VAR_INPUT
Enable : BOOL;
Mode_Auto : BOOL;
Mode_Incr : BOOL;
Cmd_Start : BOOL;
Cmd_Stop : BOOL; // hard stop (safe outputs)
Cmd_Pause : BOOL; // toggle pause (freeze)
Cmd_ResetErr : BOOL;
Cmd_Next : BOOL; // NEXT pulse in increment mode
StopReq : BOOL; // stop request flag
StopCat : E_StopCat; // stop request category
// Example process inputs (replace with real ones)
In_Ready : BOOL;
In_ClampClosed : BOOL;
In_ProcessDone : BOOL;
In_EjectDone : BOOL;
// Step config from DB/HMI/recipe
Cfg : ARRAY[1..10] OF UDT_StepCfg;
END_VAR
VAR_OUTPUT
Step : INT; // 0..10
Running : BOOL;
Paused : BOOL;
ErrorActive : BOOL;
ErrorCode : WORD;
HMI_StepText : STRING[80];
HMI_StopInfo : STRING[80];
// Example outputs (replace)
Out_ClampClose : BOOL;
Out_ProcessOn : BOOL;
Out_Eject : BOOL;
END_VAR
VAR
Mode : E_Mode;
rStart : R_TRIG;
rNext : R_TRIG;
rReset : R_TRIG;
rPause : R_TRIG;
StepPrev : INT;
StepEntry : BOOL;
tStep : TON;
AllowAdvance : BOOL;
StopLatched : BOOL;
StopLatchedCat : E_StopCat;
PauseLatched : BOOL;
Frozen_ClampClose: BOOL;
Frozen_ProcessOn : BOOL;
Frozen_Eject : BOOL;
Cmd_ClampClose : BOOL;
Cmd_ProcessOn : BOOL;
Cmd_Eject : BOOL;
i : INT;
nxt : INT;
END_VAR
BEGIN
// --------- edges ----------
rStart(CLK := Cmd_Start);
rNext(CLK := Cmd_Next);
rReset(CLK := Cmd_ResetErr);
rPause(CLK := Cmd_Pause);
// --------- sanitize step config (prevent disabling safety steps) ----------
// IMPORTANT: HMI should write ONLY ReqEnabled, not Enabled or SafetyLocked.
FOR i := 1 TO 10 DO
IF Cfg[i].SafetyLocked THEN
Cfg[i].Enabled := TRUE;
Cfg[i].ReqEnabled := TRUE; // reflect back to HMI (optional)
ELSE
Cfg[i].Enabled := Cfg[i].ReqEnabled;
END_IF;
END_FOR;
// --------- reset error ----------
IF rReset.Q THEN
ErrorActive := FALSE;
ErrorCode := WORD#0;
Step := 0;
StopLatched := FALSE;
StopLatchedCat := STOPCAT_NONE;
PauseLatched := FALSE;
END_IF;
// --------- mode selection ----------
IF (NOT Enable) OR Cmd_Stop OR ErrorActive THEN
Mode := MODE_STOP;
ELSIF Mode_Auto THEN
Mode := MODE_AUTO;
ELSIF Mode_Incr THEN
Mode := MODE_INCR;
ELSE
Mode := MODE_STOP;
END_IF;
// --------- start ----------
IF rStart.Q AND (NOT ErrorActive) THEN
IF Step = 0 THEN
// start at first enabled step
IF Cfg[1].Enabled THEN
Step := 1;
ELSE
Step := FC_NextEnabledStep(CurrentStep := 0, MaxStep := 10, Wrap := TRUE, Cfg := Cfg);
END_IF;
END_IF;
END_IF;
// --------- latch stop request (category-aware) ----------
IF StopReq THEN
CASE StopCat OF
STOPCAT_IMMEDIATE, STOPCAT_END_OF_STEP, STOPCAT_END_OF_CYCLE:
StopLatched := TRUE;
StopLatchedCat := StopCat;
STOPCAT_NO_STOP:
// info only; do not latch stop
ELSE
// none
END_CASE;
END_IF;
// --------- pause toggle ----------
IF rPause.Q THEN
PauseLatched := NOT PauseLatched;
// capture outputs when entering pause
IF PauseLatched THEN
Frozen_ClampClose := Out_ClampClose;
Frozen_ProcessOn := Out_ProcessOn;
Frozen_Eject := Out_Eject;
END_IF;
END_IF;
Paused := PauseLatched;
// --------- running ----------
Running := (Mode <> MODE_STOP) AND (Step > 0) AND (NOT ErrorActive) AND (NOT Paused);
// --------- step entry ----------
StepEntry := (Step <> StepPrev);
StepPrev := Step;
// --------- HMI step text ----------
IF (Step >= 1) AND (Step <= 10) THEN
HMI_StepText := Cfg[Step].Text;
ELSE
HMI_StepText := 'IDLE';
END_IF;
// --------- stop info ----------
HMI_StopInfo := '';
IF StopReq AND (StopCat = STOPCAT_NO_STOP) THEN
HMI_StopInfo := 'INFO: stop request is NO_STOP (alarm/info only)';
ELSIF StopLatched THEN
CASE StopLatchedCat OF
STOPCAT_IMMEDIATE: HMI_StopInfo := 'Stop requested: IMMEDIATE';
STOPCAT_END_OF_STEP: HMI_StopInfo := 'Stop requested: END_OF_STEP';
STOPCAT_END_OF_CYCLE: HMI_StopInfo := 'Stop requested: END_OF_CYCLE';
ELSE
HMI_StopInfo := 'Stop requested';
END_CASE;
END_IF;
// --------- if current step disabled -> jump immediately ----------
IF (Step >= 1) AND (Step <= 10) THEN
IF NOT Cfg[Step].Enabled THEN
Step := FC_NextEnabledStep(CurrentStep := Step-1, MaxStep := 10, Wrap := (Mode = MODE_AUTO), Cfg := Cfg);
END_IF;
END_IF;
// --------- default commanded outputs ----------
Cmd_ClampClose := FALSE;
Cmd_ProcessOn := FALSE;
Cmd_Eject := FALSE;
AllowAdvance := FALSE;
// --------- step timer (does not run while paused) ----------
IF StepEntry THEN
tStep(IN := FALSE, PT := Cfg[Step].Timeout);
END_IF;
IF (Step >= 1) AND (Step <= 10) THEN
tStep(IN := Running, PT := Cfg[Step].Timeout);
ELSE
tStep(IN := FALSE, PT := T#0S);
END_IF;
// --------- step logic (demo steps, replace with your real logic) ----------
CASE Step OF
0:
;
1: // WAIT READY
IF In_Ready THEN AllowAdvance := TRUE; END_IF;
2: // CLOSE CLAMP
Cmd_ClampClose := TRUE;
IF In_ClampClosed THEN AllowAdvance := TRUE; END_IF;
3: // PROCESS
Cmd_ProcessOn := TRUE;
IF In_ProcessDone THEN AllowAdvance := TRUE; END_IF;
4: // EJECT
Cmd_Eject := TRUE;
IF In_EjectDone THEN AllowAdvance := TRUE; END_IF;
5,6,7,8,9:
// demo pass-through steps
AllowAdvance := TRUE;
10: // END OF CYCLE
AllowAdvance := TRUE;
ELSE
ErrorActive := TRUE;
ErrorCode := WORD#16#9001; // invalid step
END_CASE;
// --------- timeout -> error ----------
IF tStep.Q AND Running THEN
ErrorActive := TRUE;
ErrorCode := WORD#16#8000 + WORD_TO_WORD(INT_TO_WORD(Step));
Mode := MODE_STOP;
END_IF;
// --------- stop category enforcement: immediate stop ----------
IF StopLatched AND (StopLatchedCat = STOPCAT_IMMEDIATE) THEN
Step := 0;
StopLatched := FALSE;
StopLatchedCat := STOPCAT_NONE;
PauseLatched := FALSE;
END_IF;
// --------- step advance ----------
IF (NOT ErrorActive) AND (NOT Paused) THEN
// Evaluate "advance permission" depending on mode
IF (Mode = MODE_AUTO AND AllowAdvance)
OR (Mode = MODE_INCR AND AllowAdvance AND rNext.Q) THEN
// Handle stop-after conditions
IF StopLatched AND (StopLatchedCat = STOPCAT_END_OF_STEP) THEN
Step := 0;
StopLatched := FALSE;
StopLatchedCat := STOPCAT_NONE;
ELSIF StopLatched AND (StopLatchedCat = STOPCAT_END_OF_CYCLE) AND Cfg[Step].IsEndOfCycle THEN
Step := 0;
StopLatched := FALSE;
StopLatchedCat := STOPCAT_NONE;
ELSE
// Compute next step:
// 1) optional jump if NextStep configured
IF (Step >= 1) AND (Step <= 10) AND (Cfg[Step].NextStep <> 0) THEN
nxt := Cfg[Step].NextStep;
IF (nxt >= 1) AND (nxt <= 10) AND Cfg[nxt].Enabled THEN
Step := nxt;
ELSE
ErrorActive := TRUE;
ErrorCode := WORD#16#9100; // invalid NextStep config
END_IF;
ELSE
// 2) normal: next enabled step
nxt := FC_NextEnabledStep(
CurrentStep := Step,
MaxStep := 10,
Wrap := (Mode = MODE_AUTO),
Cfg := Cfg
);
Step := nxt;
END_IF;
END_IF;
END_IF;
END_IF;
// --------- output policy: STOP vs PAUSE vs NORMAL ----------
IF (Mode = MODE_STOP) OR (Step = 0) OR ErrorActive THEN
Out_ClampClose := FALSE;
Out_ProcessOn := FALSE;
Out_Eject := FALSE;
ELSIF Paused THEN
Out_ClampClose := Frozen_ClampClose;
Out_ProcessOn := Frozen_ProcessOn;
Out_Eject := Frozen_Eject;
ELSE
Out_ClampClose := Cmd_ClampClose;
Out_ProcessOn := Cmd_ProcessOn;
Out_Eject := Cmd_Eject;
END_IF;
END_FUNCTION_BLOCK
------------------------------------------------------------
5) Recommended usage in OB1
------------------------------------------------------------
- Create an instance DB for FB_Seq10_Adv.
- Fill Cfg[1..10] with texts/timeouts and safety locks.
Typical values:
- Step 1: Text='Wait Ready', Timeout=T#10S, SafetyLocked=TRUE (if required)
- Step 2: Text='Close Clamp', Timeout=T#5S, SafetyLocked=TRUE
- Step 10: Text='Cycle End', Timeout=T#2S, IsEndOfCycle=TRUE
Operator can only toggle:
- Cfg[i].ReqEnabled for optional steps (SafetyLocked=FALSE)
Run modes:
- AUTO: Mode_Auto=TRUE
- INCR: Mode_Incr=TRUE and use Cmd_Next pulses
Stop:
- Cmd_Stop -> immediate safe stop
- StopReq+StopCat:
- END_OF_CYCLE for "stop after part"
- END_OF_STEP for "stop after step"
- IMMEDIATE for urgent stop
- NO_STOP for warnings/info
Pause:
- Cmd_Pause toggles pause/resume (outputs frozen + timer frozen)
------------------------------------------------------------
6) What to customize next (common in production)
------------------------------------------------------------
1) Split outputs into "Freeze on pause" and "Force safe on pause" groups.
2) Add per-step "StopAllowed" (some steps must never stop mid-step).
3) Add alarm integration with FIFO + timestamps (your previous alarm project).
4) Add step entry actions (run once on step change):
- reset internal flags
- start motion commands
- log step start time
5) Add interlocks:
- if safety chain open -> force STOPCAT_IMMEDIATE
END OF README