Loading .Pre in an array of timers?

dginbuffalo

Member
Join Date
Dec 2010
Location
Buffalo,NY
Posts
630
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.
 
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++.
 
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!
 
Why wouldn't you COP? If you're uniformly overwriting timers, presumably none are currently running, right?
 
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
 
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:
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...

TIMER_PRE_INDEXING.bmp


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.

INITIALIZED_TIMER.bmp


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
 
Well, I already use timerToCopy as the parameter! :D 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

COP.PNG COP2.PNG
 
Last edited:
See, you do need to be careful here!...

Geospark said:
...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...

JeremyM said:
...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...

COP%20instruction%20-%20data%20type%20mismatch.bmp


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...

COP%20instruction%20-%20data%20type%20match.bmp


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...

COP%20instruction%20-%20DINT-DINT_ARRAY%20data%20type%20match%26mismatch.bmp


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...

TIMER.PRE_Index_Initialize.bmp


Regards,
George
 
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...
 
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 :(

valve timer mov.jpg
 
George, great post, keep them coming!

Sorry OkiePC, the customer now needs 259 timers initialized. :D

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...
 
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.
 

Similar Topics

Hi guys, I have had some issues with uploading a program from a Micro 850 PLC. This is the first time connecting so I don’t have a file on my...
Replies
8
Views
197
Hi everyone, I want to upload the program from my CPU317TF-2 DP to my PC via connecting to CP 343-1 Advanced since it has LAN ports and the CPU...
Replies
1
Views
121
Hi all, Im stuck trying to support a client with Motorola ACE.. I was able to upload all of the other sites to STS but one. Im working remotely...
Replies
0
Views
77
how do you load from [A] floppy disk to laptop 9030 program
Replies
3
Views
134
I have a L82 system setup redundant that i need to download a lot of programs to in a critical environment. What is the process to do this and...
Replies
10
Views
261
Back
Top Bottom