Return to the Industrial Control and Automation Index
The starting point for all Programmable Logic Controllers (PLC) is an understanding of the memory structures. This is vital as the programs we compose operate on memory units. Even the simplest program, where we directly transfer the value of a screw-terminal digital input to a screw-terminal digital output, is a memory-to-memory operation. It involves at least two memory locations and two functions to operate on those memory locations including a read and a write operation. In Ladder Logic (LL) we recognize those functions as a normally open contact and a coil respectively. Please keep this distinction in mind as you read this article. While we are presented with images of contact and coil, we are not operating directly on hardware. Instead, we are performing a memory-to-memory operation.
Arduino review
In this article, we will explore the relationship between global and local memory locations. To reduce the article’s scope, we will limit the discussion to Boolean values.
Prior experience programming the Arduino microcontroller (C programming) is assumed. However, for greatest accessibility, we begin with a brief review of the important Arduino ideas including loops and the memory space involving local and global variables. Armed with this understanding, you will be able to transition from Arduino to LL with minimal confusion and effort.
Are you ready to leverage the strength of the Arduino Opta PLC with the IEC 61131-3 languages such as LL? Let’s depart from the traditional Arduino Integrated Development Environment (IDE) and move to the Arduino PLC IDE.
Tech Tip This article provides the necessary background information to begin LL programming the Arduino Opta. My apologies for the long length and textbook nature of the writing. Yet, this is the background material required to program the Opta in ladder logic. My sincere hope is that you will bookmark this page and refer to it as you begin to program the Opta PLC. You will find that the concepts are simple but not immediately apparent and not generally documented as many PLCs use an older fixed memory model as compared to the flexible tag-based methods of the Opta.
Arduino loops
Every Arduino program contains a setup( ) and a loop( ) function. Recall that statements contained within the setup( ) function are executed once upon startup. Statements within loop( ) are executed sequentially only to start over – the last action performed by loop( ) is to jump to the top and start over again (oversimplified explanation, see also the keyword static). This loop is performed many thousands of times a second provided the microcontroller does not encounter blocking code.
Tech Tip: Blocking code is one or more statements that cause the code to stop. Examples include tone( ), delay( ), or control statements such as while(WaitForEvent). The microcontroller is stalled for the duration of the blocking code. Unless other mechanisms such as interrupts are implemented, the microcontroller unable to respond to events. Note that the PLC code is purposely designed to prevent a blocking condition allowing the PLC to respond to real-world events. This concept of blocking can be a stumbling point for programmers transitioning from the Arduino to the PLC.
Arduino I/O
The Arduino Input / Output (I/O) operations are performed using the digitalRead( ) and digitalWrite( ) functions. This is an example of a memory-to-memory transfer. The digitalRead( ) function reads the status of an input pin and transfers the data to a memory location. The digitalWrite( ) function transfers the value from a Boolean memory location to the physical output pin.
Arduino memory scope
One of the more challenging aspects of C programming is the concept of memory scope. This is especially apparent when the learner first encounters functions. A classic example is the inappropriate use of same-named variables as global, for one or more functions, and within loop( ). For example, a programmer may use the index (“i”) variable in all three locations as in for(int i = 0; i < 10; i++). The compiler with its ever helpful “you know what you are doing” philosophy will happily compile the code with the established rules. This is not always helpful for the beginner who does not know the memory scope rules. The resulting code can be difficult to troubleshoot.
While the rules don’t directly translate into the PLC with LL, its good to restate the ideas:
-
global variable: The global variable is defined outside of any function. In an Arduino sketch, the global variable is typically defined above and outside of the setup( ) function. The global variable is accessible by all statements within the same tab Arduino sketch (oversimplified explanation, see also the keyword extern).
-
local variables: A local variable is defined within a function. It may only be accessed by statements within that function. The value of a local variable may be passed by reference or by value to another function. However, that function cannot access the variable directly.
Commonality of the Arduino C dialect with ladder logic
There are many parallels between programming an Arduino and a PLC using ladder logic. Concepts such as loops, I/O operations, and memory structures are essential for programming both devices. We will now explore the parallels between the two languages. Note that this ladder logic conversation is not universal. Each PLC manufacturer has a unique way of handling memory. The Arduino implementation is a sophisticated modern tag-based implementation.
Ladder logic loops
The program scan is the PLC’s fundamental program structure. As a programming philosophy, think of this as a loop that allows the PLC to have a timely response to real-world events. The clock shown in Figure 1 provides a good analogy. At 12 o’clock it starts with background housekeeping tasks. At 3 o’clock it reads the screw-terminal inputs and transfers them to memory. Your programs describing the memory-to-memory operations are performed at 6 o’clock. Finally, at 9 o’clock, the Boolean memory contents are transferred to the PLC’s screw-terminal outputs.
This loop is performed about 1000 times per second. It’s important to avoid blocking code as any such code would prevent the PLC from responding to real-world events. This fact is best illustrated in the PLC’s timers. They are designed as non-blocking. As an example, consider a 5-second Time On delay (TON). This timer would be visited (entered) for many thousands of program scans before evaluating as true. At no point does the timer halt the program scan.
Tech Tip: The PLC’s watchdog timer is closely related to the program scan. As part of the normal 12 o’clock housekeep routing, the PLC metaphorically “pets” the hardware-based dog. Should the loop time be excessive, the watchdog will become anxious, wake up, and place the PLC into a hard (not running) fault state. Contrary to popular belief, it is possible to perform time consuming loops similar to while( ) or for( ) in ladder logic; perhaps to iterate over an array. These 6 o’clock loops could cause the overall program scan to slow down enough to wake up the watchdog.
Figure 1: This clock analogy represents the actions taken during a PLC’s program scan.
Ladder logic I/O
Earlier this month, I had a conversation about Arduino-like programming languages used to program the PLC. For the first time, I heard the words “PLC Style” as applied to handling I/O within a programming loop. The idea is closely related to the clock shown in Figure 1.
The code structure captures all inputs at the beginning of the loop( ). The various memory-to-memory operations are then performed with the understanding that the shadow registers are stable for the duration of the scan. At the end of the loop, 9 o’clock using our analogy, the outputs are updated. This contrasts with the traditional practice of handling I/O as it occurs in the program.
Another way of identifying this “PLC Style” is to use the language of “shadow registers”. At 3 o’clock the screw-terminal inputs are transferred to shadow registers that are then used by your 6 o’clock code. Later at 9 o’clock, the output shadow registers are transferred to the screw-terminal outputs.
Tech Tip: A shadow register identifies a memory location that contains an image of the microcontroller’s input. It can also be interpreted as a memory location holding what will become the output. In both cases, your code operates on the shadow register as opposed to directly reading or writing to the hardware. This is directly related to Figure 1 where the inputs are transferred to a shadow register at 3 o’clock. Your PLC program then interacts with the shadow register, NOT the actual information on the screw terminals. In fact, the PLC is blind to any input changes until the next program scan.
Ladder logic memory scope
We finally arrive at the heart of our ladder logic discussion. It took some time to get here, but it was first necessary to describe the looping construct and the methods used to handle screw-terminal I/O. Armed with this knowledge, we can explore how to read an input, do something with it, and then pass the result to an output. The remainder of this article demonstrates that the Opta LL memory scope is closely related to the traditional C program.
Tag based structure
The Arduino Opta uses a tag-based memory. In many respects this is just a continuation of what you have already been doing in the traditional Arduino. You are free to name variables and define their type as you have always done. It’s easy to define a Boolean or even an array to type float.
Tech Tip: The freedom offered by tag-based PLC memory is not universal. A large number of PLCs have fixed memory locations with N number of Boolean types or M number of float available. The programmer is free to use the numbered memory location or an alias. At all times the programmer is painfully aware of the memory locations This older one, and only one, location for variables leads to a flat memory structure where every memory location is global. Contrast this with the tag-based structure, where the programmer does not pay any attention to the location of the variable instead focusing on the name and type.
Tag names
Just like the traditional Arduino, you are free, within limits, to choose variable names to anything you want. As a clever programmer, you will use this to your advantage by providing a descriptive tag. The result goes a long way to toward self-documenting code. For example, a tag name such as gxStartPB provides a clear identification of a shadow register for the start pushbutton. In this example, we are using Hungarian notation. The prefix g indicates that the variable is global in scope. The prefix x signifies a Boolean.
Some people will question the need for being fussy with the variable names. They are not wrong. Although, I would argue that we are in another one of those situations where we are attempting to code as if we are experts when we should be focused on learning.
As a metaphor, consider how you learn to read by using phonics. To read the word “cat,” point to your shoulder and enunciating the “c” (or ‘’k”) sound, then point to your elbow stressing the “a” sound, finally point to your hand with the closing “t” sound. No experienced reader needs to perform these elaborate steps. Likewise, there is good argument that experienced programmers don’t need Hungarian notation.
However, it’ been my experience that adding the Hungarian prefix forces the learner to think about the type and scope of the variable. It mitigates against those difficult to locate bugs such as the inappropriate duplication of variable names or misplaced variables. Also, later in your programming journey, the precise attention to variable names will make it easier to construct PLC based functions.
To learn more about Hungarian notation, and PLC guidelines in general, I encourage you to read this Coding Guidelines document produced by the PLCopen organization.
Tag scope
Like its C programming equivalent, the Opta ladder Logic implementation features local and global memory. A global tag is available to all Program Organizational Units (POU) within the Opta. A local variable scope is limited to the POU in which it is defined. Figure 2 shows this relationship with the local variables encapsulated in their respective POUs.
Tech Tip: The POU is a block of code within the PLC. In the IEC 61131-3 environment, the PLC’s program is composed of many POUs. A closely related and often conflated concept is a PLC Function Block (FB) or User Defined Function block (UDFB). All functions are POU in that they help organize a complex program. However, not all POUs are functions. As an example, suppose we wrote a 10-rung LL program with the name prgCtrl. This POU is a program, but not a function. To help with the subtle distinction, let’s use the traditional definition for function as something that is called from within a program. With definition our POU prgCtrl is not explicitly called by anything – it is not a function. However, it may call a function such as absolute value.
Things get interesting when we have multiple POUs. Since POUs are not functions, we need a method of passing information between POUs. For example, consider the structure shown in Figure 2. Here is an elementary three-program solution that included prgInMap, prgCtrl, and prgOutMap. The prgControl POU contains the logic for our PLC while the mapping POUs are used to transfer the shadow registers to and from the PLC’s screw-terminals. Since these are not functions, the only way to transfer data is to use global variables. To help illustrate, Figure 3 presents an Arduino PLC IDE view showing the local variables for prgCtrl.
Tech Tip Ask any computer programmer and they will tell you to avoid global variables. They may even tell you stories about the time they or someone they knew spent hours or even days troubleshooting a well-hidden bug. It’s sound advice. We should absolutely minimize the number of global variables using them only when absolutely necessary. As a rule of thumb, they should only be used for passing I/O between POUs. This will become easier as we learn to program FB and UDFBs.
Figure 2: Global variables are available to all POUs while the scope of local variable is limited to the POU in which they are defined.0
Figure 3: This image shows the local variables associate with prgCtrl.
Conclusion
This is a good stopping point for the article. We have defined the Arduino Opta memory structures by contrasting and comparing them to the well-known Arduino C programming structures. We learned that the Opta uses a tag-based memory assignment where each tag is instantiated with a data type and a descriptive name. The PLC operates using a loop that performs housekeeping activities in the background as well as transferring screw-terminal I/O to shadow registers. Finally, we learned that a variable may be local or global in scope. This was accompanied by a recommendation for the learner to use Hungarian notation to help organize the program.
Once again, I would encourage everyone to read the PLC coding guidelines provided by the PLCOpen organization. This is just as important as reading documents such as the GNU Coding Standards. These documents will help you understand how programs are assembled and best practices making you code more readable and maintainable.
Perhaps in the future we can move on to developing programs using ladder logic. As the time of this writing, there is one DigiKey Tech Forum article of interest to the beginning Opta PLC programmer.
This article provides a useful technique that allows you to view the status of the Opta’s I/O using the status and Red, Green, and Blue indicator LEDS. This is important troubleshooting aid that allows you to delineate hardware from software problems.
About the Author
Aaron Dahlen, LCDR USCG (Ret.), serves as an application engineer at DigiKey. He has a unique electronics and automation foundation built over a 27-year military career as a technician and engineer which was further enhanced by 12 years of teaching (interwoven). With an MSEE degree from Minnesota State University, Mankato, Dahlen has taught in an ABET accredited EE program, served as the program coordinator for an EET program, and taught component-level repair to military electronics technicians. Dahlen has returned to his Northern Minnesota home and thoroughly enjoys researching and writing articles such as this.
Return to the Industrial Control and Automation Index