Raspberry Pi to Arduino SPI Max Data Transfer Rate

There are many ways to send data from Raspberry Pi to an Arduino. One of these is by using the SPI interface. Here are some of the things to consider when trying to get as much speed as possible for the transfer.  The end goal of this project is to control addressable led strips from the arduino so we need to transfer as many bytes as possible from the pi to the arduino in the highest transfer rate possible.

 

01_setup  01_setup_2

The setup I’m using is shown above.

Theory

The specs of the raspberry pi give it a very high SPI transfer rate – in the tens of megabits per second.  The limiting factor is the arduino where the receive rate is Clock speed / 4.  So if you’re using a 16Mhz arduino, the theoretical rate at which it can receive is 4 Mbits per second or 500 KBps.  Unfortunately, depending on your practical considerations, you will most likely not get this high rate.

Hardware: Signal rise time

03_risetime_3

In the image above, the yellow lines are from the raspberry pi gpio output going to the level converter and the blue lines are from the output of the level converter.  We’re running the SPI at 4Mhz.  The output from the PI looks ok but the input going to the Arduino don’t look like proper square waves anymore.

03_risetime  03_risetime_2

The raspberry pi gpio’s have 3.3 v outputs while the arduino inputs are 5 volts.  I’m using a CD4050 IC to convert the voltage level.  The output takes around 400 nanoseconds to get to 5 volts.  This delay caused by the level conversion means the maximum clock cycle is 800 nano seconds limiting you maximum speed to 1.2 5Mbps (We set the PI at  1Mhz).  Some other level converter chips may have faster rise times.  A common one is the 74LVC245 but I haven’t tried it yet.

Software: From Wiring pi library to interrupts in Arduino 1 byte transfers at 1 Mhz

I’m using the wiring Pi library to send data through the SPI interface.  On the arduino side, we use an interrupt handler to get the data sent by the pi.  The Arduino sends back a reply to the PI.

Here’s the code snippet on the PI side.  We send 2 times because the way SPI works is that it sends the return byte from the arduino at the same time as the next send of the PI.

int ping(int fd)
{
   unsigned char b,bAck;
   b = MYSPI_PING;
   wiringPiSPIDataRW2(fd,SPI_CHANNEL, &b,1);
   b = 0;
   wiringPiSPIDataRW2(fd,SPI_CHANNEL, &b,1);
   bAck = b;
   if (bAck == MYSPI_ACK) {
      return(0);
   } else {
      printf("bAck = %02x  ",bAck);
   }
}

On the Arduino side we have the interrupt handler. If it receives the ping byte it just responds with an ack byte. The two asm() lines trigger an output pin on the ardunio to go high and low so that we can see when the interrupt code gets called on the oscilloscope display.

ISR(SPI_STC_vect)
{
   byte c;
   asm ("sbi %0, %1 \n" : : "I" (_SFR_IO_ADDR(PORTD)), "I" (PORTD6));
   c = SPDR;
   if (c == MYSPI_PING) {
      SPDR = MYSPI_ACK;
   }
   asm ("cbi %0, %1 \n" : : "I" (_SFR_IO_ADDR(PORTD)), "I" (PORTD6));
}

 

06_irq_funcgap_1byte 

In the image above the yellow lines are the clock signal from the raspberry pi while the blue lines represent the interrupt received by the arduino.  The SPI  on the PI is set to transmit at 1 Mhz, 1.e. 1 micro sec per bit but the rate seems more like 1 bit per 1.25 microsecs based on the oscilloscope.  Each byte is sent approximately every 15 microseconds.  The 10+ microsecond gap seems to be coming from the SPI device driver on the PI.  Using this method we can get a maximum of around 60KBps transfer rate.

06_irq_funcgap_1byte_2

In this closeup we can see the interrupt in the Arduino taking up less than a microsecond, but this is just the code block itself.  There is still additional overhead from the triggering and processing of the interrupt.

Software: From Wiring pi library to interrupts in Arduino Multiple byte transfers

If we could reduce the gap between byte transfers we could get a higher transfer rate.  Here’s what happens when we try a 2 byte transfer.  This would use the following code from the PI.

