Analysis of Raspberry Pi PLC pinout time response

Analysis of Raspberry Pi PLC pinout time response

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:

No alt text provided for this image

Python language:

No alt text provided for this image

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:

  • TX
  • RX
  • MISO
  • MOSI
  • SCK
  • GPIO8

There are also the I2C pins, which operate at 5V and have an external pull-up:

  • SCL
  • SDA

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:

No alt text provided for this image

Python language:

No alt text provided for this image

Outputs

C language:

No alt text provided for this image

Python language:

No alt text provided for this image

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.?

  • The results show that, in both C and Python languages, the execution times for pin write and read operations are quite fast.?
  • The maximum output frequency achieved is 495Hz, while the maximum sampling frequency for the inputs is 1.163kHz.
  • As for the direct pins, a maximum signal frequency of 150kHz is achieved as a sine wave.?


# In summary, the Raspberry Pi PLC has demonstrated solid performance in terms of pin response time.

Requirements

Raspberry Pi PLC >>

Power supply >>

Library RPIPLC-LIB >>

Library RPIPLC-PYTHON-LIB >>

If you want to measure the industrial PLC characteristics, you will also need a precise oscilloscope capable of reading at the 100ns scale.

Richard Fabo

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 ?.

回复

要查看或添加评论,请登录

Albert Prieto Open Source的更多文章

社区洞察

其他会员也浏览了