Two arduinos spi without delay()

Hello, im trying to figure out SPI communication with two arduinos - mega and nano.
Trying to transfer values between them like so - mega sends commands (like 'a', 'b', 'c', 'd', etc), and nano responds to that command with pot values (i have 4 pots). But problem is that there is a delay() in example code (Nick Gammon master/slave code), which we all know is not good for "multitasking" timed events. I understand that it needs to have delay() to grab data from slave (nano) How can it be done with millis()/micros() or mby there is different route?

byte transferAndWait (const byte what){
  byte a = SPI.transfer (what);
  delayMicroseconds (20);
  return a;
}

Please post a complete sketch that illustrates the problem that you are trying to solve

Whilst the use of delays can cause problems is a delay of 20 microseconds really a problem in your project ? I assume that you realise how short a period of time that really is

If Nick Gammon has said so who we are to disagree.

byte transferAndWait (const byte what){
  byte a = SPI.transfer (what);
  delayMicroseconds (20); //somehow needs to be replaced with micros() function
  return a;
}

  transferAndWait ('a');  // comand to get pot 1 value | value is from 0-255.
  transferAndWait (10); // send some data to get potval1 back?
  byte potVal1 = transferAndWait (0); // get value into variable and send dummy data
//already 60 micros delay in the loop

And yes, it is a problem in my project, because i need send few tens of commands, and it adds up in loop, for example, i send 10 transferAndWait(); to slave, its already 200 micros and it messes up timing.

has you tried to remove this delay()?

Yes, than it will send(master), but wont get data from slave.

Are you posting the complete sketch in instalments to keep us in suspense ?

Full sketch is too long to post here. But here is a master(mega) part.

#include <SPI.h>

unsigned long lastMillis;
bool doOnce = false;
byte i = 0;
byte response;

void setup (void) {
  Serial.begin (115200);
  Serial.println ();
  digitalWrite(SS, HIGH);
  SPI.begin ();
  SPI.setClockDivider(SPI_CLOCK_DIV64);
}

void loop () {
  Serial.println(sendSPI('a'));
}

byte sendSPI (byte value) {
  digitalWrite(SS, LOW);
  byte cmdArray[] = {value, 10, 0};
  if (millis() - lastMillis > 500 && !doOnce) {
    lastMillis = millis();
    response = SPI.transfer(cmdArray[i]);
    doOnce = true;
    if(i == sizeof(cmdArray) - 1){
      i = 0;
    }
    else{
      i++;
    }
  }
  else {
    doOnce = false;
  }
  digitalWrite(SS, HIGH);
  return response;
}

it shuffles thru byte cmdArray[] and send byte every 500 millis, but i get response from slave - 0 ;

Then post an MRE. It should be short. It should compile. It should demonstrate the problem at hand (and only that problem).

That's the only way people can test and verify that there actually is a problem and propose tested solutions.

Above your comment is Master code, Here is the slave(nano) code:

volatile byte command = 0;

void setup (void){
  Serial.begin(115200);
  pinMode(MISO, OUTPUT);
  // turn on SPI in slave mode
  SPCR |= _BV(SPE);
  // turn on interrupts
  SPCR |= _BV(SPIE);
}

ISR (SPI_STC_vect){
  byte c = SPDR;
  Serial.println(SPDR);
  switch (command){
  // no command? then this is the command
  case 0:
    command = c;
    SPDR = 0;
    break;
  case 'a':
    SPDR = 115;  // send value back to master
    break;
}

void loop (void){
  // if SPI not active, clear current command
  if (digitalRead (SS) == HIGH)
    command = 0;
}

No worries, there is no need for full sketch, its (few pages/ files long). I`m just trying to get these sketches to work. Problem is with implementing millis()/micros() function to control spi transfer/recieve timing

byte sendSPI (byte value) {
  digitalWrite(SS, LOW);
  byte cmdArray[] = {value, 10, 0};
  if (millis() - lastMillis > 500 && !doOnce) {
    lastMillis = millis();
    response = SPI.transfer(cmdArray[i]);
    doOnce = true;
    if(i == sizeof(cmdArray) - 1){
      i = 0;
    }
    else{
      i++;
    }
  }
  else {
    doOnce = false;
  }
  digitalWrite(SS, HIGH);
  return response;
}

i dont understend. when millis() - lastMillis > 500 is not true then doOnce = false; ?

It is declared false in global variables, than when millis() function executes code inside, it sets it true, so code inside runs one time every 500 millis, than it sets it back to false, so it can run continuously

The assignment happens on this line, this is not a reference or a pointer, a gets the value on this line, not sure what the delay is for

I think delay needs to be because slave is not yet put value in SPDR register. If it waits some time, than SPDR is populated with value, and can be sent to master, if there is no delay time, it just sets a variable (and prints out) to value which master is sending to slave.

But yeah, how can it be, that value is not put after 2 of 3 times sending command?

If I remember correctly there is a while loop in transfer method that waits so when the function returns it should be all good...dunno

If you ask me the delay is there to give the slave time to place the answer in the SPDR register.

This is a clue how to remove it. There are multiple levels of optimization each comes with its own compromise/drawback. Nothing is free in life.

  1. Just remove the delay and do something else for that amount of time, before you send the next command to your slave.

  2. Make sure your slave is as fast as possible e.g., remove the Serial.println from the ISR. The faster your slave the shorter you can make the delay and will still answer in the next cycle. If your slave needs to do something else e.g., compute the answer make sure it has a good processor and high enough clock speed.

  3. Write your own SPI library. That will give you the least amount of wasted time, but you will need to handle the fact that the SPI transfer takes time. You can get all the code you need from the Arduino SPI library.
    https://github.com/arduino/ArduinoCore-avr/blob/master/libraries/SPI/src/SPI.h
    So, after each write to SPDR you can do something else until you can read the value from the current transfer and then you need to give the slave time to place the answer into its SPDR register and do another transfer.

@killzone_kid You remember correctly.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.