Bus SPI on PLC Raspberry from Industrial Shields

Using the Serial Peripheral Interface Bus (SPI)
November 2, 2023 by
Bus SPI on PLC Raspberry from Industrial Shields
Boot & Work Corp. S.L., Joan Vilardaga Castro

Index


Introduction


In this post we will learn about the SPI bus, one of the communications available in all of our Raspberry PLCs family.

The SPI bus is interesting because a wide variety of sensors and commercial devices have an SPI interface as a means of communication. For example, many Display TFT units use SPI protocol, as well as a wide range of sensors such as thermocouples, temperature sensors, or humidity sensors. 

Requirements


In the sketch example, we have used a Raspberry 58 PLC and an M-Duino 21+ PLC. But, any Raspberry PLC and/or M-Duino PLC should do, as long as there is one of each.

Industrial Raspberry PLC >>>

Power Supply >>>

You will also need SPI and Adafruit-Blinka libraries in the Raspberry PLC. You can install them with these commands:

sudo apt-get install -y python3-pip
pip3 install spidev adafruit-blinka

The SPI protocol


The SPI (Serial Peripheral Interface) bus was developed by Motorola in 1980. It is currently a standard in the world of electronics and automation.

The SPI bus has a master-slave architecture.

The master device can initiate communication with one or more slave devices, and send or receive data from them. Slave devices cannot initiate communication, nor exchange data with each other directly. 

Data communication between masters and slaves takes place on two independent lines, one from the master to the slaves, and another from the slaves to the master. Therefore the communication is full duplex, that is, the master can send and receive data simultaneously.

Another feature of SPI is that it is a synchronous bus. The master device provides a clock signal, which keeps all devices synchronized. This reduces the complexity of the system compared to asynchronous systems. 

Then, we need a minimum of 3 signals for the transmission of data through SPI: one of the lines to send data to the slaves, another line to receive data from the different slaves and the clock signal to synchronize the entire bus. In addition, an additional SS (Slave Select) line is required for each connected slave device, to select the device with which the communication is to be made:

  • MOSI (Master-out, slave-in): Master to Slave communication
  • MISO (Master-in, slave-out): Slave to Master communication.
  • SCK (Clock): Clock signal generated by the master.
  • SS (Slave Select): It selects the device with which communication is to be made.

Advantages and Disadvantages


Advantages:

  • Full Duplex Communication.
  • Higher transmission speed than with I²C or SMBus.
  • Flexible protocol in which you can have absolute control over the transmitted bits.
  • It is not limited to 8-bit block transfer.
  • The hardware implementation is extremely simple.
  • It consumes less energy than I²C or SMBus because it has simpler and fewer circuit, and it doesn't  need pull-up resistors. 
  • No arbitration or failure response mechanism is necessary.
  • Slave devices use the clock sent, therefore they do not need their own clock.
  • It is not mandatory to implement a transceiver (transmitter and receiver): a connected device can be configured to only send, only receive, or both options at the same time.
  • It uses much fewer terminals on each chip/connector than an equivalent parallel interface.
  • At most, a single specific signal is required for each user (SS signal); the other signals can be shared.

Disadvantages:

  • It uses more pins for each chip than I²C, even in the 3-wire variant.
  • The addressing is done through specific lines (out-of-band signaling) unlike what happens in I²C where each chip is selected by a 7-bit address that is sent by the same bus lines.
  • There is no hardware flow control.
  • It does not easily allow having multiple masters connected to the bus.
  • It only works over short distances unlike, for example, RS-232 or RS-485.

Typical SPI connections


Option 1) If it is a connection where few slaves have to be connected, the parallel connection scheme should be used:

SPI Cascade parallel diagram

An additional SS (Slave Select) line is required for each slave device connected, to select the device with which communication has to be made.

Option 2) This previous connection has the disadvantage of requiring a line for each slave device. In the case of having many slave devices, this may not be practical, so it is possible to adopt a cascade connection, where each slave transmits data to the next one:

SPI Cascade connection diagram

