Can't get SPI "protocol checker" to work(Arduino to Arduino)

Hi, first post, sorry for any rookie mistakes...

Well, I'm trying to make this project where I'm supposed to make sure that the communication between two devices is working well using SPI. For that, I thought I should first make sure it works for Arduino2Arduino communication but it turns out I'm having a hard time with getting the code to actually work. I've tried putting some Serial.print in the code and I observed it always goes till the .transfer() function and doesn't move out of there. I hope some of you may be able to help me.

Again, clarifying, I basically need to send a message from a Master Arduino to a slave and make sure the message I got back is the same I sent in the first place(In the future I'll probably turn a led on or something like that, but first I need to get SPI to work).

Here's the Master code:

#include <SPI.h>
#define SS 10

void setup(){
  Serial.begin(115200);
  pinMode(SS, OUTPUT);
  digitalWrite(SS, HIGH); // makes sure slave is not selected
  SPI.begin(); // initializes spi as master
  //SPI.setClockDivider(SPI_CLOCK_DIV8); 
}

void loop(){
  byte varenviada, varrecebida;
  Serial.println("Inicio");
  digitalWrite(SS, LOW);
  
  for(int jj = 0; jj<255; jj++){
    Serial.println("Dentro");
  	varenviada = jj;
    varrecebida = SPI.transfer(jj);
    Serial.print("Enviado: ");
    Serial.print(varenviada);
    Serial.print("\t Recebido: ");
    Serial.print(varrecebida);
   	delay(200);
  }
  digitalWrite(SS, HIGH);
  Serial.print("Saiu");
  // delay(1000);  
}

And here's the slave code:

#include <SPI.h>

volatile boolean concluido; // saber quando recebeu todas as informacoes
volatile byte svarrecebida, svarenviada;
int jj = 0;

void setup(){
	Serial.begin(115200);
  	pinMode(MISO, OUTPUT); // seta o MISO como output
  	SPCR |= _BV(SPE); // spi no slave mode
  	concluido = false;
  	SPI.attachInterrupt(); // liga interrupts
}

// rotina de servico do interrupt do SPI
ISR (SPI_STC_vect){
	svarrecebida = SPDR;
  	concluido = true;
}

void loop (void)
{
  // so roda se uma transmissao completa acontecer
  if (concluido)
  {
    svarenviada = jj;
    SPDR = svarenviada;
    jj++;
    if(jj>254) jj = 0;
    concluido = false;
  } 
}

If you are learning the SPI communication for the first time, then start playing with a basic experiments (of this link-1 and of this link-2) based on the following diagram.

spi328x.png
Figure-1:

spi328x.png

I've tried putting some Serial.print in the code and I observed it always goes till the .transfer() function and doesn't move out of there.

Does that mean all serial output you got with above code is "Started"?

That's interesting because that code doesn't even compile.

So please test with the code you post (and post correct code again) and post also the output you got. Provide a wiring diagram of your setup!

On the slave side you read the content of the SPDR twice, once in the ISR and again in the loop. That probably doesn't work as expected.

pylon:
Does that mean all serial output you got with above code is "Started"?

That's interesting because that code doesn't even compile.

So please test with the code you post (and post correct code again) and post also the output you got. Provide a wiring diagram of your setup!

On the slave side you read the content of the SPDR twice, once in the ISR and again in the loop. That probably doesn't work as expected.

Sorry about the code not compiling, I tried translating some of the stuff I had on it(it was in portuguese) and probably forgot something, but now I put the updated code, that runs but does not work, you can try it now.

I'll try changing the fact the I'm reading SPDR twice, that's probably not working well indeed, thanks for the indication. I'll try it.

GolamMostafa:
If you are learning the SPI communication for the first time, then start playing with a basic experiments (of this link-1 and of this link-2) based on the following diagram.

spi328x.png

Awesome! I'll study the links you sent and get some practice with the SPI.

Update:

I made some changes to the code, got some information from the codes one of you sent above(thanks for that) and made some changes to my code. I was able to get it to work!

I didn't mention it before but I was testing my code and the circuit only within Thinkercad, I started to suspect that maybe something was going wrong with the platform and made the same set up but in the real word and it worked!

Here's the updated code:

Master:

#include <SPI.h>
#define SS 10


void setup(){
  Serial.begin(9600);
  pinMode(SS, OUTPUT);
  digitalWrite(SS, HIGH); // makes sure slave is not selected
  SPI.begin(); // initializes spi as master
  SPI.setClockDivider(SPI_CLOCK_DIV8); 
}

void loop(){
  byte varenviada, varrecebida;
  digitalWrite(SS, LOW);
  // Serial.println("Inicio");
  varenviada = 'a';
  varrecebida = SPI.transfer(varenviada);
  Serial.print("Enviado: ");
  Serial.print(varenviada);
  Serial.print("\t Recebido: ");
  Serial.print(varrecebida);  
  Serial.println();
  digitalWrite(SS, HIGH);
  if(varenviada == varrecebida){
    Serial.println("Comunicacao concluida com sucesso");
  }else Serial.println("Comunicacao falhou");
    
  delay(1000); 
}

Slave:

#include <SPI.h>

volatile boolean concluido; // saber quando recebeu todas as informacoes
volatile byte svarrecebida, svarenviada;

void setup(){
    Serial.begin(9600);
    pinMode(MISO, OUTPUT); // seta o MISO como output
    SPCR |= _BV(SPE); // spi no slave mode
    // SPCR |= !(_BV(MSTR)); 
    // SPCR |= _BV(SPIE);
    concluido = false;
    SPI.setClockDivider(SPI_CLOCK_DIV8);
    SPI.attachInterrupt(); // liga interrupts
}

// rotina de servico do interrupt do SPI
ISR (SPI_STC_vect){
    svarrecebida = SPDR;
    Serial.print("Recebido: ");
    Serial.print(svarrecebida); 
    concluido = true;
}

void loop (void)
{
  // so roda se uma transmissao completa acontecer
  if (concluido)
  {
    svarenviada = svarrecebida;
    SPDR = svarenviada;
    Serial.print("\tEnviado: ");
    Serial.print(svarenviada);
    Serial.println();
    concluido = false;
  } 
}

Luiz6ustav0:

// rotina de servico do interrupt do SPI

ISR (SPI_STC_vect){
    svarrecebida = SPDR;
    Serial.print("Recebido: ");
    Serial.print(svarrecebida);
    concluido = true;
}

void loop (void)
{
  // so roda se uma transmissao completa acontecer
  if (concluido)
  {
    svarenviada = svarrecebida;
    SPDR = svarenviada;
    Serial.print("\tEnviado: ");
    Serial.print(svarenviada);
    Serial.println();
    concluido = false;
  }
}

Don't execute print() command in the ISR() as the interrupt is globally disabled; whereas, print() method is interrupt drive. Do something like these:

ISR (SPI_STC_vect)
{
    svarrecebida = SPDR;
  //  Serial.print("Recebido: ");
  //  Serial.print(svarrecebida); 
    concluido = true;
}

void loop (void)
{
  // so roda se uma transmissao completa acontecer
  if (concluido)
  {
    
    Serial.print("Recebido: ");
    Serial.print(svarrecebida); 
    svarenviada = svarrecebida;
    SPDR = svarenviada;
    Serial.print("\tEnviado: ");
    Serial.print(svarenviada);
    Serial.println();
    concluido = false;
  } 
}

GolamMostafa:
Don't execute print() command in the ISR() as the interrupt is globally disabled; whereas, print() method is interrupt drive.

Oh, I didn't know that, I'll make sure to update the code, thanks for the code. And about the interrupts, if I'm being honest, I should say that I don't really understand how it works, I wrote this code more as "that's how SPI communication works on arduino so I'll make it", do you have any references to understand what's the role of interrupts on the communication and how they work on arduino? Or maybe you can just give me a brief on interrupts and I'll make some research after that. Thanks for your time!

Make a reading of this file to get some idea on SPI Serial Communication and this file on interrupt structure of ATmega328P MCU.

1. Meaning of Interrupting the MCU
It means -- telling the MCU to:

(1) suspend what is has been doing now (the Main Line Program, MLP);

(2) disable/deactivate 'global interrupt enable' bit (so that the MCU is not interrupted until the current ISR is finished, see Step-1.3).

(3) turn to the side job (the Interrupt Sub Routine, ISR);

(4) finish the ISR;

(5) re-activate the 'global interrupt enable' bit;

(6) resume the MLP.

2. Sources of Interrupting Signals (Fig-1)
(1) 3 from external hardware devices;

(2) 1 from SPI logic when data is ready in the SPDR Register;
When the 8-bit data entry into the SPDR Register of SPI Port is done in synchronization with SCK (Serial Clock), the MCU is automatically interrupted (conditions (Fig-1 of Post#1): SPIF flag of SPSR Register must assume HIGH state, global interrupt enable bit must be active (located in SREG Register), and local SPI interrupt enable bit (SPIE) must also be active). In response, the MCU goes to the following ISR.

ISR(SPI_STC_vect)
{
    //insert codes as needed
}

(3) 22 from various internal sources (Fig-1).


Figure-1: Interrupt vector Table of ATmega328P MCU