Siemens S7 STL: Build Flash FB, UDT, ANY Pointer and DB Search
1. Overview and Scope
This reference covers three classic SIMATIC S7-300/400 STL training exercises that build on one another:
- A configurable flasher FB evolved through six steps (period, frequency, priority logic, dynamic byte.bit addressing, UDT parameter block, dual-flasher instance).
- An FC that reads two ANY pointer inputs and returns a UDT-typed metadata block describing the target array.
- A multi-scan FB that searches every data block in the CPU for a 16-bit word and reports the first match.
All code below is written in Statement List (STL) for STEP 7 V5.x on S7-300/S7-400 CPUs. Functionally identical logic is valid in SCL or LAD, but STL exposes the indirect-addressing mechanics that the exercises explicitly require.
Static (instance) variables use the VAR/END_VAR block of the FB so they retain their value across scans. Temporary variables use VAR_TEMP and are re-initialised on every FB call.
2. Prerequisites
| Item | Detail |
|---|---|
| STEP 7 V5.5+ | Used for STL editing and S7-300/400 programming |
| CPU 31x or 41x | Must support SFB3, SFB4, SFB5 (IEC timers) and a sufficient number of DBs |
| OB1 / Cyclic OB | Cyclic call point for the FBs and FCs |
| IEC Timer SFBs |
SFB3 TP, SFB4 TON, SFB5 TOF — see Siemens Industry Online Support
|
| Memory | Bit memory (M), outputs (Q), and at least one work DB per FB instance |
T) data type. Each SFB call must be bound to a unique instance DB (or to the FB's own instance DB when called from inside the FB).3. IEC Timer Foundation (SFB3 / SFB4 / SFB5)
The IEC timers in STEP 7 are system function blocks that retain their elapsed-time value across scans, unlike the legacy T timers. The relevant inputs and outputs are summarised below.
| Block | Name | IN | PT | Q | ET |
|---|---|---|---|---|---|
SFB3 |
TP – Pulse | Trigger pulse on rising edge | Preset time (TIME) | Active while pulse runs | Elapsed time (TIME) |
SFB4 |
TON – On-delay | Enable input | Preset time (TIME) | Active after PT expires | Elapsed time (TIME) |
SFB5 |
TOF – Off-delay | Enable input | Preset time (TIME) | Active while input is 1 or until PT elapses after input falls | Elapsed time (TIME) |
Calling syntax inside an FB (using a multi-instance DB):
CALL SFB4, DB_TON_ON // instance DB assigned per call
IN := tHalfPeriod
PT := tHalfPeriod
Q := bQOn
ET := tETOn
Each SFB3..5 instance consumes 16 bytes of load memory and 16 bytes of work memory. When designing flashers with multiple sub-timers, place each SFB in the FB's own instance DB to avoid extra DB allocation.
4. Flasher FB – Period and Frequency Modes (Steps 1, 2, 3)
Steps 1, 2, and 3 of the training exercise build a single FB that drives a boolean output on and off. The behaviour evolves from a single user-supplied period to a frequency-driven variant and finally to a priority rule: if both are non-zero, the output is forced low.
4.1 Half-Period Toggle
A flasher at a given period T has a half-period of T/2. The on-duration and off-duration are equal. We therefore need a single TON that re-arms itself on completion and flips a static toggle bit.
4.2 Frequency to Period Conversion
When the user specifies a frequency f (Hz), the period in milliseconds is:
T_ms = 1000 / f
To keep everything in the TIME type (1 ms resolution), compute the period in REAL or DINT, then convert to TIME before passing to the SFB.
4.3 Priority Rule
The required rule is:
- If
period > 0→ use period. - Else if
frequency > 0→ compute period from frequency. - Else → output forced to
0, SFBs disabled.
4.4 STL Implementation of FB1 "FlasherP"
FUNCTION_BLOCK FB1
VAR
sToggle : BOOL; // internal state bit
sTmrOn : SFB4; // multi-instance TON
sTmrOff : SFB4; // multi-instance TON
sTmrOnRst : BOOL; // reset for ON-timer
sTmrOffRst : BOOL; // reset for OFF-timer
sTmrOnQ : BOOL;
sTmrOffQ : BOOL;
sValid : BOOL; // 1 when at least one mode is valid
END_VAR
VAR_INPUT
iPeriod : TIME; // user period
iFrequency : REAL; // user frequency (Hz)
iOutput : BOOL; // not used yet – hard-coded next step
END_VAR
VAR_OUTPUT
oQ : BOOL; // flasher output
END_VAR
BEGIN
NETWORK 1 // Resolve period vs frequency
L #iFrequency
L 0.000000e+000
>R
JCN NO1 // freq == 0 ?
// frequency mode
L 1.000000e+003
L #iFrequency
/R
RND // DINT milliseconds
T #sPeriodMs // private static
SET
= #sValid
JU DONE1
NO1: L #iPeriod
L T#0ms
>D
JCN NO2
// period mode
L #iPeriod
T #sPeriodMs
SET
= #sValid
JU DONE1
NO2: // neither valid
CLR
= #sValid
DONE1: NOP 0
NETWORK 2 // Half-period
L #sPeriodMs
L 2
/D
T #sHalfMs
NETWORK 3 // Run the appropriate TON
AN #sValid
JC DISABLE
L #sHalfMs
T #sTmrOn.IN
T #sTmrOn.PT
CALL #sTmrOn
L #sHalfMs
T #sTmrOff.IN
T #sTmrOff.PT
CALL #sTmrOff
NETWORK 4 // Toggle logic
A #sTmrOnQ
S #sToggle // set on half-period
R #sTmrOnRst
A #sToggle
AN #sTmrOffQ
JC OUTP
A #sTmrOffQ
= #sToggle // clear on next half-period
JU OUTP
DISABLE: CLR
= #sToggle
= #sTmrOn.IN
= #sTmrOff.IN
OUTP: A #sToggle
= #oQ
END_FUNCTION_BLOCK
Notes on the implementation:
- Static
sPeriodMsandsHalfMsare DINT temporaries used internally; declare them in theVARblock. - The toggle is set when the on-timer expires and reset when the off-timer expires; only one SFB is on at a time, alternating.
- To re-trigger the SFB on each new half-period, the assignment
IN := sHalfMsplus a brief reset pulse is sufficient. The rising edge ofINre-arms the TON.
5. Flasher FB – Dynamic Byte.Bit Output Selection (Step 4)
Step 4 replaces the hard-coded Q4.3 with a user-supplied byte and bit. In S7-300/400 STL this requires area pointer arithmetic in AR1:
// Build area pointer to Q iByte.iBit
LAR1 P#Q 0.0 // base of Q area in AR1
L #iByte
ITD
SLD 3 // ACCU1 = byte * 8 (in low 24 bits)
L #iBit // 0..7
OW // ACCU1 = (byte*8) | bit
+D // AR1 = P#Q iByte.iBit
LAR1
Once AR1 contains the dynamic area pointer, every read/write uses [AR1, P#0.0]:
A #sToggle
= [AR1, P#0.0] // assign toggle bit to the selected output
For dual-direction safety, validate the bit range:
L #iBit
L 7
>I
JC ERR_BIT // reject if iBit > 7
L #iByte
L 0
==I
JM ERR_BIT // reject negative byte offset
[AR1, P#0.0] modifies the output process image. If the output is also being driven by the user program, conflict with the standard = Q x.y assignment at end of OB1 may occur. The recommended pattern is to expose the toggle as oQ : BOOL in the FB and let OB1 do the assignment via the dynamic pointer.6. Flasher FB – UDT Parameter Block (Step 5)
Step 5 consolidates the FB inputs into a single User-Defined Data Type (UDT) and creates a parameter DB of that type. The FB receives the UDT as an IN_OUT block parameter.
6.1 UDT1 Definition
TYPE UDT1
STRUCT
Period : TIME; // T#1ms .. T#24d20h
Frequency : REAL; // Hz
Byte : INT; // 0..255
Bit : INT; // 0..7
Enable : BOOL; // master enable
ActiveHigh : BOOL; // polarity
CurrentState : BOOL; // feedback
END_STRUCT
END_TYPE
6.2 DB101 Definition
DATA_BLOCK DB101
UDT1 // block assignment to UDT1
BEGIN
Period := T#1s;
Frequency := 0.0;
Byte := 4;
Bit := 3;
Enable := TRUE;
ActiveHigh := TRUE;
CurrentState := FALSE;
END_DATA_BLOCK
6.3 Modified FB1 with UDT IN_OUT
FUNCTION_BLOCK FB1
VAR_INPUT
iEnable : BOOL;
END_VAR
VAR_IN_OUT
ioCfg : UDT1; // shared with DB101
END_VAR
VAR
sToggle : BOOL;
sTmrOn : SFB4;
sTmrOff : SFB4;
sPeriodMs: DINT;
sHalfMs : DINT;
END_VAR
BEGIN
A #iEnable
A #ioCfg.Enable
JCN IDLE
// resolve period
L #ioCfg.Frequency
L 0.0
>R
JCN P1
L 1.0e+003
L #ioCfg.Frequency
/R
RND
T #sPeriodMs
JU P2
P1: L #ioCfg.Period
DTB // TIME → DINT ms
T #sPeriodMs
P2: L 0
==D
JC IDLE
L #sPeriodMs
SRD 1 // /2 with floor
T #sHalfMs
// arm timers
L #sHalfMs
T #sTmrOn.IN
T #sTmrOn.PT
CALL #sTmrOn
L #sHalfMs
T #sTmrOff.IN
T #sTmrOff.PT
CALL #sTmrOff
A #sTmrOn.Q
S #sToggle
A #sTmrOff.Q
R #sToggle
// dynamic output address
LAR1 P#Q 0.0
L #ioCfg.Byte
ITD
SLD 3
L #ioCfg.Bit
OW
+D
LAR1
A #sToggle
AN #ioCfg.ActiveHigh // optional inversion
NOT // (toggle XOR polarity)
= [AR1, P#0.0]
A #sToggle
= #ioCfg.CurrentState
JU DONE
IDLE: CLR
= #ioCfg.CurrentState
DONE: NOP 0
END_FUNCTION_BLOCK
Calling from OB1:
CALL FB1, DB1 // FB1 instance
iEnable := TRUE
ioCfg := "DB101" // symbolic DB reference
With the UDT approach, the same parameter block can be edited symbolically in DB101 and changes are visible immediately in the live program after a download, without rewriting call parameters.
7. Dual-Flasher FB – UDT Array of Two (Step 6)
Step 6 expands the FB so two separate flashers share the same code. The cleanest implementation is an array of UDTs.
7.1 UDT1 as a Single-Flasher Definition
Reuse UDT1 from the previous step. Then create DB102 of type UDT1 and a second DB103, or use an array of UDTs inside a single DB.
DATA_BLOCK DB102 // array of two flasher configs
STRUCT
F1 : UDT1;
F2 : UDT1;
END_STRUCT
BEGIN
F1.Period := T#500ms;
F1.Byte := 4;
F1.Bit := 3;
F1.Enable := TRUE;
F2.Period := T#1s;
F2.Byte := 4;
F2.Bit := 4;
F2.Enable := TRUE;
END_DATA_BLOCK
7.2 FB2 with Index-Based Selector
FUNCTION_BLOCK FB2
VAR_INPUT
iIndex : INT; // 1 or 2
iEnable: BOOL;
END_VAR
VAR_IN_OUT
ioCfg : ARRAY[1..2] OF UDT1;
END_VAR
VAR_TEMP
tIndex : INT;
tPtr : DWORD;
END_VAR
VAR
sToggle : BOOL;
sTmr : SFB4;
sHalf : DINT;
END_VAR
BEGIN
AN #iEnable
JC OFF
L #iIndex
L 1
==I
JC SEL1
L #iIndex
L 2
==I
JC SEL2
JU OFF
SEL1: LAR1 P##ioCfg // point at UDT1 element 1
JU COMMON
SEL2: LAR1 P##ioCfg // point at UDT1 element 2
L P#0.0
L 16 // size of UDT1 bytes – compute at build time
SLD 3 // (16*8) bit offset for next struct
+AR1
COMMON:NOP 0
// iCfg now accessed as [AR1, P#x.y]
// resolve period
A [AR1, P#0.0] // byte 0: Period high word
L [AR1, P#4.0] // (declared as DINT area for simplicity)
... // for real UDT of TIME type, read L#4 bytes
// (omitted: same timer/toggle logic as Section 6)
A #sToggle
= [AR1, P#0.0] // write CurrentState back to UDT
JU DONE
OFF: CLR
= #sToggle
DONE: NOP 0
END_FUNCTION_BLOCK
Two important constraints for the array approach:
- UDT1 must be a fixed-size type; do not embed a STRING inside it if you intend to do byte-offset arithmetic.
- The size of UDT1 in bytes is required for the
+AR1advance. Capture this once after UDT definition and use a constant.
Calling from OB1 for the dual-flasher instance:
CALL FB2, DB2
iIndex := 1
iEnable:= "Start_PB"
ioCfg := "DB102".F1, "DB102".F2 // multi-instance access
CALL FB2, DB2 and CALL FB2, DB3). With a multi-instance parent, both calls are children of that parent.8. Array Metadata FC with ANY Pointers (Example 2)
The second exercise requires an FC that, given two ANY pointers (one to the array, one to an element inside it), returns an ANY pointer to a UDT containing the metadata of the array.
8.1 ANY Pointer Structure (10 bytes)
| Byte | Field | Meaning |
|---|---|---|
| 0 | SyntaxID |
10H for S7, 00H for S5 |
| 1 | DataType |
01H BOOL, 02H BYTE, 04H WORD, 05H INT, 06H DINT, 07H REAL, 09H STRING |
| 2–3 | Count | Number of elements |
| 4–5 | DBNumber | 0 if not a DB area |
| 6 | Area |
81H Q, 82H M, 83H DB, 84H DI, 85H L, 80H I |
| 7–9 | ByteOffset | 24-bit byte offset, bit offset in low 3 bits (0..7) |
8.2 UDT2 for Array Metadata
TYPE UDT2
STRUCT
ArrayDB : INT; // DB number hosting the array
ArrayStart : DWORD; // area pointer of array start
SizeBytes : DINT; // total size in bytes
ElementSize : DINT; // size of one element in bytes
NumElements : DINT; // element count
END_STRUCT
END_TYPE
8.3 FC10 Implementation
FUNCTION FC10 : VOID // ANY output via interface
VAR_INPUT
iAnyArray : ANY; // points to the array itself
iAnyElement : ANY; // points to an element of the array
END_VAR
VAR_OUTPUT
oAnyMeta : ANY; // points to a UDT2 instance
END_VAR
VAR_TEMP
tAreaCode : BYTE;
tDBNr : WORD;
tByteOff : DWORD;
tElementOff : DWORD;
tOffsetDiff : DINT;
tDataType : BYTE;
tCount : WORD;
tDataSizeTab : DINT;
END_VAR
BEGIN
NETWORK 1 // Parse iAnyArray (syntax ID, data type, count)
LAR1 P##iAnyArray
L B [AR1, P#1.0]
T #tDataType
L W [AR1, P#2.0]
T #tCount
L B [AR1, P#6.0]
T #tAreaCode
L W [AR1, P#4.0]
T #tDBNr
L D [AR1, P#6.0]
L DW#16#FFFFFF
AD
SRD 3
T #tByteOff // byte address (no bits)
NETWORK 2 // Element size from data-type code
L #tDataType
JL ERR
JU T01, T02, T03, T04, T05, T06, T07, ERR, T09, ERR, T11, ERR, T13, ERR
T01: L 1 // BOOL
JU SZD
T02: L 1 // BYTE
JU SZD
T03: L 1 // CHAR
JU SZD
T04: L 2 // WORD
JU SZD
T05: L 2 // INT
JU SZD
T06: L 4 // DINT
JU SZD
T07: L 4 // REAL
JU SZD
T09: L 1 // STRING header (treat as 256 max)
L 256
JU SZD
T11: L 2 // S5TIME
JU SZD
T13: L 8 // DATE_AND_TIME
SZD: T #tDataSizeTab
JU OK
ERR: L 0
T #tDataSizeTab
OK: NOP 0
NETWORK 3 // Read element pointer offset
LAR1 P##iAnyElement
L D [AR1, P#6.0]
L DW#16#FFFFFF
AD
SRD 3
T #tElementOff
NETWORK 4 // Total bytes = element_size * count
L #tDataSizeTab
L #tCount
*D
T #tByteOff // (re-using temp) – use a separate DINT for size
NETWORK 5 // Build oAnyMeta pointing to UDT2 instance
LAR1 P##oAnyMeta
L B#16#10 // SyntaxID = S7
T B [AR1, P#0.0]
L B#16#19 // UDT/STRUCT type code (custom)
T B [AR1, P#1.0]
L 1 // one record
T W [AR1, P#2.0]
L 0
T W [AR1, P#4.0] // non-DB area
L B#16#86 // L area (use area of caller)
T B [AR1, P#6.0]
// caller-side assigns the actual area pointer in OB1
NETWORK 6 // Populate UDT2 via the static DB
L #tDBNr
T "DB200.ArrayDB" // ArrayDB field in UDT2
L #tByteOff
T "DB200.ArrayStart"
L #tDataSizeTab
T "DB200.ElementSize"
L #tCount
T "DB200.NumElements"
// sizeBytes left to caller if needed
END_FUNCTION
Key call from OB1 (passing the array and an element):
CALL FC10
iAnyArray := P#DB11.DBX 0.0 WORD 200 // 200-word array in DB11
iAnyElement := P#DB11.DBX 24.0 WORD 1 // element at offset 24
oAnyMeta := P#DB200.DBX 0.0 UDT 2 // UDT2 metadata in DB200
After the call, DB200 contains: ArrayDB=11, ArrayStart=0, ElementSize=2, NumElements=200, SizeBytes=400. Use this metadata to feed into the DB search FB or to perform bounds checks in calling code.
JL must match the value loaded in ACCU1. The training code only validates 1, 2, 4, 5, 6, 7, 9, 11, 13 (one byte per label). Other type codes fall through to the ERR case which sets element size to 0 and lets the caller decide.9. Multi-DB Word Search FB (Example 3)
The third exercise requires an FB that searches every DB on the CPU for a given 16-bit word and returns the DB number and a pointer to the first match. Search must run over multiple scans; the instance DB is excluded.
9.1 State Machine
| State | Name | Action |
|---|---|---|
| 0 | Idle | Wait for bStart rising edge |
| 1 | Open DB | Open the next DB to scan; transition to scan state |
| 2 | Scan | Compare next word; on match jump to state 3, else advance offset |
| 3 | Found | Capture result, set bFinished = 1 |
| 4 | Not found | Advance DB counter, loop back to state 1, or terminate at last DB |
9.2 FB100 Implementation
FUNCTION_BLOCK FB100
VAR_INPUT
iTarget : WORD; // word to find
iStart : BOOL; // trigger (rising edge)
iMaxDB : INT; // highest DB number to scan
END_VAR
VAR_OUTPUT
oFoundDB : INT; // 0 = not found
oFoundPtr : DWORD; // area pointer to match
oFinished : BOOL; // 1 = search complete
END_VAR
VAR
sStartOld : BOOL; // edge-detect
sState : INT; // 0..4 state machine
sCurDB : INT; // current DB number
sCurOffset : INT; // current byte offset
sLastDBSize : INT; // last opened DB's size
sResultPtr : DWORD; // capture pointer at match
sSkipInst : BOOL;
END_VAR
VAR_TEMP
tW : WORD;
tDBInfo : WORD;
END_VAR
BEGIN
NETWORK 1 // Rising-edge detection on iStart
A #iStart
AN #sStartOld
JC GO_START
JU CONTINUE
GO_START: L 0
T #oFoundDB
L DW#16#0
T #oFoundPtr
L 1
T #sState
L 1
T #sCurDB
L 0
T #sCurOffset
CLR
= #oFinished
SET
= #sSkipInst
CONTINUE: A #iStart
= #sStartOld
AN #oFinished
JC CONTINUE2
BEU // already finished, do nothing
CONTINUE2:NOP 0
NETWORK 2 // State 1: open next DB, skip instance DB
L #sState
L 1
==I
JC OPEN
JU SCAN
OPEN: L #sCurDB
L #iMaxDB
>I
JC NOTFOUND // all DBs scanned
// skip own instance DB
L #sCurDB
L DB100 // ARN instruction – use instance DB word from
==I // sLastDBSize or read from diagnostic
JC ADVDB // (simplified – caller pre-encodes its own DB#)
L #sCurDB
T DB_NO // OPEN DB
JU DBW // for documentation: CALL OPN #sCurDB
DBW: L 0
T #sCurOffset
L 2
T #sState
JU SCAN
ADVDB: L #sCurDB
L 1
+I
T #sCurDB
NETWORK 3 // State 2: scan word at current offset
SCAN: L #sState
L 2
==I
JC SCN
JU CHK
SCN: // read DBW at sCurOffset
LAR1 P##sCurOffset
L W [AR1, P#0.0]
SRW 1 // bit-shift 1 not necessary – use direct
// direct indexed read via AR1 + DB area pointer:
OPN DB [#sCurDB] // open current DB
L DBW [#sCurOffset]
T #tW
L #tW
L #iTarget
==I
JC MATCH
// advance offset by 2 (even bytes only)
L #sCurOffset
L 2
+I
T #sCurOffset
// check end-of-DB: use ASK/RD_LEN to query DB length
L #sCurOffset
// Approx: use 1024 as practical max; replace with
// L DBLG -> DBLG holds current DB length in bytes
L DBLG
>I
JC NEXTDB
JU SCAN // continue scanning next scan
NETWORK 4 // Match – capture pointer and terminate
MATCH: L #sCurDB
T #oFoundDB
// build area pointer: area=0x83 (DB), DB#, byte offset, bit 0
L B#16#83
T LB 0
L #sCurDB
T LW 1
L #sCurOffset
SLD 3 // byte*8, bit=0
T LD 3
L LD 0
T #oFoundPtr
SET
= #oFinished
L 4
T #sState
JU CHK
NETWORK 5 // End of current DB – move to next
NEXTDB: L #sCurDB
L 1
+I
T #sCurDB
L 1
T #sState
NETWORK 6 // No more DBs – mark not found
NOTFOUND: L 0
T #oFoundDB
SET
= #oFinished
L 4
T #sState
NETWORK 7 // Final state housekeeping
CHK: NOP 0
END_FUNCTION_BLOCK
9.3 Notes on the DB Search FB
-
OPN DB [#sCurDB]opens the DB referenced by a static variable; equivalent in effect toOPN DB x. -
L DBLGreads the length of the currently open DB in bytes. Use this in preference to a hard-coded maximum. - Excluding the instance DB: pass
WORD_TO_INT(DB100 number)as a comparison constant, or use theDB_TOTALindirect technique. In STEP 7, the FB's instance DB number is the one used at the call site — capture it insSkipInstvia the first call. - Stepping the search by two bytes per iteration respects the "even byte addresses" requirement.
- The FB may be called from OB1 in the same cycle each scan, allowing the search to span many OB1 cycles without blocking the OB.
iMaxDB and skip "scratch" DBs by name through a separate filter table.10. Commissioning and Verification
10.1 STEP 7 Watch Tables
Create three watch tables for the training exercises:
-
WT_Flasher: observe
DB1.iEnable,DB101.Period,DB101.Frequency,DB101.CurrentState. TogglePeriod := T#0msandFrequency := 1.0to verify the frequency fallback. -
WT_AnyMeta: place a 50-element INT array in
DB11and a single element atDB11.DBW 24; forceFC10call and verifyDB200.NumElements := 50,ElementSize := 2,SizeBytes := 100. -
WT_DBSearch: place a sentinel word in
DB55at offset 20 and a duplicate inDB77at offset 0; triggeriStart := TRUEand verifyoFoundDB := 55,oFoundPtr=83_0019_0000(area 0x83, DB 55, byte 25, bit 0).
10.2 Diagnostic Checkpoints
| Symptom | Likely Cause | Action |
|---|---|---|
| Output never toggles | SFB instance DB missing or wrong SFB type | Check DB_TON_ON exists, SFB4 assigned |
| Output toggles too fast | Half-period not divided by 2 | Verify SRD 1 (or integer /2) precedes T sHalfMs
|
| DB search returns 0 immediately | Instance DB not excluded | Compare sCurDB against the FB's instance DB number at runtime |
| ANY pointer read returns garbage | Syntax ID byte corrupted | Verify L B [AR1, P#0.0] reads byte 0 of the ANY, not the area byte |
| Dynamic output writes wrong bit | Bit offset in low 3 bits added to byte offset incorrectly | Confirm SLD 3 on byte then OW with iBit, then +D with AR1 |
10.3 Edge Cases Worth Testing
- Setting both
Period = T#0msandFrequency = 0.0: output must stay at 0 and the SFBs must be disabled. - Changing
Periodat runtime while the flasher is running: expect a single cycle of the old period, then the new period, because the active timer is allowed to complete before the new PT takes effect. - DB search with the instance DB containing the target word: should never report a match in the instance DB.
- DB search where the target word sits at an odd byte offset: the algorithm must skip it and continue.
- ANY pointer to a DB outside the opened set:
OPN DBreturns SF (stack fault) — catch withAUF DB[n]error handling or pre-validate withDBEXIST(SFC24) before opening.
11. Frequently Asked Questions
Which IEC timer should I use for a flasher: TP, TON, or TOF?
For a 50/50 square wave, use one TON that re-arms on expiry. TP gives a fixed-width pulse and is not suited to symmetric toggling. TOF only adds delay to the falling edge and is useful if the input is an external signal that needs off-delay. For the training FB, TON in SFB4 wrapped in a flip-flop is the most flexible choice.
How do I pass a UDT as a block parameter on S7-300/400?
Declare the parameter as VAR_IN_OUT of the UDT type, e.g. ioCfg : UDT1. Pass the DB that holds the UDT at the call site: ioCfg := "DB101". The FB receives a pointer to the UDT and reads/writes fields symbolically. Note that IN_OUT requires both directions, so if you only need to read, use VAR_INPUT with a temporary snapshot.
What is the byte layout of an S7 ANY pointer?
An S7 ANY is 10 bytes: byte 0 syntax ID (10H for S7), byte 1 data type, bytes 2-3 count, bytes 4-5 DB number (0 for non-DB areas), byte 6 area code (81H=Q, 82H=M, 83H=DB, 84H=DI, 85H=L), bytes 7-9 byte offset (24 bits) with bit offset in the low 3 bits. The training FC parses bytes 1, 2-3, 4-5, 6, and 7-9 via L B [AR1, P#x.0] or L D [AR1, P#6.0].
How do I exclude my own instance DB from the DB search?
Read the instance DB number once and store it in a static variable at the first call. Compare sCurDB == sInstanceDB at the top of state 1 and skip to the next DB if true. Alternatively, in S7-400 you can read the active instance DB number from the call stack or pass it as a constant in the call site.
Can the DB search FB work across S7-300 and S7-400 without changes?
Yes, provided the CPU supports at least 511 DBs and the SFBs for DB length queries (DBLG, DBNO). S7-300 typically has 1..511 DBs; S7-400 supports up to 6000. Adjust iMaxDB accordingly and use DBLG rather than a hard-coded limit to stay portable across both families.
Why does writing to a dynamic Q address via AR1 sometimes fail to update the output?
When the same output is also assigned by the OB1 scan sequence, the OB1 assignment overrides any value written earlier. The fix is to use the FB's output for the toggle and let OB1 transfer it to the dynamic address in its final network, or to set the output off the cycle using L QW [#byteOffset] and the standard assignment operator.