Siemens S7 Indirect Addressing

MaxRoberts22

Member
Join Date
Jul 2007
Location
Conshohocken, PA
Posts
10
I'm new to S7 programming and finding it a whole lot like running backwards through a corn field!

I am helping my company develop our current standards in S7. So I know the logic is sound, but the "uniquenesses" of S7 are presenting challenges.

I would like to develop a starndard motor routine with several inputs and outputs. Instead of making an FB (with an instance DB), I'd like to create an FC for each motor and have it point to one DB that contains all of my motor data. This DB contains 4 instances of a motor UDT I created (e.g., M100, M101, M200, and M201). Each FC would have an input integer that would point to a starting byte location in the big motor data DB. I would like to create logic in the routine to increment the incoming pointer integer so that I could point to other bytes and bits in the DB as well.

My problem is with the indirect addressing of the DB location. In addition to the DB byte location, I'd like to be able to dynamically point the DB number itself (i.e. DB[xxx].DBy[zzz] where xxx = indirected DB number; y = data types X, W, and D; zzz = indirected byte or bit location).

Is there a way to dynamically address both the DB number and DB byte location, even down to the bit location? Again, I'd like to pass into the FC a DB number and a DB starting byte.
 
This has frequently been discussed. Do a search for "Step7 indirect addressing" and you'll get enough hits to get you started.

That said, I'm not sure that I understand why you want to go this route rather than using the FB/IDB (or multi-instance for several motors) system.
 
All DB[xxx].DBy[zzz] accesses require you to open the correct DB first and then refer to the address. Your code will boil down to the following where you will dynamically allocate iDBNumber for the data block and the contents of AR1 to point to the data you are after.

