can this be optimised for speed further?

So I'm setting a SPI communication between 2 arduino UNO and what I have noticed is, if I send successive request MASTER-> SLAVE, the slave messed up but if I put a small delay between the messages, it captures all the data.

I suspect its it was the code in the ISR and I've tried to strip it as much as possible in the hope of reducing its execution time. It seems to work now but I was wondering if it could be optimised further (besides going to assembly).

any good advice?

// SPI interrupt routine (all variables a global volatile)
ISR (SPI_STC_vect)
{
  byte spi = SPDR;

  if (new_data_avail == 0) {

    if (spi == 0 && start == 0) { //send data/confrimation
      SPDR = tx_arr[j];
      j = (j + 1) % tx_arr_size;
    }
    else if (start == 0) {//new request
      i=0;
      start = spi; //number of bytes to be recieved
      SPDR = spi | ACK; //acknowledge start of comms
    }
    else if (i < start) {
      rx_arr[i++] = spi;
      //SPDR = spi; //ping back rx byte. line not require as value already in SPDR!
    }
    else {
      ++i;
    }

    if (i > start) {
      new_data_avail = 1;
      SPDR = ERROR_BUSY; //any further command received while new_data_avail==1 with get a 0x7F reply
    }

    spi_oldtime = micros();
  }
  else { //if busy or unable to recieve request
    SPDR = ERROR_BUSY;
  }
}
j = (j + 1) % tx_arr_size;

That's probably faster as:

if(++j == tx_arr_size) j = 0;

The modulo operator is pretty slow but you can just test here since it only ever increases by one. I am assuming of course that j isn't changing elsewhere. That might make a difference.

sherzaad:
any good advice?

Ugh. This is why snippets are a problem...

Are any variables volatile?

Delta_G:

j = (j + 1) % tx_arr_size;

That's probably faster as:

if(++j == tx_arr_size) j = 0;

The modulo operator is pretty slow but you can just test here since it only ever increases by one. I am assuming of course that j isn't changing elsewhere. That might make a difference.

thank you Delta_G. I'll try it out

This is a radical difference from your implementation but worth thinking about. I probably fouled up your logic but I'm sure it can be properly adapted.

Use a circular buffer where the ISR advances the head pointer and the mainline follows up when it can by blasting through the state machine advancing the tail pointer until the two are equal.

Notice how small and quick the ISR is.

You might need to add timeouts to reset the SM if comms are lost.

It's a thought...

#define PACKET_SIZE_MAX     20              //whatever the largest expected data msg is

//circular receive and transmit buffer sizes
//set to power of two for easy masking
#define BUFF_SIZE           64              //0x40
#define BUFF_MASK           (BUFF_SIZE-1)   //0x3F

byte
    Data[PACKET_SIZE_MAX];
volatile byte
    rxBuffer[BUFF_SIZE];
volatile byte
    txBuffer[BUFF_SIZE];
    
volatile byte
    headPtr,
    tailPtr;
volatile byte
    txPtr;
    
void setup()
{
    headPtr = 0;
    tailPtr = 0;
    txPtr = 0;
    
}//setup

void loop()
{
    ReceiveData();
    
}//loop

void ReceiveData( void )
{
    byte
        curr_headPtr;
    static byte
        numBytes,
        dataIdx,
        stateSPI_RxData = START;

    curr_headPtr = headPtr;
    
    //has any data been received?
    if( curr_headPtr == tailPtr )
        return;

    do
    {
        switch( stateSPI_RxData )
        {
            case    START_AND_TX:
                if(rxBuffer[tailPtr] == 0 )
                {
                    SPDR = txBuffer[txPtr++];
                    txPtr &= BUFF_MASK; 
                     
                }//if
                else
                {
                    SPDR = rxBuffer[tailPtr] | ACK;
                    numBytes = rxBuffer[tailPtr];
                    dataIdx = 0;
                    stateSPI_RxData = RX_DATA;
                    
                }//else

            break;

            case    RX_DATA:
                Data[dataIdx++] = rxBuffer[tailPtr];
                if( --numBytes == 0 )
                    stateSPI_RxData = NEW_DATA_AVAILABLE;
                
            break;

            case    NEW_DATA_AVAILABLE:
                //do something with data
                //.
                //.
                //.
                stateSPI_RxData = START_AND_TX;
                
            break;
            
        }//switch

        tailPtr++;
        tailPtr &= BUFF_MASK;
        
    }while( tailPtr != curr_headPtr );

}//ReceiveData

ISR (SPI_STC_vect)
{
    rxBuffer[headPtr++] = SPDR;
    headPtr &= BUFF_MASK;
    
}//ISR

Thank you Blackfin. some useful stuff in your code you propose.

As SPI sends and receives at the same time, your code only does half the job unfortunately. with it, I can receive but not send.

but I'll take your implementation of the circular buffer as it looks certainly efficient! :slight_smile:

sherzaad:
Thank you Blackfin. some useful stuff in your code you propose.

As SPI sends and receives at the same time, your code only does have the job unfortunately. with it, I can receive but not send.

but I'll take your implementation of the circular buffer as it looks certainly efficient! :slight_smile:

Ah, right. This technique was well suited for UART comms where RX and TX were temporally separated :slight_smile: