This is part of an RSLogix 5000 Tutorial.
Understanding Asynchronous I/O
It’s important that you understand the difference between synchronous and asynchronous I/O in PLCs. If you come from the SLC 500 or PLC/5 world, you’ll be used to synchronous I/O, and that made your life a bit easier. In the PLC/3 world, I/O was asynchronous, and in a move that surprised many programmers, the ControlLogix/CompactLogix platform uses asynchronous I/O as well.
In a SLC 500, the PLC “scanned” the inputs by reading the state of the hardware and storing them in memory. Then it solved the logic. Then it “scanned” the outputs by copying the output coils to the actual hardware outputs. The important thing to realize is that the state of an input could never change during the logic solve. That meant you could write things like this:
…and you could be certain that Output 1 and Output 2 would never be on at the same time. However, with asynchronous I/O, our assumptions are wrong. Both Output 1 and Output 2 could be on during the same scan, or off during the same scan if Input 1 changes state between the time that it solves the first rung and it solves the second rung. Rare, but possible, and it leads to bugs that are intermittent and very hard to find, especially in logic that deals with critical timing, like data collection or communication logic.
In the days of the PLC/3, I understand programmers used to make sure they didn’t use any input more than once. They would copy the state of the input to an internal coil if they had to use it in more than one rung. With RSLogix 5000, I suggest solving it by creating a MapInputs routine that “scans” your inputs into temporary coils at the beginning of your program. Then you can forget about synchronous/asynchronous and go about your business.
Contact and Coil
Faced with the problem above, what I see a lot of programmers do is define a UDT for each input card, and then use a copy instruction to move the hardware input state into the UDT tag. There are two problems with this: first, UDTs can’t be edited without downloading the processor, so if you change an input you have to stop the machine, and second, if you’re using the UDT tag in your program, you can’t force those bits directly. You have to find the input bit that corresponds to the UDT tag bit, and force that. How would you go about finding that input? You can easily right click and do a cross reference, but that only brings you to the copy instruction. You still have to figure out that you’re looking at the 5th bit in the UDT, and then you know it corresponds to output 4 in the I/O card. Quite painful when you’re trying to fix a machine.
The solution is to use a separate rung for each input: a contact for the “real” input and a coil for the “internal” input that you use in your program. If you right click on the internal input in your program and pull up a cross reference, you can always find the OTE instruction, and easily find the real input bit to force.
Does it seem like a lot of work to do this? It really isn’t, especially if you master the art of copy-paste-search-replace, and the first time you have to find the input you’re looking for, you’ll save yourself at least that much time.
Back to the Tutorial
When we finished the last section of the tutorial, we had created a MapInputs program, but it still looked like this:
Following the naming convention from the last section of the tutorial, rename MainRoutine to Aa_Main (by right clicking, selecting properties, and editing the name, then clicking OK). It’s already configured as the Main routine of the MapInputs program, so we don’t have to do that.
Right click on the MapInputs program and choose New Routine… from the menu:
That will open the New Routine dialog box. We want this routine to hold the logic for mapping the local input card that we added in section 3 of the tutorial. We named the I/O card “LI01” (for Local Input card 1). Name this routine the same, for consistency:
Click OK to finish creating it. You’ll now see LI01 in the controller organizer:
Follow the instructions from the last tutorial section to edit the Aa_Main routine in MapInputs and add a JSR instruction to call the LI01 routine:
Now open the LI01 routine:
Add a contact and a coil to this empty rung. Locate the toolbar above the ladder editor, and click the contact button, and then the coil button (highlighted):
That should change the rung so it looks like the following:
When you created the LI01 input card in the I/O configuration, RSLogix 5000 automatically created some tags in the Controller Tags section for access to the inputs of this card. The tags begin with the word “Local”. The first input of the card is “Local:1:I.Data.0”. That means local rack, slot 1, input data, bit 0. Select the question mark above the contact in the new rung, and enter this full tag name. You should see it above the contact now:
That’s the input side. Now we need an internal coil to store the value of the input during the scan. Rather than creating individual boolean tags for each coil, I suggest creating a single DINT tag called LI01 and using LI01.0 (bit 0) for the first input, LI01.1 for the second input, etc. I would go as far as using these tag names as the input wire names in your electrical drawings.
Select the question mark above the coil, and type LI01.0 and press enter:
Notice that the rung still has errors. This is because we haven’t defined the LI01 tag yet. If you hover over the tag, it will say “Undefined Tag”. To define the new tag, right click on LI01.0 above the coil and select New “LI01” from the context menu:
That will open the New Tag dialog and autopopulate the Name field with LI01. However since we defining a tag based on a Coil instruction, it defaults to a Boolean data type. Change the data type (highlighted) to DINT (a DINT is a double-integer, meaning it has 32 bits – it is the default word size in the processor):
Click OK, and the rung won’t show an error anymore. However, even though LI01.0 is a perfect name for a tag because it’s concise and consistent, we also need to add a description so we know what it is. Right click on LI01.0 above the coil again, and this time select Edit Main Operand Description from the context menu:
That will open a small window above the ladder editor where you can enter a description. Go ahead an enter “INLET Cold Water Valve On”:
Click down in the ladder area when you’re done, and the description editing window will close. That will leave you with your first complete rung in the LI01 routine:
Add 15 more rungs to this routine in the same way, except increment the last number in Local:1:I.Data.XX from 0 to 15, and the last number in LI01.XX from 0 to 15. You can add the descriptions for the coils that you see here:
That’s all the inputs we’re going to have for this tutorial. If you had more input cards in your application, you’d add more routines in the MapInputs routine and more DINT tags for holding the state of the inputs during the logic scan.
Next time I’ll describe how to map your outputs, which is actually a bit different.