int ping(int fd)
{
   unsigned char b[2],bAck;
   b[0] = MYSPI_PING;
   b[1] = 0;
   wiringPiSPIDataRW2(fd,SPI_CHANNEL, b,2);
   bAck = b[1];
   if (bAck == MYSPI_ACK) {
      return(0);
   } else {
      printf("Error bAck = %02x\n",bAck);
      return(-1);
   }
}

06_irq_funcgap_2bytes_1Mhz

The image above shows the transfer at 1 Mhz.  The two bytes are send more closely together but you can still notice the gap between function calls caused by the device driver.  The problem is that the time for interrupt handling on the Arduino is shortened.  The following are the results:

Error bAck = ff Fail header ack   110823 789 0
Error bAck = ff Fail header ack   110934 790 0
Error bAck = ff Fail header ack   111121 791 0
Error bAck = ff Fail header ack   111381 792 0

This says that the response sent by the Arduino is wrong for about every 1000+ bytes.

06_irq_funcgap_2bytes_1Mhz_2

You can see in the closeup that the end of the interrupt is very close to the start of receiving the next byte.  This may be the cause of the errors.  Depending on the app we could live with this by doing retransmits on errors but we’re transferring big blocks quickly for a LED strip where we need every byte to be accurate.

Software: From Wiring pi library to interrupts in Arduino 1 byte transfers at 500 khz

So we now try it at half the rate – 500 khz.

06_irq_funcgap_2bytes_500khz  06_irq_funcgap_2bytes_500khz_2

In the two images above you can see that there’s a bigger gap between the end of the interrupt processing and the start of the next byte.  Here are the results.

Error bAck = ff Fail header ack   101757 276 0
Error bAck = ff Fail header ack   101873 277 0
Error bAck = ff Fail header ack   102419 278 0
Error bAck = ff Fail header ack   103523 279 0

There are still errors, althoug it’s only about a third of the errors at 1 Mhz.

Software: From Wiring pi library to interrupts in Arduino 1 byte transfers at 100 khz

07_irq_funcgap_2bytes_100khz  07_irq_funcgap_2bytes_100khz_2

At 100 Khz, there are no errors, but this just gives a transfer rate of around 6 KBps.

Software: Not using interrupts

05_irq

In this final test, we try sending 100 bytes straight from the PI to the Arduino at 1 Mhz.   The code on the PI end is:

len = 100;
wiringPiSPIDataRW2(fd,SPI_CHANNEL,buff,len);

And on the Arduino side, to avoid the overhead of interrupts, we just keep waiting in a tight loop.  We just included some dummy code to represent buffer leng checks and timeouts.


while (1) {
   while (!(SPSR & (1<<SPIF)));
   asm ( "sbi %0, %1 \n"  : : "I" (_SFR_IO_ADDR(PORTD)), "I" (PORTD6));
   timeOutCtr = 0;
   SPDR = 0x51;
   buffLen++;
   if (buffLen >= bytesToRecv) {
      state = 0;
   }
   asm (  "cbi %0, %1 \n"  : : "I" (_SFR_IO_ADDR(PORTD)), "I" (PORTD6));
}

At 1 Mhz, the output still has errors.  This is the returned byte stream from the Arduino.  They should all be 0×51.  There are still a lot of errors as you can see.

51 51 28 d1 51 51 51 51 28 d1 51 51 51 51 28 8f 51 51 28 d1 51 51 51 51 28 d1 51 51 51 51 28 d1 51 51 51 51 28 d1 51 51 51 51 28 d1 51 51 51 28 a8 d1 51 51 51 28 d1 51 51 51 51 28 d1 51 51 51 51 28 d1 51 51 51 51 28 d1 51 51 51 51 28 d1 51 51 51 28 d1 51 51 51 51 28 d1 51 51 51 51 28 df 51 51

At 100 khz it worked so the transfer rate is around 12.5 KBps.

51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51

So there you have it, because of a mixture of hardware and software issues the transfer rates you will get may note reach the theoretical rate.  The best are for improvement based on the results here is to be able to control the gap in sending multiple bytes from the raspberry PI.  If we use 4 Mhz and spend 2 usecs sending a byte and 2 usecs in the Arduino interrupt, we should be able to get 250 KBps.  That;s much faster than our results so far but it’s still half of the theoretical rate at 4 Mhz.

Add a Comment