Code:
OPN DB[iDBNumber]
A DBX[AR1,P#0.0] //bit access
L DBB[AR1,P#0.0] //byte access
L DBW[AR1,P#0.0] //word or int access
L DBD[AR1,P#0.0] //dword, dint, or float access

Edit: added simple example
Code:
FUNCTION FC 122 : VOID
TITLE =
VERSION : 0.1

VAR_INPUT
  iDBNumber : INT ; 
  iStartByte : INT ; 
END_VAR
VAR_OUTPUT
  bBit0 : BOOL ; 
  bBit1 : BOOL ; 
END_VAR
VAR_TEMP
  iDB : INT ; 
END_VAR
BEGIN
NETWORK
TITLE =indirect address example
	  L	 #iDBNumber; 
	  T	 #iDB; 
	  OPN   DB [#iDB]; 
	  L	 #iStartByte; 
	  SLD   3; 
	  LAR1  ; 
	  A	 DBX [AR1,P#0.0]; 
	  =	 #bBit0; 
	  A	 DBX [AR1,P#0.1]; 
	  =	 #bBit1; 
END_FUNCTION
 
Last edited:
Max

Maybe you're meant to be running forwards, and this is actually a well-manicured lawn rather than corn field!

If I ignore your description of method, and concentrate solely on the purpose it does seem to me to be a task that eminently suits the use of FBs rather than FCs. I'm not sure why you expressed a preference for FCs - perhaps there are other ideas which you haven't explained. Have you encountered multiple-instance FBs?

I would consider constructing a standard motor control FB. Any code which you've already written for an FC can be cut and pasted to an FB. Now declare as many instances of that FB as you require in a second 'parent' FB. Declare them in the STAT section of the parent FB. Give each instance a name such as M100, M101 etc and a type of FBxx (replace xx with whatever number your 'child' motor control FB has.) When you call each of these instances in the code of your 'parent' you won't be prompted for a separate DB. It's only when you call the 'parent' FB that you then have to supply a single DB which will automatically enclose all the data structures associated with the actual motor FBs. They'll be listed by name and declaration within that single multi-instance DB.

This won't be as terse as constructing new addresses on the fly, but I'll bet the maintenance guys would love having to check out your indirect pointers anyway!

Just a thought.

Ken
 
I have done similar in the past and entered the start of the motor data as a pointer, and then used this to set up the parameters for a call to SFC20, block transfer.

I transferred the data from the data block into the local temp area, used the temps in the FC and then transferred the temps back into the Db at the end.

I presume this is similar to what you want to do?
 
Clarification...

I guess one of the big ideas was to contain all of the motor information in one DB. This would allow me to differentiate all of the motors by building, area, or unit operations.

Each motor data DB would represent some level of hierarchy. By creating one template FC, I would be able call any number of this FC with I and Q addresses as necessary, and input a DB number and DB starter byte to add and remove motors as necessary.
 
Each motor data DB would represent some level of hierarchy. By creating one template FC, I would be able call any number of this FC with I and Q addresses as necessary, and input a DB number and DB starter byte to add and remove motors as necessary.
What you are describing is classical FB+IDB usage. If you want to to cram all the motor data into one DB, then consider multiple instance as allready mentioned.
Another method (that has also been mentioned) is to pass the data adress as a pointer to the FC. This is very close to what you describe.
My advice: Forget passing a byte number. Use a pointer to point to a common DB with all the motors declared as UDT instances. This is much easier than it sounds. It may also be known as "S7Guy's method" on this forum.

In this thread I descibed how to use such a method, incidentally using an array of motors:
http://forums.mrplc.com/index.php?showtopic=11497#
You can just as well use a long list of similarly declared motors (and not an array) in the common motor DB.
 
JesperMP said:
My advice: Forget passing a byte number. Use a pointer to point to a common DB with all the motors declared as UDT instances.

Forgot to mention UDT's, that is exactly how I did it.
 
Yes, I have created a motor UDT. I have four instances of this UDT in my shared DB (DB2). I have created an FC block that contains has several inputs and outputs. My thought was to map the input/output parameters to points in the shared DB. For example:

A #Safe1S
= "Motor_Data".M100.Perm.Safe1

OR

A #Safe1S
= DB2.DBX 11.1

It has to be possible to begin my FC by opening the corresponding DB:

L #DBNum //where #DBNum = 2
T #DBNum_Temp
OPN DB [#DBNum_Temp]

Now when #Safe1S goes high, how do I get this logic to set DBX11.1 high? With a byte pointer, shouldn't I be able to increment a internal FC pointer to point to Byte 11, Bit 1 like my example shows?

A #Safe1S
= DBX[#InternalPtr1] //where #InternalPtr1 is a real value = 11.1

Since the UDT structure is maintained for each motor, I should be able to write logic to create pointers for different parts of the UDT and use these pointers to indirect different bits, bytes, words and double words.

I am I asking the impossible? Maybe my ControlLogix background is making me ask the wrong questions or look in the wrong places.
 
Last edited:
Originally posted by MaxRoberts22:

I am I asking the impossible? Maybe my ControlLogix background is making me ask the wrong questions or look in the wrong places.

You aren't asking the impossible. You can do what you are asking. However, as others have said, you might be doing it the hard way. That is where the other suggestions are coming from.

The Step7 online help has a pretty extensive description of pointer calculations and operations. If you really want to do the pointer thing you need to spend a few days reading that stuff. One 15 minute read probably won't do it. The trick really does come down to knowing the structure of a pointer and where to put the various values.

But Step7 will take care of most of this for you if for use multiple instance FBs with a common DB. Since you are a CLX guy this may help. Think of the set-up like an array of UDTs in the CLX. But with S7 the development environment babysits the selection of the UDT from the array for you.

In Step7 you can use a defined FB as a data type in a DB. For example, you have created your motor control FB (FB20), defining all the inputs, outputs, stats and temps you need to make the FB work as you intend. After you do this you create another FB, which we will call the 'master FB' for lack of anything better. In the master FB STAT declarations, you declare however many motors you need, such as Motor_1, Motor_2, etc, and use FB20 (ot its symbol) as the data type. Now comes the fun part. In the master FB you insert a function block call and use the STAT tagname as the function block number, for example Motor_1. Step7 recognises that you want to call FB20 using the data group associated with Motor_1. The function block call graphic (if you are using ladder) will show all the spots where you can send in inputs and draw out outputs. This is where you do your physical I/O mapping.

The upside to this is that Step7 handles all the memory management for you. It will construct the master FB's instance data block as necessary based on how you locate the motors in the STAT area. Everything is all together. The downside is that you will need to do data block regenerations if you need to modify the motor control FB. However, you would need to make multiple pointer edits using the pointer method if you changed the motor data block.
Keith
 
I know I'm swimming against the tide on this one, but what's wrong with having one DB per motor ? As you add functionality to the motor DB, the DB just gets bigger and the code does not need to be regenerated as it would be if the motor data was all in one DB.
The supervising block caller would only need to know the first motor DB (the link to the next DB would be part of the motor data DB - a linked list) and would call the controlling (or driver) FC as specified in the motor DB. The supervisor would then move on to the next DB until the end of the list was reached. Driver FC's can be added/updated with no change to the DB structure. Driver FC's can determine if they support certain features by examining the length of the motor DB for example.
 
One method:

You can point to the motors individual data area as an input parameter of the ANY format.

Example you have a DB with the symbol "motors" and it contains 6 motors, all of the UDT type "motorData" and called "M1" to "M6".

When creating the FC, set one of the input parameters as ANY, on the call to the FC place "Motors".M1 as the input parameter for the first, "Motors".M2 for the second call, etc.

This will give you a ANY pointer to the start area of the data for the motor, it will also store the length of the UDT as well.

You could then block transfer into local flags or save in AR1 and use known offsets to the data you want when required.
 
.... and another one......pass the UDT in as a parameter to the FC. The editor works out all the adresses for you and you get a symbolic reference to the variable in your FC, no indirect addressing required.
e.g.
Code:
TYPE UDT 1
VERSION : 0.1


STRUCT 
Speed : INT ; 
END_STRUCT ; 
END_TYPE

FUNCTION FC 1 : VOID
TITLE =
VERSION : 0.1


VAR_INPUT
Motor : UDT 1; 
END_VAR
BEGIN
NETWORK
TITLE =

L #Motor.Speed; 
END_FUNCTION
 
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
 
Okay, that's the solution that most sounds like ControlLogix to me. I know that if I were to create this setup in AB, I would write all of my code in one routine and do a search and replace for a generic place holder ("M100" replaces something like "motor_num"). In the case of S7, it's almost like passing in the UDT as a paramter is relatively equal to the search and replace. All of the generic motor addresses are set to the motor addresses in the big motor data DB.

But as I stated before, I have created a DB already that has four instances of the my motor UDT (in my case, M100, M101, M200, and M201). I would assume that I'm passing in M100 into my FC, because M100 is an instance of a UDT.

Since I'm looking to write to this UDT in the DB, I would assume it has to be created as an output parameter. Am I wrong?
 

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,323
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,756
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,326
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
20,950
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,486
Back
Top Bottom