TIA Portal : best practices/variable scope questions

nyanpasu

Member
Join Date
Aug 2018
Location
Canada
Posts
79
Hi everyone.

I am very new to TIA Portal, and I come mostly from the Rockwell world.

I have a question regarding program structure. For some reason the "everything is an FB" concept confuses me.

I am mapping I/O to FBs representing diverse equipment modules (motors, valves, transmitters, etc, let's say the motor FB is FB1).

In Rockwell, I would either create the AOIs as controller scoped and alias them inside relevant programs where I need them or create them as local scope inside the relevant program (equipment section like a silo, would have a section where the AOIs for the valves, mixer, transmitters, etc would be called, as well as an interface for other programs to access in order for all the logic to stay in one place and be more portable).

I'm looking for best practices to use the module FBs I/O inside subsequent sequence or machine logic FBs.

While playing around with the software, having no idea what I'm doing, I've created IO mapping FBs (call them FB10, etc) per module type that call the individual module FBs as multi-instance (instances are created as "static" inside FB10). I then have a bunch of calls to FB1 inside FB10. The IO mapping FBs are then called in an IO FB (FB100) as multi-instance (instances are created as "static"), which is in turn called in OB1 as single-instance FB (which created a DB for it - DB100). I've then created a "test" FB (FB200) called in OB1 that has this last IO FB as an InOut parameter, which also created a DB200 for it, but it only contains the InOut reference.

I thought that "Static" variables were local to the DB but retentive as opposed to "Temporary" which are local but the memory space isn't retained between FB calls. But it seems from the IO mapping FB10, I can access static variables inside every FB1 instance. From FB100, I can access all instances of FB1 as well as all their static tags. And from FB200, I can still access everything down to static variables inside every FB1 instance made in FB10. This doesn't seem like good practice. I would like only the I/O of the FB1 calls to be accessible to "upper level" FBs.

It seems that even if I were to mimic what I do in Rockwell and multi-instance call the FB1 modules directly inside an equipment FB (or "program"), I would end up being able to access its static variables from inside the equipment FB. I had a clue that I should be able to access the FB1 instances inside FB10 for example, but I'm surprised I can even reach the static variables inside FB1.

My question is why does this happen? Is it because of the DB100 created when I finally called a FB as a single instance, everything is in the same DB so "legal" to access, and I get access to everything from FB200 due to the InOut call to DB100?

Would it be better to create a global IO DB and have the FB1 calls read from and write to UDTs contained inside, and pass that to the "machine logic" blocks? Or maybe attach I/O to the IO mapping blocks representing the inputs and outputs of all the module blocks I need the other FBs to have access to?

Thanks for the help.
 
a few comments, although possibly not a direct answer to your (many) questions:


  1. You are correct that static tags carry from scan to scan while temp tags are zeroed out (at both the beginning and end of the block call, technically). static tags also be retentive, in the sense that they carried over during a power cycle; however this is optional. Depending on the tag it may or may not make sense to keep the value. Retentive memory is limited compared to the whole data memory.
  2. Except for temp memory, there isn't really a "scope" concept in Siemens. Effectively all DBs are global, instance DBs just have a predefined structure and automatic data link. You can have local tags or external/global tags in an FB, but since the static tags are stored in a DB, you COULD access them anywhere. Note that just because you CAN does not mean you SHOULD. In general, it is often recommended against accessing the instance DBs externally. This is almost always true for writing to the instance DB, unless there's a very clear reason you're doing it like a config structure. Anyone in the future will be very confused where that data magically shows up from otherwise. Reading from the instance can often make sense, although it can still make sense to minimize it sometimes.
  3. It is much less common to create IO mapping routines in Siemens than AB. They automatically buffer the data as part of the IO scan (like PLC5), but still let you access the immediate value of the tags when needed (like logix). The simulation tools (PLCSIM) and things like watch tables mean that you're much less likely to need extensive simulation code in the PLC, although sometimes that is still done. Signal conditioning like debounce and the like are still of course needed sometimes, but also sometimes handled directly in the IO card.
  4. In general the best practice is to always process tags in an FB as local tags, and pass any data in or out via the interface. UDTs should typically be passed as an in/out, as it actually just passes a pointer instead of duplicating the whole data structure.
If you haven't seen it before, I'd recommend checking out the Programming Guidelines document on SIOS, it has a lot of best practices.

https://support.industry.siemens.com/cs/ww/en/view/81318674
 
Thank you for the precisions. The big thing missing in my understanding of the platform was 2). I gather FBs are a bit like classes in object-oriented programming but with every variable declared public (another example of you can but you shouldn't). I was confused because when defining variables in a FB there are options like "local static" and "local temp". I thought "local" was referring to the variable scope but it probably refers to the instance DB as opposed to a global DB.

So in effect, the whole "input, output, in/out, static" distinctions are kind of optional in practice because you can read and write anything from anywhere, but it is there to help you organize your code. Kind of like how program parameters in Rockwell are optional in practice because you can always declare everything at controller scope and not care, but in Rockwell the platform actually forces you to use the created variables as declared if the system is used, but in Siemens a subsequent programmer can do almost whatever he wants with your code.

I usually always create AOIs/FBs for different modules in order to program additional generic capabilities and to interface with the HMI cleanly. The synchronizing is a bonus, but you are right, it is not necessary in most platforms outside of Rockwell, it seems Siemens too.

My best bet then would seem to be to pass a "data" UDT containing module status, values and commands to "machine logic" FBs as an in/out parameter and have the logic FBs act on the module FBs with those values. Alternatively, I could pass states and values as outputs and have a command input for each module as I usually do. I would probably centralize the commands in a separate FB that would unite all the commands sent by every "machine logic" FB in order to avoid having to use set/resets.

Thank you for the reply and the document, I had read it in diagonal, but I will read it with more attention.
 
Last edited:
Reading through this, I would just point out that not everything is an FB... if it doesn't need internal states (or static memory), an FC suffices. The other caveat is that you can change FBs without a download, but more often than not it resets the internal memory. FCs won't have that issue.

Also, be mindful when looking online for Siemens manuals as there were changes between Step7 and TIA Portal that increase the abilities for structuring a program that weren't available before.

In Step7 with a relatively low number of configurable OBs to use, you'd create functions to organise a Program. So the OB would be the Task, the Function would be the program and you'd call your other FCs and FBs inside it. This will still be the case if you just use OB1.

Now with many more available OBs and more and more advice towards running the logic as required rather than always, you can make your OB the Rockwell program (the OB allows you to configure what normally would a task) and call your logic inside it.

It's a matter of figuring out quirks, but Siemens is far more flexible than Rockwell, though that also means there are far more ways for you to tie yourself up.
 
I think mk42 nailed it.

You are on the right path with organizing the data by UDTs.
Generally the local 'protected' data is part of the FB STAT area und thus its IDBs.
In reality it is not protected, so it is a question of voluntarily abstaining from accessing IDBs outside their FBs. Opinions tend to get religious regarding this.
Global 'public' data is stored in shared-DBs, and passed in and out the FBs or FCs via in/out pins.
UDTs can be used for anything, but they make particular sense when data has to be exchanged in some way, for example between an FC or FB and a shared-DB.

You should also look into 'multi-instance'. With that you have an FB call another FB, and let the called FBs STAT data be part of the calling FBs STAT data and thus you only have a single IDB for the calling FB. Pretty simple when you get the gist of it.
 
I thought "local" was referring to the variable scope but it probably refers to the instance DB as opposed to a global DB.



Well, kinda. For statics, yeah, it's all in a DB somewhere, which other code can access as if it were a global DB.



The wrinkle is in how you access the tag. Local tags you access with just the tag name. If it's static, then the data HAPPENS to be stored in an instance DB somewhere, but the code you write doesn't actually care where, the compiler takes care of all the details for you.


I'm hoping you're accessing your local statics in the FB by #tagname, not "instanceDB".tagname. This is required if you want to meaningfully use the FB more than once; not 100% sure from context what you're doing.



So in effect, the whole "input, output, in/out, static" distinctions are kind of optional in practice because you can read and write anything from anywhere, but it is there to help you organize your code. Kind of like how program parameters in Rockwell are optional in practice because you can always declare everything at controller scope and not care, but in Rockwell the platform actually forces you to use the created variables as declared if the system is used, but in Siemens a subsequent programmer can do almost whatever he wants with your code.


It's optional in the sense that it's voluntary, yes. But the distinctions do have an effect on how the program is structured. If you want to call the FB more than once, you need to pass all your tags through the interface and use them in the local form. If you refer to global DBs inside an FB it makes it much harder to re-use the code.


I usually always create AOIs/FBs for different modules in order to program additional generic capabilities and to interface with the HMI cleanly. The synchronizing is a bonus, but you are right, it is not necessary in most platforms outside of Rockwell, it seems Siemens too.


If there's other stuff you're doing to the inputs, then condition away! Just saying that the traditional AB MapIn/MapOut routines at the beginning/end of the code are not the common pattern in Siemens. What I do often see is an FB in the logic where the input is being used that handles debounce/smoothing. HMI mapping can totally depend on what you're trying to do. The system automatically handles all the diagnostics, but if you want a visual view of each IO point on the HMI for maintenance purposes, that you need to do yourself.



My best bet then would seem to be to pass a "data" UDT containing module status, values and commands to "machine logic" FBs as an in/out parameter and have the logic FBs act on the module FBs with those values. Alternatively, I could pass states and values as outputs and have a command input for each module as I usually do. I would probably centralize the commands in a separate FB that would unite all the commands sent by every "machine logic" FB in order to avoid having to use set/resets.


Passing commands as inputs and states/values as outputs is common practice. Passing big chunks of data via inout is common practice. I've seen it done where you shove everything into a UDT and then pass all data that way into the block, but it makes it a lot harder to troubleshoot. big structures don't show any value when you monitor the code calling the FB online, whereas ins and outs do.



Thank you for the reply and the document, I had read it in diagonal, but I will read it with more attention.


If you've at least skimmed that document, that puts you way ahead of most people starting on Siemens PLC.


Also, not sure if reading something "in diagonal" was an autocorrect flub or a local saying I've never heard of (or a mis-translation?) but I'm digging it and I kinda hope it becomes a thing.



I think mk42 nailed it.


From you, that compliment is a great way to start the day 🍻
 
Passing big chunks of data via inout is common practice. I've seen it done where you shove everything into a UDT and then pass all data that way into the block, but it makes it a lot harder to troubleshoot. big structures don't show any value when you monitor the code calling the FB online, whereas ins and outs do.
Absolutely true.
The advantage of passing large UDT blocks instead of single variables is the ease with which you can implement a change in the interface and make sure the change propagates throughout the program.
Also, if the interface has a lot of data, it will require a lot of pins, so many that maybe the FB will not fit on the screen.
You can also create UDTs of UDTs, and FBs with multi-instance FBs, and when creating a new program from scratch this can be a huge productivity booster.
But on the other hand, changing an UDT or a multi-instance FB may require a lot of the program having to be reinitialized, which may not be possible on a running system.
It is something to consider, what is the best compromise.
 
Last edited:
I think with Siemens & others to comply with the latest specs while keeping original (sort of) way of working is a trade off, for example in Mitsubishi (well q & FX) all data is global, when you create an FB & pass parameters or use internal (local tags) the compiler actually uses a portion of the existing direct addressable data areas i.e. the compiler reserves "M" Bits & "D" registers in the upper region for example M9000 > & D9000, so in effect these are actually addressable outside the FB but in the case of the locals you will not be aware of the actual addresses, if you look at the compiled code these can be seen.
Symbolic addressing only again the actual physical addresses are not known (I believe on later platforms the symbolics are downloaded into the PLC so these can be acessed via for example an HMI without knowing the physical address).
Locals in this platform can be destructable in a sense as if compiler allocation becomes short then they can be re-used in another function. I assume Siemens have used a similar approach i.e. DB's I have not really looked into Siemens since the S5/S7 (None TIA days), although S5 certainly had some local variables for temps. RW went completely or so it seems to a change of platform but even then there seems to be some crossover.
Again, I don't know how other platforms deal with IEC compliant timers but in Mitsi, they are not timers as per the standard but a subroutine that is generated at compile time, these use M bits & D words in the reserved compiler area so in effect, increment or decrement a D word using the clock bits
An IEC timer produces the following code:
0 LD M0
1 OUT M8191 //reserved compiler bit
2 LD SM400 // Special true bit
3 DMOV K3000 D12286 // Move T#3s (3000 ms) to reserved word
6 CALL P2048 // jump to the subroutine
The sub routine just increments the word
Then if the Timer.Q (timer DN bit) is used in another ladder the
code is as follows
8 LD M8190 // this is the timer done bit from the function (Reserved compiler bit)
9 OUT M2
 
Again, I don't know how other platforms deal with IEC compliant timers but in Mitsi, they are not timers as per the standard but a subroutine that is generated at compile time, these use M bits & D words in the reserved compiler area so in effect, increment or decrement a D word using the clock bits


I know for S7-1500 there is some magic going on in the background, but I haven't actually seen the compiled version. They do the same trick of only updating the timer.Q if it or the ET is read in the code, which can throw off a proof of concept with just n HMI reading the tag. The ET also updates continuously mid scan, not just when the timer is called. If you read timer.ET in 4 different places you'll get 4 different values.
 
mk42 said:
Well, kinda. For statics, yeah, it's all in a DB somewhere, which other code can access as if it were a global DB.



The wrinkle is in how you access the tag. Local tags you access with just the tag name. If it's static, then the data HAPPENS to be stored in an instance DB somewhere, but the code you write doesn't actually care where, the compiler takes care of all the details for you.


I'm hoping you're accessing your local statics in the FB by #tagname, not "instanceDB".tagname. This is required if you want to meaningfully use the FB more than once; not 100% sure from context what you're doing.

Yes, that is what I'm doing.

mk42 said:
Passing commands as inputs and states/values as outputs is common practice. Passing big chunks of data via inout is common practice. I've seen it done where you shove everything into a UDT and then pass all data that way into the block, but it makes it a lot harder to troubleshoot. big structures don't show any value when you monitor the code calling the FB online, whereas ins and outs do.

I will keep that in mind. I did notice that in Siemens you can pass basically anything in the interface, so I passed UDTs as I was reusing some states across modules. As you know, you have to use in/out in Rockwell if you want to pass anything other than singleton nuclear types.

mk42 said:
Also, not sure if reading something "in diagonal" was an autocorrect flub or a local saying I've never heard of (or a mis-translation?) but I'm digging it and I kinda hope it becomes a thing.

Haha, I guess that doesn't translate well. "Reading in diagonal/diagonally" is a French expression and it does mean to skim a document.

Thanks for all the help (and to the others who commented too), I have a much clearer view of how to structure this program.
 

Similar Topics

Hi PLC people, think about this scenario: The PLC is somehow connected to the same network with the facilities` network. Then someone connects to...
Replies
2
Views
41
Hello, good morning, I have been having two problems with the Tia Portal software. The first is that I have installed it on my computer and...
Replies
5
Views
171
Hello all! So I have one project with a S7-1214 and a MTP1500 "Project1" and one project with another S7-1214 "Project2". Both of these PLC:s need...
Replies
6
Views
100
Hello, i am using profibus as communication between plc and fanuc arm. I can easily send inputs from plc to arm, but i can't monitor any outputs...
Replies
0
Views
46
Hello, I need to write the following program in Ladder language, but I could not integrate the Fibonacci sequence into the ladder. Can you help...
Replies
22
Views
465
Back
Top Bottom