Automation Software Engineer with 10+ years experience in automation, instrumentation and software programming.
Abbreviations / Terminology
There are a ton of abbreviations and different terminology when looking through PLC documentation, some are vendor specific, some are more generalised among different PLC manufacturers. When I started out, I found it very difficult to know what someone meant by "Create an INT" or "This POU should be in a separate Task".
Hopefully the below comes in useful to people and helps make better sense of what documentation is really telling you to do!
Program Structure Related
Program Organisation Unit
This is an object that holds logic that are used to develop your application. These can declared as various different types (which changes their behaviour) but POUs ultimately serve one function - To hold and execute your code. As well as being declared as different types (which we'll come on to), POUs can also be declared as using a different language. This doesn't mean a different spoken language like English, but a different programming language (we'll cover these later too)
A Task exactly what it sounds like, it's a Task that tells your application to run a set of POUs or gather IO data. In some PLCs, Tasks perform various other tasks too and may not be called "Tasks" at all (looking at you Siemens, OB1, OB35 etc are basically Tasks).
In most PLCs, Tasks can be defined with a range of various parameters such as
- Task Mode: The mode the task is operating in, such as Cyclic Execution, Event Driven, Freewheeling. It's probably best to look up the different modes available and what they mean to the PLC you are using as they're not always done in the same way.
- Watchdog Timeout: The time in which the entire task MUST complete. Failing to complete the task in this time will raise an internal flag that drops all outputs to a safe state. Some PLCs allow you to configure what happens on Watchdog failure, some don't. Refer to documentation for your own PLC.
An important rule to remember is that if a POU cannot be traced back to a Task, it will not be executed. For example:
Task >> Main (PRG) >> Sub (PRG) >> Area_1 (FB) >> Function (FB)
The above shows "Task" calling "Main" which is calling "Sub" and so on. If "Area_1" was deleted, "Function" would have no route to a Task and would therefore no longer be executed in the program. Most (not all) PLC programming environments tell you that a POU is orphaned from a Task.
PRG and FB in the example above are types of POU, which we'll cover now.
A PRG is a type of POU in most PLCs (Not all, again looking at Siemens in which PRG doesn't exist). At least one PRG must exist as Tasks can only call a PRG. Because a PRG is simply a type of POU, it performs in the same manner as any other POU and can be declared in different languages.
A PRG can call another PRG as well as calling any other type of POU. A PRG can also declare it's own Variables (Covered later).
Note: In some PLCs, PRGs can declare there own variables but they are not maintained between PLC scans (a complete execution of a task), this means that any value written to the variable is lost on the end of the scan. These type of variables are usually referred to as Temp Variables.
A Function Block is probably the most common POU used in a PLC. They are used to create blocks of code that can be used over and over again by simply dropping the FB into a POU or another FB. FB's are made up of Input and Output parameters (we'll cover these in more detail) that allow data from outside the FB to be brought in and data made by the FB to be passed back out to the caller. For example
The above shows FB_1 being called on line 1 (a PRG is calling it). The input data has Sensor_1 being passed to it. The FB_1 object is performing a task and then outputting Output, which is being passed to Output in the PRG that is calling the FB.
Line 2 is showing FB_1_CALL.Counter being used, but we cannot see "Counter" as a parameter of FB_1? This is because "Counter" is a Static Variable (A variable that is used to hold information rather than pass it anywhere). In most PLCs, Static Variable information is accessible if the Instance of that data is also declared.
What is Instance Data?
Instance data is the data that belongs to an FB. In the example above, FB_1_CALL holds all instance data of FB_1. This is why declaring "FB_1_CALL.Counter" works correctly. FB_1 is the name of the FB, FB_1_CALL is the data for that specific call of that FB.
If FB_1 was called again on Line 3, you would need to give it a different set of instance data by declaring a different identifier for it, such as "FB_1_CALL2".
This approach allows an FB to be called hundreds of times without affecting eachother's data sets.
A function is very similar to a Function Block, but it does not hold it's own data for more than 1 PLC scan, all variables are temporary.
PLCs handle functions different ways, for example CoDeSys allows you to leave interface pins unassigned where as Siemens does not. Most PLCs also enforce that a variable is returned when the Function completes. This variable must be declared when the Function is created. It's very common to see functions returning a Byte or Word which contains a status on whether the Function completed without issue.
A Variable is a container that holds information, there are many different types and again it depends on the PLC that is in use. The main Variable types (also known as Data Types) are:
- BOOL: Digital Data (True / False)
- BYTE: Numerical Data / Bitwise Data (0 - 255)
- INT: Numerical Data (-32768 - 32767)
- UINT: Numerical Data (0 - 65535)
- SINT: Numerical Data (-128 - 127)
- USINT: Numerical Data (0 - 255)
- DINT: Numerical Data (-2147483648 - 2147483647)
- WORD: Numerical Data / Bitwise Data (0 - 65535)
- DWORD: Numerical Data / Bitwise Data (0 - 4294967295)
- REAL: Numerical Data (-3.402823e+38 - 3.402823e+38)
- ARRAY: Array of Any Data type (Declared as "ARRAY [0..10] OF DataType)
Most PLCs support the above, some PLCs will support a selection of the below also:
- LWORD: Numerical Data / Bitwise Data (0 - 18446744073709551615)
- UDINT: Numerical Data (0 - 4294967295)
- LINT: Numerical Data ( -9,223,372,036,854,775,808 - 9,223,372,036,854,775,807)
- ULINT: Numerical Data (0 - 18446744073709551615)
- VARIANT: Object (Anything)
- NULL: Object (Nothing)
The additional variables are generally only supported by 64bit PLCs and Runtimes. Variant & Null data types are advanced and not common in PLCs.
In addition to the above Data Types, there are also different Variable attributes (modes if you like):
- CONSTANT - Variable that is hard coded and cannot be changed at runtime
- RETAIN - Variable that remembers it's last value between loss of power supply to the PLC. Most PLCs have a limit on the maximum amount of data that can be retained. Older PLCs may retain everything by default or have special ranges of registers that are retained, so make sure you check.
- PERSISTENT - A variable that retains it's last value even after a re-initialisation of the PLC or the PLC is warm started. The only way to reload the default data is to cold start the PLC or perform a full download. Note: Persistent variables can be dangerous if used incorrectly, especially if indirect addressing / pointers are being used.
An interface is the declaration of variables a PRG, FB or FC is expecting to use. There are a few keywords that can be used to declare interfaces:
- VAR_INPUT - Data passed in to the POU
- VAR_OUTPUT - Data passed out of the POU
- VAR_IN_OUT - Data that is passed in and out of the POU to the same variable (If you know a bit about computer programming, think of this as passing by reference)
- VAR - Data that is local to the POU, Some PLCs allow access to the data by explicit reference only (For example "POU.VARIABLE")
- VAR_STATIC - The same as VAR, but does not allow access to the data from outside the block
- VAR_TEMP - Temporary data, the values stored in TEMPs is lost when the block is exited
- END_VAR - A required termination declaration after declaring your variables.
Here's an example using the above declarations:
GLOBAL Variables are special variables that are accessible anywhere in a project. They serve as a great way of passing information between different areas of your project.
Some people use Globals for everything, and don't declare any VARs in POUs. I advise against this as it gets messy quickly!
Globals are usually defined in a special Global Variable list, or Symbol table depending on the PLC you are using
(Siemens use DBs, variables stored in DBs that are not Instance DBs are the equivalent of Global Variables)
As mentioned earlier, POUs can be written in different languages. Below are the most common (Screenshots are from CoDeSys)
Ladder is probably the most commonly used language. It's easy to read and follow and fault find.
FUNCTION BLOCK DIAGRAM
FBD is very very similar to Ladder, it tends to be used for projects that are made up of many separate functions (hence the name). Logic that compares Bool values is easier in Ladder than it is in FBD.
Structured Text is one of (if not, the most) flexible of the languages. It's quick to program in, easy to read, but can get messy quickly if formatting rules aren't followed.
Sequential Function Chart
This language is excellent for sequencing (hence the name!). However it is one of the more difficult to understand. In the example below, it is important to note that the "ProcessTimer" step must be called in any scenario, else the timer will not update and will hold it's last value. It is very easy to get stuck with SFC and leave variables in states that were not intended
SFC probably needs it's own dedicated article to explain what exactly is happening here (I'll link it here when it's written!)
CONTINUOUS FUNCTION CHART
CFC is very similar to FBD, but you are not confined to networks (horizontal placeholders), you are free to draw your logic however you like. This language is useful for electricians transferring to PLC logic, as it reads the same as a drawing. There are a few things to be careful of though, the logic may not flow as expected. There are small numbers that show the logic flow, it's important to keep track of what is happening and where.
The above shows the basic building blocks needed to build almost any application. There are some slightly more advanced extras that can be utilised to help make things a bit easier though.
Structures (DUT / UDT)
Structures are great for repeated sets of variables. A Structure is basically a group of variables that can be called by the name of the group. Consider the below:
The above structure is called "SIGNALBOX" and can be declared as a variable type as below:
This would create two instances of "SIGNALBOX", of which both have access to the structures data. For example, you can use the variable "BOX1.SignalCount".
The advantages of using structures is you can quickly and easily create groups of large data sets and know that all of the required signals are definitely there.
Libraries are a collection of POUs and Variable lists that can be moved from project to project. This allows you to have a standard set of POUs, tried and tested that can be dropped in to a project when required.
Libraries can be nested too, so a library can call another library if required. Any large scale software house will almost definitely have a standard library set.
All of the screenshots for this article were obtained from CoDeSys 3.5. It's a free development package that is capable of simulation of hardware. It's free and easy to obtain. Manufacturers such as ABB, IFM, Wago, Schneider and more use CoDeSys to power their PLCs.
If you're looking to develop your understanding and skill set, I would highly recommend it as a place to start!
Questions & Answers
Question: What is a memory file?
Answer: What PLC is this in regards to? By definition though, a memory "file" would most likely be an area in which data is stored in a Non-Volatile format, such that if the PLC is turned off, the data is retained/remembered ready for when the PLC is turned back on. It could also be an area in which constants are stored.