On the other hand, in this configuration the information must reach all the slaves so that the communication is terminated, that is why, in general, the bus response speed is lower than in the first configuration.

Hardware


Every Industrial Shields programmable logic controller has the SO, SI and SCK pins. Check the User Manual of your industrial PLC version to see where these pins are located. Also, check the Pinout chapter of the User Manual to see how many pins can be used as SS (Slave Select). Here you have the Raspberry 58+ PLC controller example, picking up the GPIO8 pin as SS:

SPI Pinout of Raspberry PLCs (Communication board)

Software


To use the SPI port on Raspberry we can use the Python library Adafruit-Blinka, which contains high-level elements and low-level drivers to interact with the SPI interface.

Basic SPI functions:

spi = board.SPI()       # Creates an SPI object to interact with the bus
spi.try_lock()          # Tries to get control of the SPI bus    
spi.write(write_buff)   # Sends the data through the bus
spi.readinto(read_buff) # If anything was received, it will put it in the read buffer
                        # if not, it will zero it.
# Both write_buff and read_buff must be of "byte" types.

If you want to change settings like clock speed, phase or polarity:

spi.configure(100000, 0, 0) # speed, phase and polarity
                            # By default, clock normally low-rising edge

However, being that each device has an specific data frame,  we normally shouldn't use these functions directly, and our use of the SPI bus is carried out indirectly through the library of the component used. Luckily, there are a lot of wrapper libraries for SPI chips that are compatible with the Adafruit-Blinka library. You can found them searching for your specific module.

"Echo" example


An "echo" is essentially a program that takes in some input, processes it in some way, and then outputs that same input. It's commonly used as a basic test program to make sure that communication is working correctly between two devices.

In this example, the Raspberry PLC will be the master and the M-Duino will be the slave:

Raspberry Python script:

import board
import digitalio
spi = board.SPI()  # uses SPI0 default pins
cs = digitalio.DigitalInOut(board.CE0) # select chip, GPIO8 by default

try:
    spi.unlock() # first unlock if SPI is being used by other programs
except ValueError:
    print("SPI not locked, proceeding to lock")
while not spi.try_lock():
    pass
spi.configure(100000) # change sclk frequency

try:
    write_buff = bytearray(b'0')
    while True:
        cs.value = False # select the chip
        read_buff = bytearray(1)
        spi.write(write_buff)
        spi.readinto(read_buff)
        if (read_buff != write_buff):
            print("Expected {}, but received {}" \
                  .format(write_buff, read_buff))
        else:
            print("Sent correctly")
        cs.value = True # de-select the chip
        time.sleep(1)

except KeyboardInterrupt:  # unlock the spi bus when ctrl-c'ing out of the loop
    print("Unlocking SPI bus and exiting...")
    spi.unlock()

M-Duino program:

#include  <SPI.h>

volatile boolean received;
volatile byte receivedData;
ISR (SPI_STC_vect) {     // Interrupt routine function
  receivedData = SPDR;   // Get the received data from SPDR register
  received = true;       // Sets received as True 
}

void setup() {
  Serial.begin(115200);
  pinMode(MISO,OUTPUT);   //Sets MISO as OUTPUT
  SPCR |= _BV(SPE);       //Turn on SPI in Slave Mode
  received = false;
  SPCR |= _BV(SPIE);
}

void loop() { 
  if(received) {                        
    SPDR = receivedData;    // send back the received data
    received = false;
    Serial.println((char) receivedData);
  }
}

If we run these two programs, connecting both PLCs as we seen before in option 1, we will see that the Raspberry PLC is always receiving the character '0'.

​Search in our Blog

Bus SPI on PLC Raspberry from Industrial Shields
Boot & Work Corp. S.L., Joan Vilardaga Castro November 2, 2023

Looking for your ideal Programmable Logic Controller?

Take a look at this product comparison with other industrial controllers Arduino-based. 

We are comparing inputs, outputs, communications and other features with the ones of the relevant brands.


Industrial PLC comparison >>>