You are not registered yet. Please click here to register!


 
 
plc storereviewsdownloads
This board is for PLC Related Q&A ONLY. Please DON'T use it for advertising, etc.
 
Try our online PLC Simulator- FREE.  Click here now to try it.

---------->>>>>Get FREE PLC Programming Tips

New Here? Please read this important info!!!


Go Back   PLCS.net - Interactive Q & A > PLCS.net - Interactive Q & A > LIVE PLC Questions And Answers

PLC training tools sale

Reply
 
Thread Tools Display Modes
Old March 9th, 2016, 06:23 PM   #1
dginbuffalo
Member
United States

dginbuffalo is offline
 
Join Date: Dec 2010
Location: Buffalo,NY
Posts: 608
Loading .Pre in an array of timers?

Hey guys- I have an array of 30 timers. I want to copy a # into all 30 of the .PRE registers. Can I use a copy instruction into Timer[0].Pre with a length of 30? Will this move the data into all the .pre registers or will it just overwrite the next 30 registers no matter what they are? Anyone know how to do this? Trying to set up a operator defined timer that populates all timers. Thanks.
  Reply With Quote
Old March 9th, 2016, 06:29 PM   #2
Ken Roach
Lifetime Supporting Member + Moderator
United States

Ken Roach is offline
 
Ken Roach's Avatar
 
Join Date: Apr 2002
Location: Seattle, WA
Posts: 14,259
Timers in A-B systems are constructed of a block three integers (Control, Preset, Accumulator) so you'd end up writing over a bunch of the other words if you tried a block copy.

You've got to use a For/Next loop with an index, or just brute-force it.

When I do brute force stuff like this I like to just put it in a Structured Text subroutine and call that with a JSR, so it doesn't end up taking up a long stretch of my ladder diagram.

Plus, it's easier to cut/paste/increment in ST using tools like Notepad++.
  Reply With Quote
Old March 9th, 2016, 07:10 PM   #3
dginbuffalo
Member
United States

dginbuffalo is offline
 
Join Date: Dec 2010
Location: Buffalo,NY
Posts: 608
Quote:
Originally Posted by Ken Roach View Post
Timers in A-B systems are constructed of a block three integers (Control, Preset, Accumulator) so you'd end up writing over a bunch of the other words if you tried a block copy.

You've got to use a For/Next loop with an index, or just brute-force it.

When I do brute force stuff like this I like to just put it in a Structured Text subroutine and call that with a JSR, so it doesn't end up taking up a long stretch of my ladder diagram.

Plus, it's easier to cut/paste/increment in ST using tools like Notepad++.
I was afraid of that Thanks for the quick reply!
  Reply With Quote
Old March 9th, 2016, 07:34 PM   #4
JeremyM
Member
United States

JeremyM is offline
 
Join Date: May 2014
Location: Texas
Posts: 357
Why wouldn't you COP? If you're uniformly overwriting timers, presumably none are currently running, right?
  Reply With Quote
Old March 9th, 2016, 09:24 PM   #5
dginbuffalo
Member
United States

dginbuffalo is offline
 
Join Date: Dec 2010
Location: Buffalo,NY
Posts: 608
Quote:
Originally Posted by JeremyM View Post
Why wouldn't you COP? If you're uniformly overwriting timers, presumably none are currently running, right?
It doesn't matter if they're running or not. The issue is they won't be copied to each .pre register respectively (timer[0].pre, timer[1].pre, etc, etc). they will be written to 30 consecutive registers which may be timer[0].pre, timer[0].acc,timer[0].en, etc, etc. It doesn't know to copy "directly" to each .pre register, it just copies to the next 30 registers in a row, starting with timer[0].pre
  Reply With Quote
Old March 9th, 2016, 10:19 PM   #6
JeremyM
Member
United States

JeremyM is offline
 
Join Date: May 2014
Location: Texas
Posts: 357
Quote:
Originally Posted by dginbuffalo View Post
It doesn't matter if they're running or not. The issue is they won't be copied to each .pre register respectively (timer[0].pre, timer[1].pre, etc, etc). they will be written to 30 consecutive registers which may be timer[0].pre, timer[0].acc,timer[0].en, etc, etc. It doesn't know to copy "directly" to each .pre register, it just copies to the next 30 registers in a row, starting with timer[0].pre
Sure they can! Write into the first element, then copy it to the rest:

XIC copToggle ONS OneShots[0] BST MOV timerToCopy.PRE timerArray[0].PRE MOV timerToCopy.ACC timerArray[0].ACC NXB COP timerArray[0] timerArray[1] 29 BND

// thirty timers are identical now

Last edited by JeremyM; March 9th, 2016 at 10:24 PM.
  Reply With Quote
Old March 10th, 2016, 12:07 AM   #7
Geospark
Lifetime Supporting Member
Ireland

