I've been assuming that when S7Guy says no DBs, he means just that, not just multiple instances
No, I didn't mean no DBs at all. That would be impossible. What I mean is that I can use FBs without a declared IDB, and use a shared DB instead. I think Daniel's example sort of does the same thing, but I prefer my method because I don't have to enter any variables at all in the FB except for the UDT that AR2 is mapped to. It just seems cleaner to me.
Anyway, here it is. First, a few notes:
1. Obviously, if you have a small machine with just a few IO with a program that will be rarely updated, it won't be worth it to use this method. But, I have used it successfully on medium to very large projects (up to 100 nodes), and it has sped up development dramatically because I could add elements in my DBs without changing any code whatsoever.
2. Except for a function that is called on startup, I don't need to assign any parameters in block calls. There isn't anything wrong with input parameters I guess, but I found it a big pain to have to update the calls if I had to add a new parameter. This method eliminates that problem altogether.
3. One goal of mine was to create a programming environment where maintenance is practically nil. If someone else makes a program change, I don't want to have to worry about whether pointers or absolute addresses are being affected somewhere. Or, if someone adds another station (or whatever your machine components are called), it shouldn't be necessary to edit (or add) a bunch of blocks. Rather, I can just create the FB and drop it in my Station Calls "container". And, since almost all of my FBs use the same STAT variable (one udt) with no other input variable, if I need a new FB, I just copy an old one and edit the code.
4. The code approach, regardless of the complexity of the machine or number of IO, looks like this: On start up, the DB is analyzed and pointers are calculated. It just works, and there is nothing to troubleshoot here. In OB1, each station is called, and AR2 is indexed based on the previously calculated pointers. Within each Station call, I first map the real-world inputs, then execute the control code, and then map the results to the real-world outputs.
5. You can see that there is nothing in the core code that cares if someone decides to change the IO addresses. Rather than changing it in dozens of places, you only have to change it in the Machine Config function (or at a Config GUI, your choice).
6. You will see that I only use one shared DB. Actually, you can easily use more than one, but I've found it easier to use one as long as it doesn't get too big (I have some with over 50,000 bytes). I realize this goes against the Siemens concept since they allow you to have thousands of DBs and offer the instance DB functionality, but why bother switching blocks if you don't have to?
7. At first, this code won't make any sense at all. But after a while, it becomes so easy that you can't believe you ever did it any other way. So, before you discard it, give it a shot. It just might pay off, depending on your projects.
8. If you want to create your own project, the only requirement is that you select symbolic address priority. Also, if you change any UDT structure, recompile the code by going to Blocks>CheckBlockConsistency>CompileAllBlocks, and you'll be all set.
9. Don't be alarmed by my use of STL. Except for the start-up block, you can program your regular way. I'm just more comfortable with STL. Also, although I haven't tested it yet, I think this code will run. I substituted "MW" for IW,QW,PQW in this project, and it should map to those bits just fine.
10. Finally, this approach makes it a cinch to maintain a GUI interface. The shared DB can be exported as a text file, and you can do whatever you want with it from there. So, if the absolute addresses change after a UDT structure change, you can automate the GUI tags changes very easily. If you were using dozens of IDBs for this, it would be a nightmare.
Ok, enough rambling. Have fun.