531 lines
16 KiB
Markdown
531 lines
16 KiB
Markdown
# 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 |