Geospark is offline
 
Geospark's Avatar
 
Join Date: Feb 2012
Location: Kildare
Posts: 2,410
This simple couple of rungs of logic should work to index through all 30 TIMER[X].PRE elements in just 30 scans.

I haven't tested it live as I just threw it together rather quickly, but fundamentally I think it should do the trick...



Note: this just writes to the .PRE elements of each TIMER in the array and is not how I would normally do it.

Moving on...

You will also notice from the Tag window how the TIMER[X].PRE elements are not contiguous, as is being explained and this is why the COP instruction will not work when defining the Destination as TIMER[0].PRE.

But also note that even though the predefined TIMER data type is not made up of 3 x DINT elements, but instead 2 x DINT and 3 x BOOL, it is still a Structured data type.

NB: A COP instruction will operate on Structured data types, but you must be careful.

The COP instruction simply copies the Source data type to the Destination data type in a byte-wise fashion until the Length of the Destination data type is satisfied. So if the starting element defined for the Destination is part of a Structured data type, and is a DINT, the COP instruction will continue writing to consecutive bytes until the Length in DINTs is reached.

Example1:

COP
Source NEW_TIMER_PRE
Dest TIMER[0].PRE
Length 2

The Source is a DINT.
The Destination is a DINT or 4 x bytes.
So the Length is 2 x DINT or 8 x bytes to be copied.
The second element of the TIMER[0] structure is TIMER[0].ACC, which is also a DINT or 4 x bytes.

So the COP instruction will copy the Source DINT's 4 x bytes into TIMER[0].PRE until its DINT's 4 x bytes are written and Length count 1 is done.
Then the Source DINT's 4 x bytes are copied again to the next contiguous 4 x bytes after TIMER[0].PRE, which happens to be TIMER[0].ACC, until its DINT's 4 x bytes are written and Length count 2 is done.

So in this example both TIMER[0].PRE and TIMER[0].ACC have been copied the same value as the Source DINT with no overwriting in bytes to further elements of the structure.

Example2:

COP
Source NEW_TIMER_PRE
Dest TIMER[0].PRE
Length 3

The Source is a DINT.
The Destination is a DINT or 4 x bytes.
So the Length is 3 x DINT or 12 x bytes to be copied.
The second element of the TIMER[0] structure is TIMER[0].ACC, which is also a DINT or 4 x bytes.
The third member of the TIMER[0] structure is TIMER[0].EN which is a BOOL. As are members four and five - TIMER[0].TT and TIMER[0].DN.

As BOOL members are packed into data types using an 8 bit boundary or single byte, these three subsequent members are part of the ninth byte deep into this structure...

TIMER structure = 12 bytes:

Byte 01 = .PRE bits 0-7
Byte 02 = .PRE bits 8-15
Byte 03 = .PRE bits 16-23
Byte 04 = .PRE bits 24-31
Byte 05 = .ACC bits 0-7
Byte 06 = .ACC bits 8-15
Byte 07 = .ACC bits 16-23
Byte 08 = .ACC bits 24-31
Byte 09 = .EN bit 0, .TT bit 1, .DN bit 2 (bits 3-7 not used)
Byte 10 = Reserved for TIMER functions
Byte 11 = Reserved for TIMER functions
Byte 12 = Reserved for TIMER functions

So...

The COP instruction will likewise copy the Source DINT's 4 x bytes to both TIMER[0].PRE and TIMER[0].ACC and the Length count will be 2. For Length count 3 the Source DINT's 4 x bytes will continue to write to the next 4 available bytes...

Source DINT byte 1 > TIMER[0] byte 09 (.EN, .TT, .DN)
Source DINT byte 2 > TIMER[0] byte 10 (Reserved)
Source DINT byte 3 > TIMER[0] byte 11 (Reserved)
Source DINT byte 4 > TIMER[0] byte 12 (Reserved)

So with a Length of 3 the Source DINT has now been copied 12 x bytes deep into the entire TIMER[0] structure. If we set a Length of 4 the Source will now carry on and copy into the next 4 x bytes which happens to be the TIMER[1].PRE DINT in the next array member.

So a COP instruction, with a Length of 30 and Destination of TIMER[0].PRE in an array of 30 TIMER structures, would overwrite 120 bytes of contiguous data from TIMER[0].PRE to the second byte in TIMER[2].ACC.

Now that we have that out of the way, let's get back to what JeremyM is proposing...

Remember, the COP instruction can write to a structured data type. But it can also write from them as well.

If you really want to prime or initialize an array of TIMER data types to all be the same in every way i.e. all members identical, then you can simply use an initialized TIMER data type tag as the Source and copy it for the Length of your array, while specifying the Destination as the first TIMER[0] member itself within the array, as opposed to the first element TIMER[0].PRE within the first member...

