Análisis de la respuesta temporal del pinout del PLC Raspberry Pi

Análisis técnico del rendimiento de los pines del controlador industrial Raspberry Pi PLC
13 de junio de 2023 por
Análisis de la respuesta temporal del pinout del PLC Raspberry Pi
Boot & Work Corp. S.L., Ricard Franch Argullol

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

 
Los PLCs Raspberry Pi están equipados con un multiplexor de pines conocido como PCA9685A. Este circuito integrado utiliza la comunicación I2C para proporcionar las salidas necesarias al PLC, lo que le permite controlar varias salidas utilizando sólo un bus I2C. Como desventaja, estos pines son más lentos que los pines directos, ya que la comunicación I2C tiene cierta latencia en comparación con el control de los pines directos.

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 - MediaFrecuencia de muestreo - Tiempo máximo de lectura
De interrupción digital
13.514kHz (74µs)333.33kHz (3µs)
Digital optoaislada
1.223kHz (816µs)2.245kHz (445.36µs)
Digital no optoaislada
13.889kHz (72µs)1MHz (1µs)
Analógica
376.4Hz (2657µs)1.163 kHz(860µs)
Directa de pines
8.197kHz (122µs)125kHz (8µs)
Lenguaje Python:
Tipo de entrada
Frecuencia de muestreo - MediaFrecuencia de muestreo - Tiempo máximo de lectura
De interrupción digital
12.346kHz (81µs)153.85kHz (6.5µs)
Digital optoaislada
1.223kHz (816µs)2.245kHz (445.36µs)
Digital no optoaislada
23.256kHz (43µs)250kHz (4µs)
Analógica
376.4Hz (2657µs)1.163 kHz(860µs)
Directa de pines
8.197kHz (122µs)125kHz (8µs)
Salidas
Lenguaje C:
Tipo de salida
Frecuencia máxima estable
Frecuencia de señal - MáximaTiempo de subidaTiempo de caída
Digital (QX.0-7)50Hz (59.4%)
495Hz (93.1%)
0.744µs55.6µs
Analógica (AX.5-7)2.89Hz (49%)
4.87Hz (47%)
120ms
173ms
Directa de pines
150kHz (50%)
62.5kHz (50%)
162ns
162ns
Lenguaje Python:
Tipo de salida
Frecuencia máxima estable
Frecuencia de señal - MáximaTiempo de subidaTiempo de caída
Digital (QX.0-7)50Hz (59.4%)
495Hz (93.1%)
0.744µs55.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. 
Odoo • Text and Image

En resumen, el PLC Raspberry Pi ha demostrado un sólido rendimiento en términos de tiempo de respuesta de los pines.

Buscar en nuestro blog

Análisis de la respuesta temporal del pinout del PLC Raspberry Pi
Boot & Work Corp. S.L., Ricard Franch Argullol 13 de junio de 2023
Compartir

¿Estás buscando tu Controlador Lógico Programable ideal?

Echa un vistazo a esta comparativa de producto de varios controladores industriales basados en Arduino.

Comparamos entradas, salidas, comunicaciones y otras especificaciones con las de los equipos de otras marcas destacadas.


Industrial PLC comparison >>>