Every line of logic you write β and most of Siemens' β lives inside an FC or an FB. Learn the difference, where the built-ins live, and how to read every pin on the interface.
TIA Portal gives you exactly two block types for writing reusable logic. They look identical in the editor β the difference is memory. A Function computes and forgets; a Function Block computes and remembers, because it owns a private instance DB. Everything else follows from that one fact.
Inputs in, result out β like sqrt(x). It runs, returns a value, and keeps nothing. Call it a thousand times; each call starts from a clean slate.
Behaviour plus a private instance DB. Its Static variables survive scan-to-scan, so it can hold a timer, a counter, a step number, a whole state machine.
β instance DB β remembers across scansSame idea you'll meet again as OB / FC / FB / DB in Blocks & Reuse β here we zoom all the way in on the two you actually write code in.
Here's the part that confuses everyone: Siemens' built-in instructions are FCs and FBs too. A TON timer is a system FB; MOVE is a function. They sit in the Instructions task card, ready to drag in. The blocks you author live in Program blocks. Two sources β same two underlying types. Search by block or pin name, filter to just FBs or just FCs, and click any block to read its description and every pin it brings with it. Arrow keys walk the list.
The takeaway: βbuilt-in vs yoursβ is about who wrote it; βFC vs FBβ is about whether it remembers. Those are two independent questions β a built-in can be either, and so can your own.
Open any block and the top pane is its interface: rows of declarations grouped into named sections. These sections are the same in LAD, FBD and SCL β and they decide what shows up as a pin when you call the block. Flip between FC and FB and watch which sections appear; click a section to learn exactly what it does.
Two sections do all the work of telling FC and FB apart: Static (FB only β the memory) and Return (FC only β the result). The other five are shared.
| Section | FC | FB | What it is for |
|---|---|---|---|
| Input | β | β | Values handed in by the caller, read-only inside. Pins on the left. |
| Output | β | β | Results written back out. Pins on the right. |
| InOut | β | β | By reference β edited in place, no copy. A pin on both sides. |
| Static | β | β | The memory. Lives in the instance DB and survives every scan. |
| Temp | β | β | Stack scratchpad. Undefined at block start β write before you read. |
| Constant | β | β | Named literals local to the block. Fixed at compile time. |
| Return | β | β | The Functionβs single result value, like sqrt() β Real. |
Every FC and every FB β yours or Siemens' β is built from the same three layers: a header of properties, an interface of declarations, and a code section. Open a block and the editor splits exactly that way: declarations in the top pane, logic in the bottom. An FB adds a fourth layer an FC can never have β a generated instance DB. Here is the same kind of job written both ways.
// # = block-local tag, "β¦" = global #span := #hi - #lo; #FC_Scale := #lo + #span * INT_TO_REAL(#raw) / #RAW_MAX;
#span sits on the local stack and is gone the moment the block returns β the answer leaves only through Return.CASE #step OF 0: IF #open THEN #step := 10; END_IF; 10: #tmr(IN := TRUE, PT := #tOpen); IF #tmr.Q THEN #step := 20; END_IF; 20: #done := TRUE; END_CASE;
"Valve_1". #step and the whole nested #tmr are stored there, so next scan the CASE picks up exactly where it left off.βͺ Header & properties
Name, number (FC1, FB5), language (LAD / FBD / SCL / STL / GRAPH) and Optimized block access. Language is chosen per block β an SCL FB can call a LAD FC without a care.
β Interface
The seven sections. This is the block's public contract β change a pin here and every existing call goes inconsistent until you update and recompile the callers.
β‘ Code section
One or more networks in LAD/FBD, or plain statements in SCL. Local tags are written #name; global tags and block names go in "quotes".
β’ Instance DB (FB only)
Generated for you the first time you call the FB. Its layout is the interface: Input, Output, InOut and Static laid out as real bytes you can watch online.
Read it as one sentence: the interface declares, the code section acts, and only an FB gets somewhere to keep what it learned. That is the whole structural difference.
When you drop a block into your network, its Input and InOut declarations appear as pins on the left, its Output and InOut pins on the right. Data flows in β the left and β out the right. In SCL the two directions even get different operators: := feeds a value in, => pulls a result out. Here are three real calls β note that the two FBs demand an instance name, while the FC does not.
An InOut pin is special: it's passed by reference, so the block reads and writes your variable in place β no copy. That's how you hand a big UDT or array to a block efficiently, and it appears on both sides of the box.
"FC forgets, FB remembers" is the headline. Here's what actually trips people up:
π§ Single- vs multi-instance
Call an FB and you must name its instance DB. Declare that instance as a Static var inside another FB and it nests β multi-instance. One TON living inside your FB needs no separate DB; its memory rides along in your instance DB.
β FCs can have outputs too
An FC isn't limited to one Return value β it can also have Output and InOut pins. The difference from an FB stays the same: no Static section, so nothing it computes survives the return.
β Temp is never zero by default
A Temp var starts each call with whatever garbage was on the local stack β not 0. Always write before you read it. This is the #1 source of "works sometimes" bugs in FCs.
π InOut for structures, always
Passing a large UDT or array by Input copies the whole thing every scan. Pass it as InOut (by reference) instead β faster, and the block can edit it in place. For FBs this is also how you share one data record between blocks.
π Optimized vs standard access
With Optimized block access on (the default on S7-1200/1500) the compiler decides the byte layout and you address everything symbolically. Turn it off and you get the old fixed offsets β needed only for legacy pointer tricks or absolute addressing.
β οΈ Change a pin, break the callers
The interface is a contract. Add or rename a parameter and every existing call goes red as inconsistent β and for an FB, its instance DBs need regenerating too. Design the interface before you write 30 calls, not after.
π·οΈ One block, many instances
The code of an FB is downloaded once no matter how many times you call it β only the instance DBs multiply. Thirty valves cost you thirty small DBs and one block to maintain. That is the whole reason FBs exist.
You're writing a reusable valve controller that holds a TON timer and a step number, and you'll call it for 30 valves. Which block, and what does each call need?