SPI interrupt is slow!

Hello,

I have connected a Raspberry Pi (Master) to an Arduino Uno (Slave) using SPI. I want to send data from Arduino to RPi. I know that the data should be written in 8 bit packets to the SPDR register. Normally, the clock speed of spi is 2 MHz, however if I want to send data from Arduino and I have some "If..else" conditions in the SPI ISR, then I have to lower the spi clock speed. This problem gets even worse when the code in SPI ISR has more if..else statements. For instance, for the following code in the SPI ISR, I have to lower the clock speed to 500 KHz, to be able to get the data on the RPi.

ISR (SPI_STC_vect)
{

  data[bytes] = SPDR;

  if(bytes == 0){
    //determine if read/write
    spiWrite = SPDR & 0x80;
    spiAddress = SPDR & 0x7F;

    if(!spiWrite){
      test = 1;
      //set registry for requested data
      if(spiAddress == 49){
        test = 90;
      }
      else if(spiAddress == 50){
        test = 2;
        SPDR = motorASpeed;
      }
      else if(spiAddress == 51){
        test = 3;
        SPDR = motorBSpeed;
      }
      else 
        test = 4;
        
    }
  }
  else if(bytes == 1 && spiWrite){
    //store data to variable
    if(spiAddress == 50)
      motorASpeed = SPDR;
    else if(spiAddress = 51)
      motorBSpeed = SPDR;
  }
  
  bytes++;

 
  
}

Is there any way to send data more rapidly or I can't get better results with 16MHz Arduino?

It looks like you can check for an overflow by reading the SPSR before you read the SPDR. Bit 6 will be set if the SPDR was written in the middle of a data transfer.

Perhaps, instead of reading the SPDR multiple times, you should store the SPDR into a local variable near the beginning of the ISR and use the variable.

What is the symptom of not being "able to get the data on the RPi."? What value are you sending and what value are you receiving? Are you sure the data mode is set the same at both ends?

ata1988:
and I have some "If..else" conditions in the SPI ISR, then I have to lower the spi clock speed. This problem gets even worse when the code in SPI ISR has more if..else statements.

Why not take all the IF statements out of the ISR and just use it to collect a byte of data, or a succession of bytes. Then set a flag in the ISR which tells the main part of your program that the data has arrived and can be processed.

...R

If I want to send data from RPi to Arduino, I would save the data in a Buffer each time that a Byte is sent, however the main issue is in sending the data from Arduino to RPi. I have to put the data (one byte at a time) in the SPDR.

I am sending 2 bytes from RPi to Arduino. The first Byte specifies the Read/Write command and also which register the R/W should be applied and the second Byte is the Data. Suppose I want to read data from Arduino. I send the first Byte to command that I want to read a certain register and I send the second Byte so that I get that value in the SPDR. I need to have some conditions in the ISR and that creates the issue with the clock speed of SPI.

ata1988:
I need to have some conditions in the ISR

Why?

I doubt very much that you do - or at least not the sort that would noticeably impact the speed.

...R

Robin2:
Why?

I doubt very much that you do - or at least not the sort that would noticeably impact the speed.

...R

There are going to be three motors in my setup. I will send commands to these motors to read or write to specific registers. For example, I might need to send back the number of encoder pulses from arduino to RPi. In this case, I will need to have some conditions in my ISR to know that I have that specific command and put the data on the SPDR register so that it can be sent to the RPi.

ata1988:
There are going to be three motors in my setup. I will send commands to these motors to read or write to specific registers. For example, I might need to send back the number of encoder pulses from arduino to RPi. In this case, I will need to have some conditions in my ISR to know that I have that specific command and put the data on the SPDR register so that it can be sent to the RPi.

If this was my project I think I would send a standardised message that is long enough to always include commands for all 3 motors, even if the commands have not changed. And I would always send back a standardised reply that contains all the necessary info - even if, on some (or most) occasions all of the info is not necessary.

That way the ISR has no decisions to make. It just saves the incoming data to a suitable buffer and sends back whatever info has been pre-prepared by the main body of code.

IMHO (based on my own experience) it is well worthwhile spending a considerable amount of time planning the communication system to make it easy to implement at both ends before starting any coding.

By the way, is there some reason that you are not using Serial for communication - it might be simpler.

...R

Robin2:
If this was my project I think I would send a standardised message that is long enough to always include commands for all 3 motors, even if the commands have not changed. And I would always send back a standardised reply that contains all the necessary info - even if, on some (or most) occasions all of the info is not necessary.

That way the ISR has no decisions to make. It just saves the incoming data to a suitable buffer and sends back whatever info has been pre-prepared by the main body of code.

IMHO (based on my own experience) it is well worthwhile spending a considerable amount of time planning the communication system to make it easy to implement at both ends before starting any coding.

By the way, is there some reason that you are not using Serial for communication - it might be simpler.

...R

This is a great idea. I will work on it to see if it solves this issue.

Actually, I want to use SPI because I have to communicate with other chips also (besides Arduino).

The issue with speed of transfer could be, also, related to the operational speed of the 3.3V (Rpi) to 5V (Arduino) interface. I had such a problem once, an upgrade in components let me go from 9600 to 230400 baud.

I used CAN for a bit for microcontroller to RPi comms, works good but required more hardware.

I've currently settled on WiFi/MQTT, working great and is highly expandable.

ata1988:
[…] For instance, for the following code in the SPI ISR, I have to lower the clock speed to 500 KHz, to be able to get the data on the RPi.

Have you thought on “why at reduced speed of 5 kbits/s in place of 2 Mbit/s” you can exchange data
smoothly?

At 2 Mbits/s speed, exchange of 1-byte data takes 4 us time. After the elapse of the said 4 us time, the Master is probably issuing another byte-oriented command. Look at your ISR routine; where, you have so much “over head tasks”; do you think that the Slave can doing all these tasks in 4 us time? It simply can’t that what I am saying from my experience.

On the other hand, at 5 kbit/s speed, exchange of 1-byte data takes 1600 us time. At the elapse of another “1600 us time slot”, the next command/data frame will arrive from Master to Slave. As a result, you know or not, you have given a lot of time to the Slave to finish the tasks of the ISR. You observe that there is a smooth data exchange; you think that the problem is solved; but, the problem is still there; you must be able to exchange data at 2 Mbits/s speed.

So, the probable solution is the following due to @Robin2:

Robin2:
Why not take all the IF statements out of the ISR and just use it to collect a byte of data, or a succession of bytes. Then set a flag in the ISR which tells the main part of your program that the data has arrived and can be processed.

The examples of the attached pdf file may worth reading to you in order to see how the flag concept proposed @Robin2 could be implemented.

Ch-7OnlineLec.pdf (291 KB)