Example3:

COP
Source INITIALIZED_TIMER (TIMER data type)
Dest TIMER[0] (First TIMER data type member in array)
Length 30

Now the Destination is defined as a TIMER data type which dictates that the Length will be defined as the number of bytes in the Destination data type i.e. 12 bytes to be copied.

So...

INITIALIZED_TIMER x 12 bytes will be copied to TIMER[0] x 12 bytes for Length count 1.
INITIALIZED_TIMER x 12 bytes will be copied to TIMER[1] x 12 bytes for Length count 2.
.
.
.
INITIALIZED_TIMER x 12 bytes will be copied to TIMER[29] x 12 bytes for Length count 30.



Because the Source and Destination are a matching structured data type it performs perfectly fine and there is no writing short of or outside of the boundaries of the array.

You could also initialize TIMER[0] instead of my INITIALIZED_TIMER and then copy to TIMER[1], etc. instead, as JeremyM has demonstrated, but I prefer to have a separate timer for priming first.

Regards,
George
__________________
"A little nonsense now and then is relished by the wisest men".
  Reply With Quote
Old March 10th, 2016, 12:36 AM   #8
JeremyM
Member
United States

JeremyM is offline
 
Join Date: May 2014
Location: Texas
Posts: 357
Well, I already use timerToCopy as the parameter! Have a look at your resulting array after each of these executes:

COP timerToCopy timerArray[0] 30

BST MOV timerToCopy.PRE timerArray[0].PRE NXB COP timerArray[0] timerArray[1] 29 BND
Attached Images
File Type: png COP.PNG (10.1 KB, 27 views)
File Type: png COP2.PNG (6.9 KB, 24 views)

Last edited by JeremyM; March 10th, 2016 at 12:44 AM.
  Reply With Quote
Old March 10th, 2016, 08:42 AM   #9
dginbuffalo
Member
United States

dginbuffalo is offline
 
Join Date: Dec 2010
Location: Buffalo,NY
Posts: 608
Wow- some good info on here! Thanks for all the suggestions- now to digest them!
  Reply With Quote
Old March 10th, 2016, 07:03 PM   #10
Geospark
Lifetime Supporting Member
Ireland

Geospark is offline
 
Geospark's Avatar
 
Join Date: Feb 2012
Location: Kildare
Posts: 2,410
See, you do need to be careful here!...

Quote:
Originally Posted by Geospark
...NB: A COP instruction will operate on Structured data types, but you must be careful...

...Because the Source and Destination are a matching structured data type it performs perfectly fine and there is no writing short of or outside of the boundaries of the array...
Quote:
Originally Posted by JeremyM
...Have a look at your resulting array...
My mistake and thank you JeremyM!

But this error provides us with a prime example of where a programmer needs to be careful when using a COP instruction and trying to copy structured data types, or indeed any data types which do not match...

In my example, which I had also not tested, but written out quickly from memory, the Source and Destination are NOT matching structured data types as far as the COP instruction is concerned...

INITIALIZED_TIMER tag is data type TIMER
TIMER[0] element is also data type TIMER
But, the array is data type TIMER[30]

Even though the Source and Destination are both essentially TIMER data types; the fact that TIMER[0] is a member of an array, the COP instruction looks to the Destination data type as that of the array and not the assigned element itself.

Therefore, because TIMER[0] is used as the Destination in the COP instruction, and the Length is defined by the Destination data type, the Length is defined as a unit of data type TIMER[30], and not TIMER.

So now we have a Source of data type TIMER and a Destination of data type TIMER[30]. The Source is now too small for the amount of data in bytes that the COP instruction will look to write to the destination.

But does the COP instruction care?

The answer is NO!

When the COP instruction executes, it will write the 12 bytes of the Source TIMER data type to the first 12 bytes of the Destination. But now the Source has no more bytes of data. However, the COP instruction will blindly start reading the next contiguous bytes after the Source tag in the processor's memory, which of course could be anything!

This can be seen here after I've executed the logic. Note how the first 12 bytes of the Source tag INITIALIZED_TIMER have been correctly copied to the first 12 bytes of the Destination data type i.e TIMER[0]. After that the COP instruction has started copying erroneous bytes of data from memory and the remaining TIMER elements of the array are populated with undesirable data...



So the COP instruction does not work correctly when copying Source data from one data type to the same Destination data type when the Destination data type is inside an array. The array data type will dictate the Destination data type and undesired data will result.

For the COP instruction to work correctly, with an array element as the Destination, you must copy from within the array, as JeremyM is demonstrating. This way both the Source and Destination will be of the same data type with respect to the COP instruction.

So we use the INITIALIZING_TIMER, or "timerToCopy" to prime the first TIMER array element, and then copy this first element to the rest of the array elements. You can see this demonstrated in the following screenshot.

