Siemens S7 Indirect Addressing

kamenges said:
If you do plan to pass a UDT into an FC and you have items in the UDT that will be updated by the FC put the UDT under the in_out declarations.
Keith

As UDT's are passed to FC's by pointer this is not necessary, passing it as an IN gives the FC read/write access. (IN_OUT would be required for an FB though)
 
Thank You!!!

Gentlemen:

Thank you all for providing so many diverse, creative solutions to my problems. And thank you to the folks across the pond who have replied after work hours!

I think I have a set of solutions to ponder and test. Hopefully I can reciprocate equally, in the future.

Thanks again.

Ben Tarnowski
Advanced Automation
[email protected]
 
Has anyone encountered issues when there's an S5 Timer in the UDT being passed into an FC? I cannot address my timer block to the timer address I've setup in my UDT.

Any thoughts?
 
Originally posted by L D[AR2,P#0.0]:
As UDT's are passed to FC's by pointer this is not necessary, passing it as an IN gives the FC read/write access. (IN_OUT would be required for an FB though)

I didn't know that. Is that true of all IN parameters to an FC or just UDT references. Ether way that's pretty nice. It keeps the processor overhead low.

Keith
 
I'll formulate an answer to your question tomorrow - I'll need to check Berger + get access to Step 7.
On reflection it may be better to declare the UDT as an in_out though to allow you to pass the individual structure elements on as parameters to other functions. If the udt is declared as an in, you get full access in terms of:

L Motor.Speed
T Motor.Speed

but you can only pass Motor.Speed on as an input parameter to another function. Making the UDT in_out means you can use Motor.Speed as the output parameter of a function.
 
Keith

For an FC a parameter of type UDT uses the same format as a DB pointer (6 bytes consisting of db number + area pointer).

For an FB an input parameter of type UDT causes the block editor to insert a call to SFC20 (block copy) immediately before the FB call and the data is copied from the source of the UDT into the instance DB. (OUT parameter causes block copy after the FB call, IN_OUT causes block copy before and after FB call)

Please note that there are important considerations that arise from the above.

FC - UDT is passed as a pointer so the overhead at the block call is minimal. However, each UDT access inside the FC requires the pointer to be examined and if necessary the correct DB has to be opened. The following processing logic therefore applies.

Code:
L Motor.Speed
 
executes as
 
L P#motor
LAR1
L W[AR1,P#0.0]
L 0
==I
jc nodb
OPN DB[W[AR1,P#0.0]]
nodb: L D[AR1,P#2.0]
LAR1
L W[AR1,P#0.0]   //this is the actual access to Motor.Speed !

This code may be implemented more efficiently in MC7 than I've shown, but there is no getting around the fact that the processing required to access a UDT variable inside an FC will result in extra memeory space being used and extra execution time being used.

FB - UDT is passed by copying the data into the instance DB each time so the overhead at the block call will depend on the size of the data area. This time however, each UDT access inside the FB is the same as any other access to FB interface data. The following processing logic therefore applies.

Code:
L Motor.Speed
 
executes as 
 
L W[AR2,P#0.0]

So, FC's low overhead at block call, high overhead at each access, FB's high overhead at block call, low overhead at each access.
 
MaxRoberts22 said:
Has anyone encountered issues when there's an S5 Timer in the UDT being passed into an FC? I cannot address my timer block to the timer address I've setup in my UDT.

Any thoughts?

Are you trying something like this ?

Code:
TYPE UDT 1
VERSION : 0.1

  STRUCT  
   Motor : STRUCT  
	Speed : INT ; 
	s5TimerValue : S5TIME  := S5T#1S; 
   END_STRUCT ; 
  END_STRUCT ; 
END_TYPE
 
FUNCTION FC 8 : VOID
TITLE =
VERSION : 0.1

VAR_INPUT
  Pump : UDT 1; 
END_VAR
BEGIN
NETWORK
TITLE =
	  A	 M0.0; 
	  L	 #Pump.Motor.s5TimerValue; 
	  SD	T	  1; 
	  NOP   0; 
	  NOP   0; 
	  NOP   0; 
	  NOP   0; 
END_FUNCTION
 
I'll still stick with the multi-instance FB recommendation.

Issues about regenerating the 'master' or 'parent' FB instance data block are absolutely valid if additional motors are added, but surely additional code/data would have to be added to cover this scenario using FCs in any case?
For something like a motor I'd expect the development of the standard motor control FB itself to be a relatively quick and finite task. A motor is a motor is a motor ... and once created and tested shouldn't require any changes. It gets placed in Max's own or the company's standard library and re-used wherever and whenever similar control is required.

Keith and I have tried to show in our different ways what the construction of multi-instance FBs is about. Max, have you extended your planning and thinking beyond the basic device level? What about cells or units or whatever your particular industry may use to refer to functional clusters of devices? If you have a standard FB for motors, why not one for valves, and one for variable speed drives etc. Then you could consider a standard FB for a process unit. Embedded in this could be, say, four declarations of the motor FB, six declarations of the valve FB, and one declaration of the drive FB. All of a sudden you have a company standard for a mixer or a press or whatever you're controlling. And all the data for this is held in an ordered structured consistently-named fashion in a single DB.

Nesting FBs in FBs as functional elements can be extended several levels. Passing pointers to pointers to pointers is not something I'd personally like to tackle during the phases of writing, debugging or long-term maintenance.

I put my 'X' in the FB box!

Regards

Ken
 
MaxRoberts32 said:
Has anyone encountered issues when there's an S5 Timer in the UDT being passed into an FC? I cannot address my timer block to the timer address I've setup in my UDT.
To put it shortly: Do not use S5timers !
All you will get from trying to use S5 timers in reusable code is pain, pain and more pain.
Use IEC timers. You can make the IEC timers part of the motor UDT. Or you can make IEC timers be part of a FB variable declaration.

edit:
An added bonus with IEC timers is that they ressemble AB timers much more than S5Timers.
 
Last edited:
OK, I'll bite, I've been using S5 timers in S7 from day one - what pain is it you are referring to ?
 
pains are:
1. 0-999. Say no more.
2. Possible but complex to use with reusable code. I dont think there exists a good concept for reusable code that also uses S5Timers.
3. Only a limited number available - and if you use the extra timers available on the larger CPUs, then the code isnt compatible with the smaller CPUs.
4. The interaction with the HMI is awkward. I never really figured out how to input a setpoint directly on a panel without having to combine with some code to set the timebase.
5. Counts down to zero. I prefer to count up to the setpoint.

I user Merkers and S5Timers only when I need to write some quick-and-dirty code. Later I tidy it up.
 
Back to the FC/FB/UDT etc. discussion, you could have the best of both worlds and use the method described by S7Guy as already mentioned. You can use an FC to call an FB with the instance data area of the FB set-up by the FC, this way you only pass a pointer to the FC, and the FB is called with no parameters so no block copying takes place. All static variables you wish to access in the FB must be declared in the UDT and of course the temp area is available as normal.Here's the source code for a small working example to show the principle. The symbol table needs an entry for the datablock named "dbData" in order for the code to compile. To run the code simply call FC2 from OB1

Code:
TYPE UDT 1
VERSION : 0.1

  STRUCT  
   SetSpeed : INT ; 
   s5RunTime : S5TIME  := S5T#10S; 
   ActualSpeed : INT ; 
  END_STRUCT ; 
END_TYPE
DATA_BLOCK "dbData"
TITLE =
VERSION : 0.1

  STRUCT  
   Pump : UDT 1; 
   Conveyor : UDT 1; 
   Indexer : UDT 1; 
  END_STRUCT ; 
BEGIN
   Pump.SetSpeed := 10; 
   Pump.s5RunTime := S5T#10S; 
   Pump.ActualSpeed := 0; 
   Conveyor.SetSpeed := 20; 
   Conveyor.s5RunTime := S5T#10S; 
   Conveyor.ActualSpeed := 0; 
   Indexer.SetSpeed := 30; 
   Indexer.s5RunTime := S5T#10S; 
   Indexer.ActualSpeed := 0; 
END_DATA_BLOCK
FUNCTION_BLOCK FB 1
TITLE =must be called with UC
VERSION : 0.1

VAR
  Motor : UDT 1; 
END_VAR
BEGIN
NETWORK
TITLE =simple test FB, STAT/TEMP data only
	  L	 #Motor.SetSpeed; 
	  T	 #Motor.ActualSpeed; 
	  NOP   0; 
END_FUNCTION_BLOCK

FUNCTION FC 1 : VOID
TITLE =call FB with a manually set instance DB
VERSION : 0.1

VAR_INPUT
  UDTPointer : POINTER ; 
END_VAR
VAR_TEMP
  iDino : INT ; 
END_VAR
BEGIN
NETWORK
TITLE =
	  L	 P##UDTPointer; 
	  LAR1  ; 
	  L	 W [AR1,P#0.0]; //get DB of source
	  T	 #iDino; 
	  OPN   DI [#iDino]; //and make it the instance DB
	  L	 D [AR1,P#2.0]; //get area pointer of source
	  LAR2  ; //and set AR2 to point to the area
	  UC	FB	 1; 
	  SET   ; //make block ENO=1
	  SAVE  ; 
END_FUNCTION
FUNCTION FC 2 : VOID
TITLE =motor supervisor
VERSION : 0.1
BEGIN
NETWORK
TITLE =pump
	  CALL FC	 1 (
		   UDTPointer			   := "dbData".Pump);
	  NOP   0; 
NETWORK
TITLE =conveyor
	  CALL FC	 1 (
		   UDTPointer			   := "dbData".Conveyor);
	  NOP   0; 
NETWORK
TITLE =indexer
	  CALL FC	 1 (
		   UDTPointer			   := "dbData".Indexer);
	  NOP   0; 
END_FUNCTION
 
I also don't get the problem with S5 timers, but it's probably because I was used to them with S5. Yeah, they have a 999.3 limit, but if I need a timer longer than that I would probably use a timestamp compare anyway. I particularly like having five different flavors of timers to choose from.

SD_Scott said:
I used s7guy's recipe example and stripped out what I wanted and it has worked for everything I have needed. It's a nice piece of code. You can read and write to arrays of built in data types or UDTs and it uses symbolic programming.

http://www.plctalk.net/qanda/showthread.php?t=8978&page=2&pp

at the bottom


Glad you can use it. I've done indirect addressing a dozen different ways, and still find this the easiest. Keep in mind that I think there are a couple of typos in my example, but you should still get the idea.
 

Similar Topics

Hello guys, This works: L P#DBX 30.0 LAR2 But I need "DBX 30.0" to be input parameter to the function. I made pTag variable with data type...
Replies
19
Views
6,389
Hey all, I am currently working on converting many of my company's standard Rockwell Code to Siemens S7, and have run into an issue with indirect...
Replies
6
Views
4,801
I have spent a couple of weeks now hunting around for information to step7 indirect addressing. There are many, many discussions relating to this...
Replies
5
Views
10,377
Yes, I did search for information first. Yes I looked through the limited help files that came with my Step 7. No I do not have any other books...
Replies
77
Views
21,012
I have seen many posts relating to indirectly addressing arrays but none seem to fully explain how to do it simply. At the moment I have an...
Replies
5
Views
5,497
Back
Top Bottom