Logix 5000 UDINT from two INT

Paul351W

Member
Join Date
Mar 2008
Location
Northern Illinois
Posts
154
I am working on a project with a Controllogix 5582 processor using v32 firmware where I will be communicating with a Genset using Modbus TCP. It is sending power in Watts over two 16 bit Modbus registers, with a scaling value of 1w/bit, and an offset of -2,000,000,000 for a range of -2,000,000,000 to +2,211,081,215W.

If I try to use a UDINT data type, I cannot use BTD or COP/CPS to get the two 16 bit values into the UDINT. If I try to use a LINT, then I can only use COP/CPS, which would require me to first use BTD's to get the 16 bit values into a DINT, then add an extra DINT tag (value of zero) to use in the COP to zero out the upper 32 bits of the LINT

Is there a better way to do this that I am missing, other than maybe using XIC's and OTE's on a LINT?
 
So if the two Modbus registers are 0, then the raw value is 0, and the power value after applying the offset is -2,000,000,000W?

And if the two Modbus registers combine to be 4,211,081,215, then the power value after applying the offset is 2,211,081,215?
 
If I try to use a UDINT data type, I cannot use BTD or COP/CPS to get the two 16 bit values into the UDINT.


Also, I don't understand why this does not work, specifically the COP Modbus_16int_array[0] dest_udint 1, assuming the two 16-bit UINT/INT values from Modbus are in adjacent locations in that array?
 
Frequently encounter Real / Floating Point values read from a Modbus Master as two consecutive Register Addresses that need to be converted to REAL.