Note: I have intentionally set .ACC to 50 and .TT to 1 to further demonstrate that the COP instruction is populating the TIMER array correctly...



The above is using the structured TIMER data type, but the same can be said for atomic data types, such as DINT. In this screenshot you can see the results from using both methods to copy MYDINT into DINT_ARRAY[30] and how the first method results in erroneous data while the second method is valid...



Also notice again that, while using the incorrect method, the first 4 bytes representing the Source DINT are successfully copied to the first Destination array element, and all data after that is erroneous.

JeremyM,

I'm so glad that I did not test the above first and that you spotted my mistake. It has proven very useful in pointing out exactly what I was warning users to avoid.

Also, I have tested the indexing logic I provided above where we only write to the .PRE element of each TIMER member, and it works. This might be useful if a scenario arose where you do not want to initialize all elements of the TIMER array members. Some applications might require updating the .PRE value mid TIMER execution, without interfering with the .ACC or BOOL elements at that time.

Here is the indexing logic before and after...



Regards,
George
__________________
"A little nonsense now and then is relished by the wisest men".
  Reply With Quote
Old March 10th, 2016, 07:05 PM   #11
OkiePC
Lifetime Supporting Member
United States

OkiePC is offline
 
OkiePC's Avatar
 
Join Date: Mar 2005
Location: ENE of Nowhere Oklahoma
Posts: 9,937
6 rungs.

5 MOVs in series on each rung.

Done.

No worrying about upsetting accumulators or done bits.
No worrying about confusing the next guy who looks at this code in a year who could very well be the same guy who writes it.

Brute force is a beautiful thing. I also find it to be quite elegant in most cases, not to mention faster...
__________________
It's not all the variables I am most concerned with, it's the undiscovered constants.
  Reply With Quote
Old March 10th, 2016, 07:27 PM   #12
dginbuffalo
Member
United States

dginbuffalo is offline
 
Join Date: Dec 2010
Location: Buffalo,NY
Posts: 608
Well, being an odd bird- I decided to try something different! Not sure if this will work but what i did is made a "master timer" that is just a tag without logic. If the hmi time setting doesn't equal the master timer.pre, I MOV the hmi time into the master timer.pre . After that I copy the master timer to the timer array 60 dimensions.

If I'm understanding everyone correctly, I can copy a timer data type to an array of timers. Think it will be ok? No PLC to test on
Attached Images
File Type: jpg valve timer mov.jpg (26.7 KB, 74 views)
  Reply With Quote
Old March 10th, 2016, 07:29 PM   #13
JeremyM
Member
United States

JeremyM is offline
 
Join Date: May 2014
Location: Texas
Posts: 357
George, great post, keep them coming!

Sorry OkiePC, the customer now needs 259 timers initialized.
  Reply With Quote
Old March 10th, 2016, 07:31 PM   #14
OkiePC
Lifetime Supporting Member
United States

OkiePC is offline
 
OkiePC's Avatar
 
Join Date: Mar 2005
Location: ENE of Nowhere Oklahoma
Posts: 9,937
Quote:
Originally Posted by JeremyM View Post
George, great post, keep them coming!

Sorry OkiePC, the customer now needs 259 timers initialized.
Okay, so it's no longer beautiful, put the rungs in a subroutine so no one has to look at them

Actually above about 50 of them, I would break down and use a loop with indirection...
__________________
It's not all the variables I am most concerned with, it's the undiscovered constants.
  Reply With Quote
Old March 10th, 2016, 07:32 PM   #15
JeremyM
Member
United States

JeremyM is offline
 
Join Date: May 2014
Location: Texas
Posts: 357
dginbuffalo, read Geo's most recent post. You can't COP an atomic type to the entire array; you need to COP to the first element only, then copy the first element to the remainder of the array.
  Reply With Quote
Reply
Jump to Live PLC Question and Answer Forum

Bookmarks


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Similar Topics
Thread Thread Starter Forum Replies Last Post
How To Enter Rslogix 5000???? sundar_scada LIVE PLC Questions And Answers 14 April 2nd, 2016 04:50 PM
Variable array size call to add-on instruction - A solution Mr Modbus LIVE PLC Questions And Answers 4 July 17th, 2015 08:59 AM
FIFO Array of active Timers in ControlLogix5555 radfahrer LIVE PLC Questions And Answers 7 February 23rd, 2007 07:07 AM
Need WinCC Option for truck loading system huynt LIVE PLC Questions And Answers 7 July 14th, 2005 02:18 AM
Building an S7 Array Move Questions DesertDog LIVE PLC Questions And Answers 19 June 17th, 2004 10:51 AM


All times are GMT -5. The time now is 02:43 AM.


.