Repository navigation
start stop
Start-stop is the classic first program in automation: start turns the motor on, stop turns it off, the motor keeps running after start is released, and stop wins when both are pressed. Let's compare it in ST, LAD (ladder logic) and ANSI C using the OpenCPLC libraries, considering two mapping styles in C: variable mapping and pointer mapping. If C code feels most readable and natural to you, you're probably on the right path.
| Signal | ST and LAD | C |
|---|---|---|
| Start button | %IX0.0 |
DI1 |
| Stop button | %IX0.1 |
DI2 |
| Motor | %QX0.0 |
RO1 |
PROGRAM main
VAR
start_button: BOOL := FALSE;
stop_button: BOOL := FALSE;
motor_running: BOOL := FALSE;
END_VAR
start_button := %IX0.0;
stop_button := %IX0.1;
IF stop_button THEN
motor_running := FALSE;
ELSIF start_button THEN
motor_running := TRUE;
END_IF;
%QX0.0 := motor_running;
END_PROGRAM
In every PLC cycle a coil takes the result of its rung, so without an extra contact motor_running would drop as soon as start is released.
The motor_running contact under start_button is the self-holding contact: once the coil is on, it keeps rung 3 closed until stop_button breaks the rung.
ST and C need no such contact, as a variable keeps its value until it is written again.
A coil S (set) turns motor_running on, and it stays on until a coil R (reset) turns it off.
With both buttons pressed, stop breaks rung 3 and rung 4 resets motor_running, so stop wins.
The program is the loop thread of a new project, made with opencplc -n myapp -b Uno for example.
Every pass of its while(1) is one cycle, as in a PLC: inputs are copied into variables at its start and the output is written from one at its end, like the first two rungs and the last one of each ladder.
let() ends the pass and hands the processor over to the other threads.
bool start_button = false;
bool stop_button = false;
bool motor_running = false;
void loop(void)
{
while(1) {
start_button = DIN_State(&DI1);
stop_button = DIN_State(&DI2);
if(stop_button) {
motor_running = false;
}
else if(start_button) {
motor_running = true;
}
DOUT_Preset(&RO1, motor_running);
let();
}
}Pointers name the inputs and the output in one place, and the code works on them directly: DOUT_Set and DOUT_Rst act like the coils S and R.
Both buttons are read by their state, as in the ladders: read by its edge with DIN_Rise, stop would act only at the moment it is pressed, and a stop held down would not keep the motor from starting.
DIN_t *start_button = &DI1;
DIN_t *stop_button = &DI2;
DOUT_t *motor_running = &RO1;
void loop(void)
{
while(1) {
if(DIN_State(stop_button)) {
DOUT_Rst(motor_running);
}
else if(DIN_State(start_button)) {
DOUT_Set(motor_running);
}
let();
}
}