S7 indirect addressing question

C

Thread Starter

Corne, Chris

<p>Hello,

<p>I am trying to write a function that will take in a DB number, starting address, address offset, number of words, comparison value, and return a count of non true equal comparisons. I have it working with the exception of being able to pass in the starting address. Below is what I have so far (From and .AWL source file). Specifically, I would like to get away from "hard coding" in the line "L P#DBX 30.0" so that this address is passed into the function.

<p>Thanks,

<p>Chris
<pre>
FUNCTION "Data_Counter" : VOID
TITLE =Count_Val
//Routine will take in a DB number, a comparison value, number of
comparisons to
//make, and an offset value (number of bytes between data words to compare)
VERSION : 0.1


VAR_INPUT
Comparison_Val : INT ; //The value comparing data against
DBNUM : BLOCK_DB ; //Data Block number
Offset : INT ; //How many bytes between words to compare
Quantity_Data : INT ; //How many words to compare
END_VAR
VAR_OUTPUT
Count_Val : INT ; //Number of true comparisons in the Range
END_VAR
VAR_TEMP
Cycle : INT ; //Cycle number for the counting routine
Count : INT ; //Current count of the true comparisons
END_VAR
BEGIN
NETWORK
TITLE =Count routine
//Count the words that do not equal the comparison value
OPN #DBNUM; //Open the data block
L P#DBX 30.0; //Starting Address, How do I do this with a
variable?
LAR1 ; //Put the starting address in address reg 1
L #Quantity_Data; //Load the number of Items to compare
Next: T #Cycle;
L W [AR1,P#0.0]; //Load the data word pointed at by AR1, offset by
zero
L #Comparison_Val;
==I ;
JC yes; //if =0, jump around incrementing the counter
L #Count;
INC 1;
T #Count;
L #Offset;
+AR1 ; //add Offset to the address register 1 to get to next word
JU jump;
yes: L #Offset; //add Offset to address register 1 to get to the next
word
+AR1 ;
jump: L #Cycle;
LOOP Next;
L #Count;
T #Count_Val; //transfer out the count value

END_FUNCTION
</pre>
 
<p>Hello Chris,

<p>Bellow you have the code modified by me (in awl format).

<p>Few notes:<br>
You said you have the program working. I don’t think this code ever worked in a PLC. I saw two evident mistakes:<br>
- At the very end you add an integer (#Offset) to an address pointer (AR1). This can never work!<br>
- You count your “not equal values” in the temp variable #Count without initializing first. You will never know what result you will get since local data stores the values from the last processing.

<p>Note the following changes to your program:<br>
- I added a new input variable called #First_Byte (To eliminate your hardware addressing).<br>
- I eliminated temp variable #Count. Calculation is done directly in #Count_Val.<br>
- Function is called FC_2 (I eliminated your symbol name. You can modify the number to the one matching your symbol table).<br>
- I reorganized the whole program in separate networks for easier understanding. (Note that in S7, increasing the number of networks does not increase the size of executable code; as opposed to S5).

<p>Since I believe you are a Coop student (or maybe a technician taking an S7 class) I hope you will get a good grade!

<p>ValRo ([email protected])<br>
E-mail me if you have problems generating / importing the awl.
<pre>
FUNCTION FC 2 : VOID
TITLE =Count_Val
//Routine will take in a comparison value, DB number, first byte address, number
//of comparisons to make and an offset value (number of bytes between data words
//to compare).
AUTHOR : CorneC
FAMILY : CorneC
NAME : CompVal
VERSION : 0.1


VAR_INPUT
Comparison_Val : INT ; //The value comparing data against
DB_Num : BLOCK_DB ; //Data Block number
First_Byte : INT ; //Location of the 1st byte (of the word) used for comparison.
Offset : INT ; //How many bytes between words to compare
Quantity_Data : INT ; //How many words to compare
END_VAR
VAR_OUTPUT
Count_Val : INT ; //Number of true comparisons in the Range
END_VAR
VAR_TEMP
Cycle : INT ; //Cycle number for the counting routine
END_VAR
BEGIN
NETWORK
TITLE =Initialize values.

L 0;
T #Count_Val;
NETWORK
TITLE =Open the coresponding data block.

OPN #DB_Num; //Open the data block

NETWORK
TITLE =Load "First_Byte" address, format it and move it to AR1 .

L #First_Byte; //Starting Address.
SLW 3; //Transform byte address to bit address (echiv multiply with 8).
LAR1 ; //Load the starting address in address reg 1.
NETWORK
TITLE =Initialize and start loop.

L #Quantity_Data; //Load the number of Items to compare
Next: T #Cycle;
NETWORK
TITLE =Compare loaded value with reference value and jump if equal.

L DBW [AR1,P#0.0]; //Load the data word pointed at by AR1, offset by zero
L #Comparison_Val; //Reference value.
==I ;
JC yes; //if =0, jump around incrementing the counter
NETWORK
TITLE =Increment counting variable.

L #Count_Val;
+ 1;
T #Count_Val;
NETWORK
TITLE =Prepare address pointer for the next value to be compared.

yes: L #Offset; //add Offset to address register 1 to get to the next word
SLW 3;
+AR1 ;
NETWORK
TITLE =Loop end.
//Count the words that do not equal the comparison value
L #Cycle;
LOOP Next;
END_FUNCTION
</pre>
 
D

Dobrowolski, Jacek

<p>Hi Chris,

<p>Some ideas inserted below in your code. BTW, your offset won't move you by number of bytes as you want but rather by number of bits. And the FC will return wrong Count_Val after encountering more than 255 values non equal to the Comparison_Val.

<p>Regards,

<p>Jacek Dobrowolski
<pre>
FUNCTION "Data_Counter" : VOID
TITLE =Count_Val
//Routine will take in a DB number, a comparison value, number of
comparisons to
//make, and an offset value (number of bytes between data words to compare)
VERSION : 0.1


VAR_INPUT
Comparison_Val : INT ; //The value comparing data against
StartingAddress : INT ; //<-here, starting address you want
DBNUM : BLOCK_DB ; //Data Block number
Offset : INT ; //How many bytes between words to compare
Quantity_Data : INT ; //How many words to compare
END_VAR
VAR_OUTPUT
Count_Val : INT ; //Number of true comparisons in the Range
END_VAR
VAR_TEMP
Cycle : INT ; //Cycle number for the counting routine
Count : INT ; //Current count of the true comparisons
END_VAR
BEGIN
NETWORK
TITLE =Count routine
//Count the words that do not equal the comparison value
OPN #DBNUM; //Open the data block
// L P#DBX 30.0; //Starting Address, How do I do this with a
variable?
L StartingAddress ; //<-here
SLD 3 ; //<-here
LAR1 ; //Put the starting address in address reg 1
L #Quantity_Data; //Load the number of Items to compare
Next: T #Cycle;
// L W [AR1,P#0.0]; //Load the data word pointed at by AR1, offset
by
zero
L DBW [AR1,P#0.0] ; //<-here
L #Comparison_Val;
==I ;
JC yes; //if =0, jump around incrementing the counter
L #Count;
INC 1;
T #Count;
L #Offset;
+AR1 ; //add Offset to the address register 1 to get to next word
JU jump;
yes: L #Offset; //add Offset to address register 1 to get to the next
word
+AR1 ;
jump: L #Cycle;
LOOP Next;
L #Count;
T #Count_Val; //transfer out the count value

END_FUNCTION
</pre>
 
<p>Chris,

<p>Here is a code snippet from something I did, similar to what you are asking (I think).

<p>First I create a UDT to construct my ANY pointer:
See the section "Format of the Parameter Type ANY" in the Step7 help section for more information on this.
<pre>
TYPE UDT_ANY_Pointer
STRUCT
Syntax_ID : BYTE; //10 bytes of data to describe the ANY pointer, always 10h for S7
DataType : BYTE; //Type of data being stored BYTE := 2
Count : WORD; //Length of the data to be stored
DB_Number : WORD; //DB number in which to store the data
Byte_Pointer : DWORD; //Pointer to the memory area to store the data
END_STRUCT
END_TYPE

Next, create a VAR of the type UDT:
GetAnyData : UDT_ANY_Pointer;

Then:
GetAnyData.Syntax_Id := B#16#10; //10 bytes of data in the ANY pointer
GetAnyData.DataType := 2; //Data type := 2 (BYTE)
GetAnyData.Count := INT_TO_WORD(BYTE_Count); //Number of bytes of consistent data
GetAnyData.DB_Number := IDBPointer.DB_Number;; //Instance DB number for the call
GetAnyData.Byte_Pointer := BytePointer; //Pointer to Byte offset in instance DB
</pre>
<p>This code will go out to the specified DB, grab the number of BYTES of data contained in the Count, from the offset indicated in Byte_Pointer.

<p>Hope this will help... Scott
 
D

Dobrowolski, Jacek

Dear ValRo,

You are terribly wrong. Local data (VAR_TEMP) are not stored after execution of FC/FB they belong to. And at each start of FC/FB local data are initialised by the OS.

One more thing. Your code will fail with First_Byte or Offset greater than 8 k. Also use of negative Offset will fail. While converting INT type into pointer address part you should use SLD not SLW.

Regards,

Jacek Dobrowolski
 
D

Dobrowolski, Jacek

Dear Scott,

I'm afraid that this code won't grab any data by itself as it only initialises variable of type ANY. You need to call BLKMOV or UBLKMOV function to get this data. However I can't see how it solves the problem Chris is facing with.

Regards,

Jacek Dobrowolski
 
Or.............

Instead of re-inventing the wheel you can use FC86 in the TI-S7 Converting Blocks Library. It even has an option to search for values that are equal or un-equal......that would impress your instructor
 
C

Corne, Chris

S7 Gurus (From Webster:2 a : a teacher and especially intellectual guide in matters of fundamental concern),

Thank you for the guidance on this indirect addressing code. Here is what I have done:

1) The shifting of the offset with the SLD 3 I had done, but I had not updated the source, sorry for posting source that hadn't caught up to my own revisions. You guys are good! My local AE provided the lines that I decided to use.. L #Offset; L P#1.0; *D ; +AR1 ; //add Offset to the address register 1 to get to next word

2) After reviewing the postings, talking to a local S7 programmer, and reading Berger some more, at the bottom my post is how I attacked the pointer..

3) Unfortunately, I am not a tech in a class or a student. I am working on my first S7 project for work. I don't have an engineering degree, but I did sleep at a Holiday Inn Express recently. I am very appreciative of the support that you guys here on the List provide. Reading Berger and looking at the online help and trying to create a solution for a problem is akin to reading the rules of chess and then playing the game...knowing the rules does not make a chess player, it's only the beginning!

My intentions on the rest of this block to make it more versatile will be to add in jumps for other comparisons as well (=,<,> etc) as well as some error catching if the offset is too large or negative as some of the excellent replies have pointed out.

FUNCTION "Data_Counter3" : VOID TITLE =Count_Val //Routine will take in a DB number, a comparison value, number of comparisons to //make, and an offset value (number of bytes between data words to compare) VERSION : 0.1

VAR_INPUT Comparison_Val : INT ; //The value comparing data against Offset : INT ; //How many bytes between words to compare Quantity_Data : INT ; //How many words to compare Start_Point : POINTER ; //Offset to initial comparison value in DB END_VAR VAR_OUTPUT Count_Val : INT ; //Number of true comparisons in the Range END_VAR VAR_TEMP Cycle : INT ; //Cycle number for the counting routine Count : INT ; //Current count of the true comparisons DBNum : INT ; //DB # of data to compare END_VAR BEGIN NETWORK TITLE =Count routine //Count the words that do not equal the comparison value L P##Start_Point; //points to parameter in calling temp memory LAR1 ; //..get DB# from bytes 0..1 of POINTER L W [AR1,P#0.0]; //..open data block, then T #DBNum; OPN DB [#DBNum]; //..indirectly access bytes 2..5 of POINTER L D [AR1,P#2.0]; //..to get bit offset of Start_Point LAR1 ; //Put the starting address in address reg 1 L 0; //Initialize current count T #Count; L #Quantity_Data; //Load the number of Items to compare
Next: T #Cycle;
L W [AR1,P#0.0]; //Load the data word pointed at by AR1, offset by zero L #Comparison_Val; ==I ; JC yes; //if =0, jump around incrementing the counter L #Count; INC 1; T #Count; L #Offset; L P#1.0; *D ; +AR1 ; //add Offset to the address register 1 to get to next word JU jump;
yes: L #Offset; //add Offset to address register 1 to get to the next
word L P#1.0; *D ; +AR1 ;
jump: L #Cycle;
LOOP Next; L #Count; T #Count_Val; //transfer out the count value

END_FUNCTION
 
D

Dobrowolski, Jacek

Dear Chris,

Please tell me more... :)

There's still one issue with your code. Instruction INC 1 will roll over after counting up to 255. I'd advise you to use simply +1 instead. And as I'm crazy of code speed (as well as quality of course :) ) then I must point the difference between SLD 3; and L p#1.0; *D; The first one is 3x faster on an average S7-300 and 14x(!) faster on S7-400.

Regards,

Jacek Dobrowolski

<...>
FUNCTION "Data_Counter3" : VOID
TITLE =Count_Val //Routine will take in a DB number, a comparison value,
number of comparisons to
//make, and an offset value (number of bytes between data words to compare)
VERSION : 0.1
VAR_INPUT
Comparison_Val : INT ; //The value comparing data against
Offset : INT ; //How many bytes between words to compare
Quantity_Data : INT ; //How many words to compare
Start_Point : POINTER ; //Offset to initial comparison value in DB
END_VAR
VAR_OUTPUT Count_Val : INT ; //Number of true comparisons in the Range
END_VAR
VAR_TEMP
Cycle : INT ; //Cycle number for the counting routine
Count : INT ; //Current count of the true comparisons
DBNum : INT ; //DB # of data to compare
END_VAR
BEGIN
NETWORK
TITLE =Count routine //Count the words that do not equal the comparison
value
L P##Start_Point; //points to parameter in calling temp memory
LAR1 ; //..get DB# from bytes 0..1 of POINTER
L W [AR1,P#0.0]; //..open data block, then
T #DBNum;
OPN DB [#DBNum]; //..indirectly access bytes 2..5 of POINTER
L D [AR1,P#2.0]; //..to get bit offset of Start_Point
LAR1 ; //Put the starting address in address reg 1
L 0; //Initialize current count
T #Count;
L #Quantity_Data; //Load the number of Items to compare
Next: T #Cycle;
L W [AR1,P#0.0]; //Load the data word pointed at by AR1, offset by zero

L #Comparison_Val;
==I ; JC yes; //if =0, jump around incrementing the counter

L #Count;
INC 1;
T #Count;
L #Offset;
L P#1.0;
*D ;
+AR1 ; //add Offset to the address register 1 to get to next word
JU jump;
yes: L #Offset; //add Offset to address register 1 to get to the next
word
L P#1.0;
*D ;
+AR1 ;
jump: L #Cycle;
LOOP Next;
L #Count;
T #Count_Val; //transfer out the count value

END_FUNCTION
<...>
 
A

Andrew Hawdon

Hi,

The best solution is not to try coding in STL at all for indirect addressing. You should use SCL a pascal type add on for step 7. It makes indirect addressing very simple, well worth the cost.

Andrew
 
D

Dobrowolski, Jacek

Dear Andrew,

There're many factors to be considered. It cannot be said generally that SCL is better than STL in this particular case.

Points for SCL:
- simple code (you don't have to bother of the pointers calculations)
- access to arrays possible via indexing
- you don't have to learn the CPU architecture
- you'll get solution faster and with less coding

Points for STL:
- built-in in Step 7 (SCL you have to buy separately if you don't have Step 7 Pro)
- code does exactly what you want and only this (code written in SCL does a lot of thing you don't want it to do)

The last point means that you get from the SCL compiler code with huge overhead (huge means few times larger than same thing done in STL) and very slow.

Example: simple "for" loop moving array cells (core of shift register) is 8 lines of code written manually in STL and more than 90 lines of code generated by SCL compiler.

So it depends on you to choose. If you have enough of memory and you are satisfied with scan time go with SCL. But if not - forget it and write your code in STL.

Regards,

Jacek Dobrowolski
 
A

Andrew Hawdon

Dear Jacek

You are fundamently correct, however the compiler for SCL has been much improved in the recent versions of SCL and hence the overhead in terms of code generated by SCL is now far smaller. PLC memory is also now far larger with processing times for instructions far shorter. As you point out it is horses for courses.

Andrew
 
RAM is plentiful, speed is plentiful, I have frequently had to install a CPU with <20% utilization of its resources, just to get the addressing room I need for the hardware, this is particularly significant with Profibus slaves such as VSD's if you use a big footprint.

As far as SCL goes, there are many many approaches to indirect addressing which SCL makes practically possible, and which I would avoid by any means possible from doing in STL code, note I say avoid, not impossible to implement, the distinction is that someone following after you must be able to understand what you did. This particularly in indirect addressing in STL is a problem and I have found that SCL sets me free to do many things I just wouldn't consider any other way.

Remember too that there are different ways of programming in SCL, just as there are in STL, you can usually improve the size speed and compactness of your SCL code if you work hard enough at it. Very often the appropriate use of Standard functions and function blocks from within SCL code can also significantly shorten things when you most need it, however adopting the same approach from STL is not always a suitable alternative.

Donald P
 
S7
can everybody show STL basci-code loop for array int and array real

FC
in
InputInt array (0..5) of int
out
OutReal array (0..5) of real
temp
counter int

// loop begin

L #InputInt [#counter]
itd
dtr
T #OutReal [#counter]

// loop end

The principle suits for SCL but with STL needed inderect adressing with pointer
 
It should look similar to this:

L p##InputInt //load pointer
LAR1
L p##OutReal
LAR2
L 6
ET: T LoopCounter
L W[AR1,p#0.0]
ITD
DTR
T D[AR2,p#0.0]
L LoopCounter
LOOP ET

Best regards,
Jacek
 
Is the code for FB?
FC dont have internal addressing.
Or we can't it see?

What adress or can adress L W[AR1,p#0.0] and D[AR2,p#0.0] ?

Thaks in advance
 
ps
>What address or can address L W[AR1,p#0.0] and T D[AR2,p#0.0]

or must be
L W#InputInt[AR1,p#0.0]
and
T D#OutReal[AR2,p#0.0] ?
 
Is the code for FB? - No.
FC doesn't have internal addressing.
Or we can't it see?

What address or can address L W[AR1,p#0.0] and D[AR2,p#0.0] ? - Depends on what is in ARs.

Best regards,

Jacek
 
Jasek,

>Is the code for FB? - No.
>What address or can address L W[AR1,p#0.0] and T D[AR2,p#0.0]? - Depends on what is in ARs

why In this case with L W[AR1,p#0.0] and T D[AR2,p#0.0] at Inputs and Outputs of the FC-Block "emtpy operand"?
 
Top