Analysis of Raspberry Pi PLC pinout time response
Albert Prieto Open Source
CEO @ IndustrialShields | New Business Development, Project Planning
Introduction
In this post, we will explore the?performance of the Raspberry Pi PLC pinout time response, examining its ability to detect and respond to changes in input,?as well as the ability to output signals.
Digital output
The Raspberry Pi PLCs are equipped with pin multiplexer known as PCA9685A. This integrated circuit uses I2C communication to provide the necessary outputs to the PLC, allowing it to control various outputs using just an I2C bus. As a downside, these pins are slower than direct pins, as the I2C communication has some latency compared to controlling direct pins.
To carry out the pins test, you must program the RPI PLC using both Python and C, in order to check if there are any differences. Both programs perform writes to a digital pin, measuring the time it takes to execute the write function.
The C version uses our RPIPLC-LIB library, which can be found in?our GitHub repository, and the?common.h?file found in its tests. the Python version uses the?corresponding library.
C:
#include <iostream>
#include <chrono>
#include <rpiplc.h>
#include "common.h"
int main() {
initPins();
pinMode(digitalOutputs[0], OUTPUT);
bool a = true;
for (;;) {
auto start = std::chrono::high_resolution_clock::now();
digitalWrite(digitalOutputs[0], a);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
std::cout << "Execution time: " << duration << " microseconds" << std::endl;
a = !a;
usleep(10000);
}
return 0;
}
Python:
from rpiplc_lib import rpiplc
import time
usleep = lambda x: time.sleep(x/1000000.0)
def main():
rpiplc.init("RPIPLC_21")
a=True
????????rpiplc.pin_mode("Q0.0", rpiplc.OUTPUT)
while True:
a = not a
start = time.time()
rpiplc.digital_write("Q0.0", a)
end = time.time()
res = ( end - start )
print(res)
usleep(10000)
if __name__ == "__main__":
main()
Both languages perform virtually the same, and the digitalWrite() barely takes 1ms to execute. The rise and fall time of the output signal are 0.744μs and 55.6μs respectively. The fall time being slower is what determines the maximum output frequency of the pin, as performing successive writes on a pin fast enough will result in a signal which barely stays low.
Some measurements illustrating this behaviour are shown below:
C language:
Python language:
Therefore, a 50% duty cycle 45Hz signal can be achieved using the output pins. if the duty cycle is not crucial, though, much higher frequencies, up to 495Hz, can be achieved.
Digital Input
The digital inputs also go through a pin multiplexer, the MCP23008. The RPi PLC has different types of digital inputs: the opto-isolated inputs, the interrupt inputs (which are also isolated) and the analogue inputs, which can work as digital (5-24V) too.
To test the reading time of the input, measure the execution time of the digitalRead() function:
C:
#include <iostream>
#include <chrono>
#include <rpiplc.h>
#include "common.h"
#define PIN digitalInputs[0]
#define READ_TYPE digitalRead
#define N_AVERAGE 10000
int main() {
initPins();
pinMode(PIN, INPUT);
unsigned long t_max, t_average;
for (long i = 0; i < N_AVERAGE; i++) {
auto t1 = std::chrono::high_resolution_clock::now();
READ_TYPE(PIN);
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
t_average += duration;
if (t_max < duration) t_max = duration;
}
std::cout << "Average: " << t_average/N_AVERAGE << " microseconds" << std::endl;
std::cout << "Maximum: " << t_max << " microseconds" << std::endl;
return 0;
}
Python:
from rpiplc_lib import rpiplc
import time
PIN = "I0.7"
READ_TYPE = rpiplc.digital_read
N_AVERAGE = 10000
def main():
rpiplc.init("RPIPLC_21")
rpiplc.pin_mode(PIN, rpiplc.INPUT)
t_max = 0; t_average = 0;
for i in range(N_AVERAGE):
start = time.perf_counter()
READ_TYPE(PIN)
end = time.perf_counter()
res = (end - start) * 1e6
t_average += res
if t_max < res: t_max = res
print("Average: {}\nMaximum: {}".format(t_average/float(N_AVERAGE), t_max))
if __name__ == "__main__":
main()
The execution time of the digitalRead() using the opto-isolated input(I0.0) is 445.36μs average with 816μs maximum using both C and Python, which translates to a maximum sampling frequency of 2245.4Hz.
In the case of the interrupt pins, the execution time is 3μs on average and 74μs using C. And using Python, the average is 6.5μs and 81μs maximum. This means that the maximum sampling frequency is around 333.33kHz and 153.85kHz, respectively.
Finally, the analogue input used as a digital has an execution time of 1μs on average and 72μs using C. And using Python, the average is 4μs and 43μs maximum. This means that the maximum sampling frequency is around 1MHz and 250kHz, respectively.
Analog output
The RPI PLC also features analogue outputs. These outputs work with the PCA9685A outputs, using circuitry to convert the PWM to a continuous signal with the adequate voltage. As the previous cases, you can test the outputs using both C and Python:
C:
#include <iostream>
#include <chrono>
#include <rpiplc.h>
#include "common.h"
int main() {
initPins();
pinMode(analogOutputs[0], OUTPUT);
int a = 4095;
for (;;) {
auto start = std::chrono::high_resolution_clock::now();
analogWrite(analogOutputs[0], a);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
std::cout << "Execution time: " << duration << " microseconds" << std::endl;
usleep(1000000);
a = (a == 4095) ? 0 : 4095;
}
return 0;
}
Python:
from rpiplc_lib import rpiplc
import time
usleep = lambda x: time.sleep(x/1000000.0)
def main():
rpiplc.init("RPIPLC_21")
a=4095
????????rpiplc.pin_mode("A0.5", rpiplc.OUTPUT)
while True:
if a == 4095: a = 0
else: a = 4095
start = time.time()
rpiplc.analog_write("A0.5", a)
end = time.time()
res = ( end - start )
print(res)
usleep(1000000)
if __name__ == "__main__":
main()
The execution time of the analogWrite() function is 1ms using both languages. In this case, what determines the maximum output frequency are the rise and fall time, 120ms and 173ms respectively, as they are much slower than the code execution. Considering the fall time as half the period, we can achieve a signal with a maximum stable signal frequency of 2.89Hz , and could be stretched up to 4.87Hz.
Analog input
The analogue inputs allow measuring voltages between 0V and 10V, returning a value with a 12 bit resolution (0-4095). In the RPI PLC, the analogue inputs use an I2C expansor. These programs can be used to test the analog input sampling frequency:
C:
#include <iostream>
#include <chrono>
#include <rpiplc.h>
#include "common.h"
int main() {
initPins();
pinMode(analogInputs[0], INPUT);
for (;;) {
auto start = std::chrono::high_resolution_clock::now();
int a = analogRead(analogInputs[0]);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
std::cout << "Execution time: " << duration << " microseconds" << std::endl;
std::cout << "Read result: " << a << std::endl;
}
return 0;
}
Python:
from rpiplc_lib import rpiplc
import time
usleep = lambda x: time.sleep(x/1000000.0)
def main():
rpiplc.init("RPIPLC_21")
rpiplc.pin_mode("I0.7", rpiplc.INPUT)
while True:
start = time.time()
a = rpiplc.analog_read("I0.7")
end = time.time()
res = ( end - start )
print(res)
print(a)
usleep(1000000)
if __name__ == "__main__":
main()
The analogue readout runtime averages 860μs with a maximum of 2657μs using both C and Python, which translates to a maximum sample rate of 1162.8Hz.
领英推荐
Direct Pins
The Raspberry Pi PLCs also have some pins connected?directly?to the Raspberry. These are the available direct pins, working at 3.3V:
There are also the I2C pins, which operate at 5V and have an external pull-up:
Output
C language:
The direct pins can be used as outputs. In this case we conducted a test using the?pigpio library:
#include <pigpio.h>
#include <iostream>
#include <thread>
#define PIN 8
#define DELAY_NS 8*1000
void delayNanoSeconds(unsigned int nanoseconds) {
auto start = std::chrono::high_resolution_clock::now();
auto end = start + std::chrono::nanoseconds(nanoseconds);
while (std::chrono::high_resolution_clock::now() < end);
}
int main() {
if (gpioInitialise() < 0) {
std::cout << "Failed to initialize pigpio library." << std::endl;
return 1;
}
gpioSetMode(PIN, PI_OUTPUT);
while (1) {
gpioWrite(PIN, 1);
delayNanoSeconds(DELAY_NS);
gpioWrite(PIN, 0);
delayNanoSeconds(DELAY_NS);
}
}
The maximum frequency of the signal achieved for all pins is 150kHz doing a kind of sine wave. If you want a square shape you can go up to 62.5kHz.
Python language:
The direct pins can be used as outputs. In this case, you can test using the?gpiozero library:
from gpiozero import LED
led = LED(14)
while True:
led.on()
led.off()
The maximum signal frequency achieved for all the pins is 62.5kHz with a 50% duty cycle.
Their rise and fall time, measured with an oscilloscope, are both 162ns. The I2C pins, however, have a slightly higher rise time, 2.72μs.
Input
The sampling time of the direct pins when used as inputs can be measured using this code:
from gpiozero import Button
import time
button = Button(8)
N_AVERAGE = 10000
def main():
t_max = 0; t_average = 0;
for i in range(N_AVERAGE):
start = time.perf_counter()
button.is_pressed;
end = time.perf_counter()
res = (end - start) * 1e6
t_average += res
if t_max < res: t_max = res
print("Average: {}\nMaximum: {}".format(t_average/float(N_AVERAGE), t_max))
if __name__ == "__main__":
main()
The read method takes on average up to 8μs to execute using this library, which means you can achieve up to a 125kHz sampling frequency using this method. And it can take a maximum of 122μs of execution time.
Summary
Inputs
C language:
Python language:
Outputs
C language:
Python language:
Conclusion
In this post, we have analyzed the response time of the pins of the Raspberry Pi PLC industrial controller, examining the ability to detect and respond to input changes, as well as the ability to generate output signals.?
# In summary, the Raspberry Pi PLC has demonstrated solid performance in terms of pin response time.
Requirements
If you want to measure the industrial PLC characteristics, you will also need a precise oscilloscope capable of reading at the 100ns scale.
Owner at TRITON FAMME s.r.o.
1 年Thank you for the excellent article. As we can see, the RPi PLC can also handle fast optical encoders (e.g. 10 kHz) if connected to non-isolated pins. So far we have been using MDuino PLCs for these applications, where the reading speed is orders of magnitude faster. Either way, this disproves the common notion that PLCs are "not friends" with fast events ?.