Introducción
En este post, vamos a explorar el rendimiento de la Raspberry Pi PLC pinout tiempo de respuesta, examinando su capacidad para detectar y responder a los cambios en la entrada, así como la capacidad de las señales de salida.
Requisitos
Si deseas medir las características del PLC, también necesitarás un osciloscopio preciso capaz de leer en la escala de 100ns.
Salida digital
Para realizar la prueba de los pines, debes programar el PLC industrial RPI utilizando tanto Python como C, con el fin de comprobar si existen diferencias. Ambos programas realizan escrituras en un pin digital, midiendo el tiempo que tarda en ejecutarse la función de escritura.
La versión en C utiliza nuestra librería RPIPLC-LIB, que se puede encontrar en nuestro repositorio de GitHub, y el archivo common.h que se encuentra en sus pruebas. La versión en Python utiliza la librería correspondiente.
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()
Ambos lenguajes funcionan prácticamente igual, y el digitalWrite() apenas tarda 1ms en ejecutarse. Los tiempos de subida y bajada de la señal de salida son de 0,744µs y 55,6µs respectivamente. El tiempo de caída al ser más lento es el que determina la frecuencia máxima de salida del pin, ya que realizar escrituras sucesivas en un pin lo suficientemente rápido dará como resultado una señal que apenas se mantiene baja.
A continuación se muestran algunas mediciones que ilustran este comportamiento:
Lenguaje C:
Retraso | Frecuencia de salida | Periodo | Ancho de pulso |
0ms | 495Hz | 2.020ms | 1.888ms |
5ms | 82.24Hz | 12.16ms | 7.440ms |
10ms | 45.05Hz | 22.20ms | 11.20ms |
Lenguaje Python:
Retraso | Frecuencia de salida | Periodo | Ancho de pulso |
0ms | 478.9Hz | 2.088ms | 1.884ms |
5ms | 81.97Hz | 12.2ms | 7.44ms |
10ms | 45.05Hz | 22.2ms | 11.1ms |
Por lo tanto, se puede conseguir una señal de ciclo de trabajo del 50% a 45 Hz utilizando los pines de salida. Sin embargo, si el ciclo de trabajo no es crucial, se pueden conseguir frecuencias mucho más altas, de hasta 495 Hz.
Entrada digital
Las entradas digitales también pasan por un multiplexor de pines, el MCP23008. El PLC RPi dispone de diferentes tipos de entradas digitales: las entradas optoaisladas, las entradas de interrupción (que también están aisladas) y las entradas analógicas, que pueden funcionar también como digitales (5-24V).
Para comprobar el tiempo de lectura de la entrada, mide el tiempo de ejecución de la función digitalRead():
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()
El tiempo de ejecución de digitalRead() utilizando la entrada optoaislada(I0.0) es de 445.36µs de media con 816µs de máximo utilizando tanto C como Python, lo que se traduce en una frecuencia de muestreo máxima de 2245.4Hz.
En el caso de los pines de interrupción, el tiempo de ejecución es de 3µs de media y 74µs usando C. Y usando Python, la media es de 6,5µs y 81µs como máximo. Esto significa que la frecuencia máxima de muestreo es de unos 333,33kHz y 153,85kHz, respectivamente.
Por último, la entrada analógica utilizada como digital tiene un tiempo de ejecución de 1µs de media y 72µs utilizando C. Y utilizando Python, la media es de 4µs y 43µs como máximo. Esto significa que la frecuencia máxima de muestreo se sitúa en torno a 1MHz y 250kHz, respectivamente.
Salida digital
El PLC RPI también dispone de salidas analógicas. Estas salidas funcionan con las salidas del PCA9685A, utilizando circuitos para convertir el PWM en una señal continua con el voltaje adecuado. Como en los casos anteriores, puedes probar las salidas utilizando tanto C como 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()
El tiempo de ejecución de la función analogWrite() es de 1ms utilizando ambos lenguajes. En este caso, lo que determina la frecuencia máxima de salida son los tiempos de subida y bajada, 120ms y 173ms respectivamente, ya que son mucho más lentos que la ejecución del código. Considerando el tiempo de caída como la mitad del periodo, podemos conseguir una señal con una frecuencia de señal estable máxima de 2,89Hz , pudiendo estirarse hasta 4,87Hz.
Entrada analógica
Las entradas analógicas permiten medir tensiones entre 0V y 10V, devolviendo un valor con una resolución de 12 bits (0-4095). En el PLC RPI, las entradas analógicas utilizan un expansor I2C. Estos programas pueden utilizarse para probar la frecuencia de muestreo de las entradas analógicas:
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()
El tiempo medio de ejecución de la lectura analógica es de 860 µs, con un máximo de 2657 µs, tanto en C como en Python, lo que se traduce en una frecuencia de muestreo máxima de 1162,8 Hz.
Pins directos
Los PLCs Raspberry Pi también tienen algunos pines conectados directamente a la Raspberry. Estos son los pines directos disponibles, trabajando a 3.3V:
- TX
- RX
- MISO
- MOSI
- SCK
- GPIO8
También están los pines I2C, que funcionan a 5V y tienen un pull-up externo:
- SCL
- SDA
Salida
Lenguaje C:
Los pines directos pueden utilizarse como salidas. En este caso realizamos una prueba utilizando la librería pigpio:
#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);
}
}
La frecuencia máxima de la señal alcanzada para todos los pines es de 150kHz haciendo una especie de onda sinusoidal. Si quieres una forma cuadrada puedes llegar hasta 62,5kHz.
Lenguaje Python:
Los pines directos pueden utilizarse como salidas. En este caso, puede realizar una prueba utilizando la glibraría gpiozero:
from gpiozero import LED
led = LED(14)
while True:
led.on()
led.off()
La frecuencia de señal máxima alcanzada para todos los pines es de 62,5 kHz con un ciclo de trabajo del 50%.
Sus tiempos de subida y bajada, medidos con un osciloscopio, son ambos de 162ns. Los pines I2C, sin embargo, tienen un tiempo de subida ligeramente superior, 2,72µs.
Entrada
El tiempo de muestreo de los pines directos cuando se utilizan como entradas se puede medir utilizando este código:
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()
El método de lectura tarda de media hasta 8µs en ejecutarse usando esta librería, lo que significa que puedes alcanzar hasta una frecuencia de muestreo de 125kHz usando este método. Y puede tomar un máximo de 122µs de tiempo de ejecución.
Resumen
Entradas
Lenguaje C:
Tipo de entrada | Frecuencia de muestreo - Media | Frecuencia de muestreo - Tiempo máximo de lectura |
De interrupción digital | 333.33kHz (3µs) | 13.514kHz (74µs) |
Digital optoaislada | 2.245kHz (445.36µs) | 1.223kHz (816µs) |
Digital no optoaislada | 1MHz (1µs) | 13.889kHz (72µs) |
Analógica | 1.163 kHz(860µs) | 376.4Hz (2657µs) |
Directa de pines | 125kHz (8µs) | 8.197kHz (122µs) |
Lenguaje Python:
Tipo de entrada | Frecuencia de muestreo - Media | Frecuencia de muestreo - Tiempo máximo de lectura |
De interrupción digital | 153.85kHz (6.5µs) | 12.346kHz (81µs) |
Digital optoaislada | 2.245kHz (445.36µs) | 1.223kHz (816µs) |
Digital no optoaislada | 250kHz (4µs) | 23.256kHz (43µs) |
Analógica | 1.163 kHz(860µs) | 376.4Hz (2657µs) |
Directa de pines | 125kHz (8µs) | 8.197kHz (122µs) |
Salidas
Lenguaje C:
Tipo de salida | Frecuencia máxima estable | Frecuencia de señal - Máxima | Tiempo de subida | Tiempo de caída |
Digital (QX.0-7) | 50Hz (59.4%) | 495Hz (93.1%) | 0.744µs | 55.6µs |
Analógica (AX.5-7) | 2.89Hz (49%) | 4.87Hz (47%) | 120ms | 173ms |
Directa de pines | 62.5kHz (50%) | 62.5kHz (50%) | 162ns | 162ns |
Lenguaje Python:
Tipo de salida | Frecuencia máxima estable | Frecuencia de señal - Máxima | Tiempo de subida | Tiempo de caída |
Digital (QX.0-7) | 50Hz (59.4%) | 495Hz (93.1%) | 0.744µs | 55.6µs |
Analógica (AX.5-7) | 2.89Hz (49%) | 4.87Hz (47%) | 120ms | 173ms |
Directa de pines | 62.5kHz (50%) | 62.5kHz (50%) | 162ns | 162ns |
Conclusión
En este post, hemos analizado el tiempo de respuesta de los pines del controlador industrial Raspberry Pi PLC, examinando la capacidad de detectar y responder a los cambios de entrada, así como la capacidad de generar señales de salida.
- Los resultados muestran que, tanto en lenguaje C como en Python, los tiempos de ejecución de las operaciones de escritura y lectura de pines son bastante rápidos.
- La frecuencia de salida máxima alcanzada es de 495 Hz, mientras que la frecuencia de muestreo máxima para las entradas es de 1,163 kHz.
- En cuanto a las clavijas directas, se consigue una frecuencia de señal máxima de 150 kHz como onda sinusoidal.
En resumen, el PLC Raspberry Pi ha demostrado un sólido rendimiento en términos de tiempo de respuesta de los pines.
Análisis de la respuesta temporal del pinout del PLC Raspberry Pi