(also in: Italiano, Deutsch, Português, Русский)
Quick summary: I wrote a compiler that starts with a ladder
diagram and generates native PIC16 or AVR code. Features include:
- digital inputs and outputs
- timers (TON, TOF, RTO)
- counters (CTU, CTD, `circular counters' for use like a sequencer)
- analog inputs, analog (PWM) outputs
- integer variables and arithmetic instructions
- easy-to-use serial communications, to a PC, LCD, or other device
- shift registers, look-up tables
- EEPROM variables, whose values are not forgotten when you lose power
- simulator, to test your program before you generate PIC/AVR code
This program is free software; source code and executables
are available for download.
Introduction
PLCs are often programmed in ladder logic. This is because PLCs
originally replaced relay control systems, and forty years later, we
still haven't quite let go. A PLC, like any microprocessor, executes
a list of instructions in sequence. Ladder logic tools abstract
this; you can program the PLC by wiring up relay contacts and coils
on-screen, and the PLC runtime will simulate the circuit that you've
drawn. Some of the relay contacts can be tied to input signals from
the real world; some of the coils can be tied to outputs. That way
you can make your simulated circuit interact with other devices,
and actually control things. That is the point.
Actually it's more general than that, because you can incorporate
timers and counters and arithmetic operations that you couldn't
(easily) perform with just relays. The circuit concept is still
useful though, partly just because it's intuitive, but also because
it abstracts the concurrency issues. It looks like this:
|| Xa Xb Yout ||
1 ||-------] [------+-------] [------+-------( )-------||
|| | | ||
|| | Xc | ||
|| +-------]/[------+ ||
This is a simple piece of combinational logic. There are three
input terms, Xa, Xb, and Xc. There is one output term, Yout. The
expression is Yout := Xa and (Xb or (not Xc)). This makes sense
if you think of Xa and Xb as normally open relay contacts, Xc as
normally closed relay contacts, and Yout as a relay coil. Of course
it gets more complicated than that:
|| ||
|| Asetpoint ||
1 ||-------------------------------------{READ ADC}----||
|| ||
|| Atemperature ||
||-------------------------------------{READ ADC}----||
|| ||
|| ||
|| ||
|| ||
|| {SUB min_temp :=} ||
2 ||------------------------{ Asetpoint - 20 }--------||
|| ||
|| {ADD max_temp :=} ||
||------------------------{ Asetpoint + 20 }--------||
|| ||
|| ||
|| ||
|| ||
||[Atemperature >] Yheater ||
3 ||[ max_temp ]+------------------------(R)-------||
|| | ||
|| Xenable | ||
||-------]/[------+ ||
|| ||
||[Atemperature <] Xenable Yheater ||
||[ min_temp ]--------] [--------------(S)-------||
|| ||
|| ||
|| ||
|| ||
|| {SUB check_temp :=} ||
4 ||-----------------------{ Asetpoint - 30 }-------||
|| ||
|| ||
|| ||
|| ||
||[Atemperature >] Yis_hot ||
5 ||[ check_temp ]-------------------------( )-------||
|| ||
|| ||
|| ||
||------[END]----------------------------------------||
|| ||
|| ||
This is for a simple thermostat. There are two analog inputs; one
of them is for the setpoint, so that it might, for example, be connected to
a pot that the user turns to select the desired temperature. The other
provides the temperature measurement; it might be a semiconductor
temperature sensor, or a platinum RTD with suitable interfacing
circuitry. There is a digital output, Yheater. That might control
a heating element, through a suitable switch (a TRIAC, or a relay,
or a solid-state relay, or whatever).
We close the loop with a simple hysteretic (bang-bang)
controller. We have selected plus or minus 20 ADC units
of hysteresis. That means that when the temperature falls below
(setpoint - 20), we turn on the heater, and when it climbs above
(setpoint + 20), we turn the heater off.
I chose to add a few small frills. First, there is an enable
input: the heater is forced off when Xenable is low. I also added an
indicator light, Yis_hot, to indicate that the temperature is within
regulation. This compares against a threshold slightly colder than
(setpoint - 20), so that the light does not flicker with the normal
cycling of the thermostat.
This is a trivial example, but it should be clear that the language
is quite expressive. Ladder logic is not a general-purpose programming
language, but it is Turing-complete, accepted in industry, and, for
a limited class of (mostly control-oriented) problems, surprisingly
convenient.
A Ladder Logic Compiler for PIC16 and AVR
Modern sub-3.00 USD microcontrollers probably have about the
computing power of a PLC circa 1975. They therefore provide more
than enough MIPS to run reasonably complex ladder logic with a cycle
time of a few milliseconds. I think PLCs usually have some sort of
runtime that's kind of like an interpreter or a virtual machine,
but if we're doing simple logic on a processor without much memory
then a compiler might be a better idea.
So I wrote a compiler. You start with an empty rung. You can
add contacts (inputs) and coils (outputs) and more complicated
structures to build up your program. Timers (TON, TOF, RTO) are
supported. The max/min durations depend on the cycle time of
the `PLC,' which is configurable; timers can count from
milliseconds to tens of minutes. There are counters and arithmetic
operations (plus, minus, times, div).
Circuit elements may be added in series or in parallel with
existing elements. An I/O list is built from the ladder logic
drawn. You can have internal relays (Rfoo), for which memory is
automatically allocated, or inputs (Xfoo) and outputs (Yfoo), to which
you must assign a pin on the microcontroller. The selection of pins
available depends on the microcontroller. I have tried to support
the most popular PICs and AVRs (see below).
You can edit the program in graphical form:

Then you can test the program by simulating it in real time.
The program appears on screen with the energized (true) branches
highlighted, which makes it easy to debug. The state of all
variables is shown at the bottom of the screen in the I/O
list.

Once the program works in simulation you can assign pins to the
inputs and outputs and generate PIC or AVR code. The code generator
isn't all that difficult. If you realize that a parallel circuit
is an OR and a series circuit is an AND, it's a second-year CS
assignment, and not a very long one. The editor is actually much more
challenging. It would take some work to make a smart compiler,
though. For the AVR a good register allocator would provide a major
speedup. If you wanted to get very fancy then you could apply standard
logic reduction algorithms, and maybe state reduction too. That
would be much more difficult. The timers screw things up anyways.
Even ignoring all that, my code generator for the AVR is very
poor. The AVR back end still generates PIC code...for example,
it does not really take advantage of the fact that the AVR has
more than one register. Some of the code that it generates is just
embarrassingly bad. The PIC back end is better, but not really
great. None of this matters much until you're trying to run dozens
of rungs of logic with fast cycle times.
I support the A/D converter, PWM unit, and UART on those
microcontrollers that provide them. That means that you can write
ladder logic that reads analog inputs, and that sends and receives
characters over serial (for example to a PC, if you add a suitable
level-shifter like a MAX232, or to a character LCD). It is possible to
send arbitrary strings over serial, as well as the values of integer
variables, as ASCII text. Finally, I now support `preserved variables'
on devices with EEPROM; you can indicate that a particular variable
should be auto-saved to nonvolatile memory whenever it changes,
so that its value is persistent across power-on resets.
Limitations, and Disclaimer
Of course a microcontroller with this software can't do everything
that a PLC will. Most PLC environments offer more features and
predefined blocks than my tool does. The PLC hardware is better too;
usually the inputs and outputs are designed to withstand incredible
electrical abuse. You can get a PIC16F877 on a carrier board for ten
or twenty USD, though, and you'll pay a fair bit more for a PLC with
the same capabilities.
So far I have received very few bug reports compared to the
number of people with questions or feature requests. There is
still great possibility for defects, especially in the targets for
microcontrollers that I do not physically have (and therefore cannot
test). Certainly do not use LDmicro for anything safety-critical,
or anything that would break something expensive if it failed.
As noted above, the code that it generates is far from
optimal. Also, not all of the data RAM in PIC16 devices is available
to the ladder logic program. This is because I have not implemented
very much support for all the paging nonsense. I do, however,
support the program memory paging, which is necessary to access
program memory locations in the PIC16 beyond 2k.
Download
This software is tested under all
versions from Windows 2000 to Windows 10. Unconfirmed reports suggest
that it works under WINE.
The download is a .exe file; there
are no other files required, so there is no installation program.
Save it somewhere on your computer and just run it, and it will
work. The manual is included in the .exe file, but you can
download it separately if you want.
The compiler generates Intel
IHEX files. Most of the programming software that I have seen
expects this. Of course you need some sort of programming gadget
to get the hex file into the chip. For the AVRs, I recommend an AVRISP
mkII, which is available from
various distributors.
For the PICs, I recommend Microchip's PICkit 2 or 3, which is
available from their web store.
Both of these are officially supported, connect over USB, and cost
less than 40 USD.
It should generally be possible to use code generated by LDmicro
with a bootloader. Most AVR parts have special fuses (BOOTRST, BOOTSZx)
that will need to be configured for whatever bootloader you are using.
The PIC16 parts don't have any specific hardware support for a bootloader,
but LDmicro generates code with the correct format to allow the
bootloader to rewrite the reset vector.
I would appreciate any bug reports. The following chips are supported:
- PIC16F628(A)
- PIC16F88
- PIC16F819
- PIC16F877(A)
- PIC16F876(A)
- PIC16F887
- PIC16F886
- ATmega128
- ATmega64
- ATmega162
- ATmega32
- ATmega16
- ATmega8
It is also possible to generate C code from the ladder program. That is
less convenient, but you could use it on any processor for which you
have a C compiler.
LDmicro can generate interpretable byte code. If you
are willing to write an interpreter then you can use this to run
your ladder code on any kind of target. There is not very much
documentation on this, but I provide a sample interpreter in
mostly-portable C.
You can download a build of my last stable release:
LDmicro is now maintained
by Ihor Nehrutsa, who has added
support for many new processors and other features. You can get more
recent builds from his repo:
The source code, and various other files, are also available for download.
This program may be distributed and modified under the terms of the
GPL Version 3.
Older releases are also available:
(right-click to save any of these files)
Please report any defects. This is free software, with no
department in charge of quality control. I do not even have the
hardware to test many of the targets myself. A bug that is not
reported is unlikely to ever be fixed.
I have a tutorial, in
which I describe how to enter a simple ladder diagram, simulate it,
and then generate an IHEX file and program it into a PIC. That is
probably the easiest way to get started with this software.
If you have questions about LDmicro, then ask them on
the forum.
January 2016, Portland, OR;
thanks to G. Mariani and A. Pedrazzi for Italian translations;
H. U. Noell for German; D. Corteletti and O. da Rocha for Portuguese;
M. Vaufleury for French; J. Pascual for Spanish; A. Akici for Turkish.
 |