As above, a COP instruction using the first modbus Register & Address as the Source (eg Register[First Address of data] and a DINT as the Destination correctly performs the conversion.
The COP has a DINT (4 bytes) as the Destination data type and a length of 1, so it copies (1 * 4) contiguous bytes of Logix memory starting at Register[First Address of data] to the DINT.
4 bytes = 2 * 2 byte INT's (the two Modbus Addresses).

However, this does not always give the expected result.

There are 4 choices in Modbus implementations to encode floating point and long integer values into consecutive Addresses that I am aware of:
1. Big endian
2. Big endian with byte swap
3. Little endian
4. Little endian with byte swap

Device makers decide which format to encode the data in. Non byte swapped is the most common I have seen.

If the result of the COP is incorrect, use a SWPB (Swap Byte) instruction to byte swap the Modbus Addresses to mapped input INT's for the COP instruction.
 
Last edited:
So if the two Modbus registers are 0, then the raw value is 0, and the power value after applying the offset is -2,000,000,000W?

And if the two Modbus registers combine to be 4,211,081,215, then the power value after applying the offset is 2,211,081,215?

Correct. Unfortunately The offset value is too large to just ignore the 32nd / sign bit.

Also, I don't understand why this does not work, specifically the COP Modbus_16int_array[0] dest_udint 1, assuming the two 16-bit UINT/INT values from Modbus are in adjacent locations in that array?

The Modbus data is in 2 adjacent locations, but unfortunately UDINT is not a supported data type for COP/CPS instructions, OR BTD instructions, and the software generates an error if you try to use a UDINT as a source or destination. Such as "Error: Rung 8, COP, Operand 1: Invalid data type. Argument must match parameter data type."

Frequently encounter Real / Floating Point values read from a Modbus Master as two consecutive Register Addresses that need to be converted to REAL.

As above, a COP instruction using the first modbus Register & Address as the Source (eg Register[First Address of data] and a DINT as the Destination correctly performs the conversion.
The COP has a DINT (4 bytes) as the Destination data type and a length of 1, so it copies (1 * 4) contiguous bytes of Logix memory starting at Register[First Address of data] to the DINT.
4 bytes = 2 * 2 byte INT's (the two Modbus Addresses).

However, this does not always give the expected result.

There are 4 choices in Modbus implementations to encode floating point and long integer values into consecutive Addresses that I am aware of:
1. Big endian
2. Big endian with byte swap
3. Little endian
4. Little endian with byte swap

Device makers decide which format to encode the data in. Non byte swapped is the most common I have seen.

If the result of the COP is incorrect, use a SWPB (Swap Byte) instruction to byte swap the Modbus Addresses to mapped input INT's for the COP instruction.

The value in the documentation is not listed as a REAL, it is listed as Uint32. The byte / word order is not going to change the amount of bits, or the signed/unsigned status. My issue is that the UDINT data type in Logix 5000 isn't supported by COP/CPS or BTD so I don't have a good way to get the bit pattern of the Modbus data into a UDINT.
 
It's inelegant and I don't like it much, but you could do some sort of bit-wise brute force....
If your data is in array[0] and array[1], with the least significant bits in array[0]
Code:
[B]XIC array[0].0 OTE dest_udint.0
XIC array[0].1 OTE dest_udint.1
XIC array[0].2 OTE dest_udint.2[/B]
XIC array[0].3 OTE [B]dest_udint.3[/B]
[B]...[/B]
XIC array[0].14 OTE [B]dest_udint.14[/B]
XIC array[0].15 OTE [B]dest_udint.15[/B]
XIC array[1].0 OTE [B]dest_udint.16[/B]
XIC array[1].1 OTE [B]dest_udint.17[/B]
XIC array[1].2 OTE [B]dest_udint.18[/B]
XIC array[1].3 OTE [B]dest_udint.19[/B]
...
XIC array[1].14 OTE [B]dest_udint.30[/B]
XIC array[1].15 OTE [B]dest_udint.31[/B]
If the COP supported UDINT as the destination, this problem would be far easier to sort...
 
Rockwell has L_ADD and L_MUL instructions in their Process Library (v4.10)

Start by copying both 16-bit values into LINT. Then L_MUL the high word by 2^16 and L_ADD the low word.

Then L_ADD the offset.
 
UDINT is not a supported data type for COP/CPS instructions
Can you COP/CPS the two (U)INTs to a DINT?

  • If the resulting DINT value is non-negative (bit 31 is 0), then subtract 2,000,000,000 from it.
  • If the resulting DINT value is negative (bit 31 is 1), then
    • OTU the_dint.31
      • same as SUB the_dint -2,147,493,648 the_dint
        • N.B. we cannot ADD the_dint 2,147,483,648 the_dint
      • also the same as SUB the_udint 2,147,483,648 the_udint
    • ADD the_dint 147,483,648 the_dint
 
So I was previously under the impression that the COP/CPS instruction defined the bit length by the destination variable type, and the length field.

However I did some experimenting, and it appears that you can CPS a DINT (not part of an array, inside a UDT) into a LINT and it seems to fill the upper 32 bits of the LINT with 0's from what I can tell. I was not able to modify the individual bits of the LINT in the tag editor with the CPS instruction running.

BND BST BTD Modbus:I1.Genset_W.Genset_W_Gen_Read_6 0 Genset_W_Raw.KW 0 16 NXB
BTD Modbus:I1.Genset_W.Genset_W_Gen_Read_7 0 Genset_W_Raw.KW 16 16 NXB
CPS Genset_W_Raw.KW Genset_W_Raw.KW_LINT 1 NXB
SUB Genset_W_Raw.KW_LINT 2000000000 Genset_W_Raw.KW_LINT NXB
DIV Genset_W_Raw.KW_LINT 1000 Genset_W.KW BND



The code snippet above is what I tested. I used BTD to put the 2 Modbus 16 bit values into a DINT, then used a CPS to push that into the LINT, then did my offset and division to get it into KW. Manually changing the Modbus register values, it seemed to work properly.
 
To follow up further on this:

Previously my UDT had the DINT immediately preceding the LINT variable and it was operating properly with the code above.

As a test, I modified my UDT so that the source and destination of the CPS instruction were separated by a second DINT variable, and did not modify the logic.

If I put values into the second DINT, it starts writing into the LINT at bit x.32 so apparently the CPS instruction was smart enough to realize it was running into the destination variable and fill the test of the destination with 0's before I added the second DINT variable.
 
Can you COP/CPS the two (U)INTs to a DINT?

The instruction you want to use are two BTDs: They can copy the bit pattern from the two INTs into a DINT, the first one copying 16 bits starting at bit 0 of the DINT, the second starts at bit 16.

The first INT will be assumed to be a UINT -- the .15 (sign) bit will get transposed into the .15 bit of the DINT. The second INT's sign bit will determine the sign of the DINT.

I haven't played with the v.30+ Rockwell Studio to know if they've finally gotten around to having actual UINTs and UDINTs or not.
 
The instruction you want to use are two BTDs: They can copy the bit pattern from the two INTs into a DINT, the first one copying 16 bits starting at bit 0 of the DINT, the second starts at bit 16.

The first INT will be assumed to be a UINT -- the .15 (sign) bit will get transposed into the .15 bit of the DINT. The second INT's sign bit will determine the sign of the DINT.

I haven't played with the v.30+ Rockwell Studio to know if they've finally gotten around to having actual UINTs and UDINTs or not.

As of v32.14, UDINT is not supported by COP, CPS, or BTD. The issue was I was trying to get a UDINT (UINT32) value, but was not able to use the typical methods to get the data into a UDINT data type.
 
So I was previously under the impression that the COP/CPS instruction defined the bit length by the destination variable type, and the length field.

However I did some experimenting, and it appears that you can CPS a DINT (not part of an array, inside a UDT) into a LINT and it seems to fill the upper 32 bits of the LINT with 0's from what I can tell.


Your previous impression is correct. The CPS does not "fill the upper 32 bits of the LINT with 0's,", it copies the 0s from whatever follows the first 32 bits from the Source address. You should take care to explicitly ensure that second set of 32 bits are indeed zeros, which is what I think you did in the next post.


As an alternative to ensuring the upper 32 source bits are 0 before the CPS, you may be able to mask out the destination bits after the CPS like this

  • CPS source the_lint 1
  • AND the_lint 00000000FFFFFFFFh the_lint
  • SUB the_lint 2000000000 the_dint
    • OR
    • SUB the_lint 2000000000 the_lint
    • MOV the_lint the_dint
 
Your previous impression is correct. The CPS does not "fill the upper 32 bits of the LINT with 0's,", it copies the 0s from whatever follows the first 32 bits from the Source address. You should take care to explicitly ensure that second set of 32 bits are indeed zeros, which is what I think you did in the next post.


As an alternative to ensuring the upper 32 source bits are 0 before the CPS, you may be able to mask out the destination bits after the CPS like this

  • CPS source the_lint 1
  • AND the_lint 00000000FFFFFFFFh the_lint
  • SUB the_lint 2000000000 the_dint
    • OR
    • SUB the_lint 2000000000 the_lint
    • MOV the_lint the_dint

After chatting with tech support and not getting a clear answer about what I was seeing with the copied DINT and destination LINT consecutive in a UDT, I decided the most prudent approach was to create a 2 DINT array to copy from, to make sure the upper 32 bits are forced to 0 with the 2nd DINT.
 
Originally posted by Paul351W:

...I decided the most prudent approach was to create a 2 DINT array to copy from...

That is certainly the most prudent course of action. The whole copying from an uncontrolled address thing is one of those things that works 99.999% of the time. But that one failure every 100000 will drive you nuts.

Keith
 

Similar Topics

Hi folks, in the alarm manager of Rslogix 5000, the tag-based alarm has been created. But when I tried to change the condition, it was found the...
Replies
2
Views
156
I am completely stuck on building a ladder program that requires a start button to be pressed 3 times to turn on motor 1. Then motor 2 starts...
Replies
20
Views
578
Is there a way to use the FAL instruction to do +=2 instead of +=1? I have an array that is organized with alternating "data" and "flag" values...
Replies
5
Views
126
I have an AOI with revision notes. Someone entered the last note into the description instead of the notes. I cannot just click into the revision...
Replies
4
Views
152
Back
Top Bottom