The Logix5000 Family
This two part series focuses on using Rockwell ControlLogix, CompactLogix, and similar controllers programmed by RSLogix5000 / Studio5000. These will collectively be referred to here as "CLx". Note that Rockwell controllers in other families lack some of the features discussed below.
User Defined Types
User Defined Types (UDTs) allow data of different types to be combined into a single controller tag. UDTs are useful to:
- Organize data within a controller.
- Reduce development time for multiple similar devices.
- Combine data to be transferred as one tag between AOIs (Add-On-Instructions).
- Combine data to be transferred as one tag for processors-to-processor communication.
UDTs may not be modified on-line. Changing them requires a download, so plan ahead!
UDT Member Order
The order in which members of UDTs are listed determines their order in memory. Poorly ordering members wastes memory - it consumes bits in memory and network traffic that transfer no useful information.
Follow these rules to optimize UDT memory usage:
- CLx processors are 32-bit, so memory is handled in 32-bit chunks (the red lines in these screen shots indicate transitions between 32-bit words).
- BOOLs consume 1 bit, SINTs (small integers) consume 8 bits or one byte, and INTs consume 16 bits or two bytes. All other data types, including atomic types DINT (32-bit double integer), REAL (32-bit floating point) & LINT (64-bit long integer for date/time stamps), as well as all complex types, consume memory in multiples of 32 bits.
- If you add a BOOL to a UDT, always add at least eight BOOLs together.
- If you have an odd multiple of 8 BOOLs, add an odd number of SINTs before them.
- Round out sections of the UDT with BOOLs, SINTs, and INTs that do not consume a multiple of 32 bits with more of those types to reach exactly 32 bits.
- INTs must start at the beginning of a 32 bit section or on the 16th bit. If you have two SINTs and an INT, or an INT followed by two SINTs, that consumes 32 bits. But if you have a SINT, then an INT, then a SINT, that consumes 64 bits, with 32 of them wasted.
- Generally, simply grouping types together will minimize waste without doing any counting or adding spares.
- Switching back and forth between types - especially between 32+bit and BOOL types - will maximize waste.
- There are many reasons to COPy a portion of a UDT. CLx does not allow a COPy to start with a BOOL, but if there are BOOLs after the first member being copied, they will copy. The length of the copy is determined in multiples of the destination type referenced. Implications:
- If the first member of a UDT is a BOOL, it cannot be COPied unless the entire UDT is COPied.
- If the first member to be COPied is an INT, you can only COPy an even number of bytes to another instance of the same UDT (you could copy an odd number if the target is an array of SINTs or a different UDT with a SINT as the first member of the destination).
- If the first member to be COPied is anything other than a SINT or INT, you can only COPy to another instance of the same UDT in chunks of 4 or more bytes.
- Therefore, I recommend starting all UDTs with SINTs, then BOOLs, then INTs, then everything else. And if the UDT is for communication purposes, the first SINT should be the HB.
Add-On-Instructions Are Different
The above guidelines apply to UDTs. AOI parameters have some similarities to UDT members, but AOI parameter ordering is not important - that information is automatically optimized for memory consumption, and is not necessarily stored in memory in the same order the parameters appear.
Nested UDT Members
If a controller has multiple identical pieces of equipment with many pieces of data, it often makes sense to create a UDT for one piece of equipment, then a large main UDT containing an instance of the equipment UDT for each piece of equipment - and spare instances if there is the slightest possibility that more might be added.
UDTs and AOIs
You can nest AOI instances inside UDTs. All Input and Output parameters will be visible. In/Out parametes do not “live” in the AOI, so they will not be part of the UDT. If the AOI has local tags, they will consume memory in the UDT, but will not be visible.
UDTs and other complex types can only be In/Out parameters for AOIs - not Inputs or Outputs. But if you want a UDT to “live” in an AOI, add it as a local tag. It will not be visible to outside logic. It can be visible to an HMI or outside controllers if its External Access is changed from the default “None” to “Read Only” or “Read/Write”. And you can make atomic type (BOOL, SINT, INT, DINT, and REAL) members of the UDT visible to outside logic by creating parameters aliased to those members.
User Defined Types are a great tool to organize a wide variety of data into single CLx tags. Proper ordering of members will avoid wasting memory and allow greater flexibility in copying portions of the UDT.