Hi,
Apologies if my question is in the wrong category as I'm new here, and couldnt find similar questions after searching for 15 minutes or so.
I'm trying to communicate between 2 arduino mega2560 boards using SPI, master board need to transmit 3~10 variables ( uint8 for now, but will do float later) every 500ms to the slave board, I borrowed a piece of example code online and it works very well if I only transmit these variables once, however if I do it repeatedly every 500ms, occasionally the slave terminal will display the "incorrect" numbers, which is different from what I put in the master, not sure if it's error in the SPI transmission or something else, hope to get your help on that and thank you very much
master code:
// https://arduino.stackexchange.com/questions/16348/how-do-you-use-spi-on-an-arduino
#include <SPI.h>
unsigned long tt=0;
int cnt;
uint8_t roll_x,pitch_x,yaw_x;
void setup (void)
{
Serial.begin (115200); // debugging
digitalWrite(SS, HIGH); // ensure SS stays high for now
SPI.begin ();
// Slow down the master a bit
SPI.setClockDivider(SPI_CLOCK_DIV8);
tt=0;
cnt=0;
} // end of setup
void loop (void)
{
tt=millis();
cnt=cnt+1;
char c;
pitch_x=1+cnt;
roll_x=2+cnt;
yaw_x=3+cnt;
// enable Slave Select
digitalWrite(SS, LOW);
SPI.transfer(pitch_x);
delayMicroseconds(100);
SPI.transfer(roll_x);
delayMicroseconds(100);
SPI.transfer(yaw_x);
delayMicroseconds(100);
c='\n';
SPI.transfer(c);
// disable Slave Select
digitalWrite(SS, HIGH);
delay (500-(millis()-tt)); // 05 second delay
} // end of loop
slave code:
// https://arduino.stackexchange.com/questions/16348/how-do-you-use-spi-on-an-arduino
#include <SPI.h>
unsigned long tt=0;
int tcnt;
uint8_t i;
char buf [100];
volatile byte pos;
volatile bool process_it;
uint8_t pitch_x,roll_x,yaw_x;
void setup (void)
{
Serial.begin (115200); // debugging
// turn on SPI in slave mode
SPCR |= bit (SPE);
SPCR |= bit (SPIE);
// have to send on master in, *slave out*
pinMode (MISO, OUTPUT);
// get ready for an interrupt
pos = 0; // buffer empty
process_it = false;
tt=0;
tcnt=0;
} // end of setup
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR; // grab byte from SPI Data Register
if (pos < sizeof buf)
{
buf [pos++] = c;
// example: newline means time to process buffer
if (c == '\n') process_it = true;
}
} // end of interrupt routine SPI_STC_vect
// main loop - wait for flag set in interrupt routine
void loop (void)
{
if (process_it)
{
pitch_x=buf[0];
roll_x=buf[1];
yaw_x=buf[2];
Serial.print("transmitted pitch roll yaw are: ");
Serial.print(pitch_x);
Serial.print(" ");
Serial.print(roll_x);
Serial.print(" ");
Serial.println(yaw_x);
pos = 0;
process_it = false;
} // end of flag set
} // end of loop
slave output snapshots (error after 71 72 73 and after 79 80 81):
transmitted pitch roll yaw are: 65 66 67
transmitted pitch roll yaw are: 66 67 68
transmitted pitch roll yaw are: 67 68 69
transmitted pitch roll yaw are: 68 69 70
transmitted pitch roll yaw are: 69 70 71
transmitted pitch roll yaw are: 70 71 72
transmitted pitch roll yaw are: 71 72 73
transmitted pitch roll yaw are: 73 74 75
transmitted pitch roll yaw are: 74 75 76
transmitted pitch roll yaw are: 75 76 77
transmitted pitch roll yaw are: 76 77 78
transmitted pitch roll yaw are: 77 78 79
transmitted pitch roll yaw are: 78 79 80
transmitted pitch roll yaw are: 79 80 81
transmitted pitch roll yaw are: 5 21 32
transmitted pitch roll yaw are: 84 85 86
transmitted pitch roll yaw are: 85 86 87
transmitted pitch roll yaw are: 86 87 88
transmitted pitch roll yaw are: 87 88 89
transmitted pitch roll yaw are: 88 89 90
transmitted pitch roll yaw are: 89 90 91
To detect the source of errors, send constant values (For example: pitch = 45, roll = 68, yaw = 27) from Master to Slave; check that the Slave receives/display the parameters correctly at 1-sec interval.
Note that due to circulating nature of the SPI's Data Register, there is one-byte offset between transmission and reception (Fig-1).
@GolamMostafa thank you very much for the reply, do you mean on the master, the offset exist between the return value of SPI.transfer() and the byte actually sent?
Also I tried using constant value as you suggested, below are the output, still some error when msg are not received every 500ms
@johnwasser thanks for pointing these out and I did modify the master to send the constant value without 10 to avoid the issue with '\n' , also I turned on the time stamp, below are the output, looks like when the slave did not receive msg every 500ms as it supposed to, the error occurs. Wonder what could have caused that
salve Serial output:
12:51:16.617 -> transmitted pitch roll yaw are: 45 68 27
12:51:17.098 -> transmitted pitch roll yaw are: 45 68 27
12:51:17.616 -> transmitted pitch roll yaw are: 45 68 27
12:51:18.586 -> transmitted pitch roll yaw are: 45 16 108
12:51:20.108 -> transmitted pitch roll yaw are: 45 68 97
12:51:21.101 -> transmitted pitch roll yaw are: 45 68 97
12:51:21.612 -> transmitted pitch roll yaw are: 45 68 27
12:51:23.614 -> transmitted pitch roll yaw are: 45 16 108
12:51:24.094 -> transmitted pitch roll yaw are: 45 68 27
12:51:24.609 -> transmitted pitch roll yaw are: 45 68 27
12:51:25.096 -> transmitted pitch roll yaw are: 45 68 27
12:51:25.609 -> transmitted pitch roll yaw are: 45 68 27
I was using fairly standard 16in arduino wires with dupont connections ( since the board has these), I just tried with 8in wires, it seems to have solved the problem, wonder if it's just simply faulty wires/flimsy connections as I dont see these two length can make that big of a difference.
3. The above are fairly constant value; needs investigation.
4. Can you please try to test the following sketches with your original setup (wires/distance) and check the responses? Data transfer rate : 1 MBits/sec (your's were 2 MBits/se). You may change my sketches to run at 2 MBits/sec.
Master sketch collects 4-byte data from Slave.
#include<SPI.h>
byte myData[] = {0x00, 0x00, 0x00, 0x00};
void setup()
{
Serial.begin(9600);
SPI.begin();
delay(100);
SPI.setClockDivider(SPI_CLOCK_DIV16);//1 MBits/s
//pinMode(SS, OUTPUT);
digitalWrite(SS, LOW); //Slave is selected
//--------------------
}
void loop()
{
for (int i = 0; i < 4; i++)
{
myData[i] = SPI.transfer(myData[i]);
//delayMicroseconds(100); //allows Slave to process byte
Serial.println(myData[i], HEX); //shows: 0xAB
}
long x = (long)myData[1] << 24 | (long)myData[2] << 16 | (long)myData[3] << 8 | (long)myData[0];
Serial.println(x, HEX); //shows: 0x123456AB
Serial.println("======================");
delay(1000); //test interval
}
Slave sketch sends 4-byte data to Master in response to query.
#include<SPI.h>
int i = 0;
byte myData[] = {0x12, 0x34, 0x56, 0xAB};
void setup()
{
Serial.begin(9600);
SPI.setClockDivider(SPI_CLOCK_DIV16);//
pinMode(SS, INPUT_PULLUP); // ensure SS stays high for now
pinMode(MISO, OUTPUT);
SPCR |= _BV(SPE);
SPCR |= !(_BV(MSTR)); //Arduino is Slave
SPI.attachInterrupt(); //interrupt logic is enabled
}
void loop()
{
}
ISR(SPI_STC_vect)
{
SPDR = myData[i]; //places 0x12, then 0x34, then 0x56, then 0xAB
i++;
if (i == 4) //4-byte data are sent
{
i = 0; //array pointer is reset
}
}
I would suggest to revise your sketch of Post-1 in the light of the logic of the sketch of Post-7 and check that the Slave makes consistent response within allowed operating distance/bit rate.
I think your way is better and more stable, since the one I borrowed still doesnt work with the re plugged 16in wires, though I can get around with 8in wires it seems. I need to think about how to use your logic down the road when I need to talk to multiple slaves from single/multiple masters.
Thank you
Here is an example of exchanging 2-byte data between Master and Slave at 1-sec interval. You may follow the logic of these sketches to tailor your original sketches and then check with long wires.
Master Codes:
#include<SPI.h>
byte myData[] = {0x12, 0x34};//,
byte recData[2];
void setup()
{
Serial.begin(9600);
SPI.begin();
delay(100);
SPI.setClockDivider(SPI_CLOCK_DIV16);//1 MBits/s
//pinMode(SS, OUTPUT);
digitalWrite(SS, LOW); //Slave is selected
//--------------------
}
void loop()
{
for (int i = 0; i < 2; i++)
{
recData[i] = SPI.transfer(myData[i]); //sends:
//delayMicroseconds(100); //allows Slave to process received byte
Serial.println(recData[i], HEX); //shows: 0xAB
}
int x = (int)recData[1] << 8 | (int)recData[0];
Serial.println(x, HEX); //shows: 0x5678
Serial.println("======================");
delay(1000); //test interval
}
Slave Codes:
#include<SPI.h>
byte txData[] = {0x56, 0x78};
byte rxData[2];
volatile int i = 0;
volatile bool flag = false;
void setup()
{
Serial.begin(9600);
bitClear(SPCR, MSTR);
bitSet(SPCR, SPE); //must be
pinMode(SS, INPUT_PULLUP);
pinMode(MOSI, INPUT);
pinMode(MISO, OUTPUT);
pinMode(SCK, INPUT);
SPI.attachInterrupt();
}
void loop()
{
if (flag == true)
{
Serial.println(rxData[0], HEX);
Serial.println(rxData[1], HEX);
flag = false;
Serial.println("==================");
}
}
ISR(SPI_STC_vect)
{
if (flag == false)//this flag syncheonizes data with Master
{
rxData[i] = SPDR;
SPDR = txData[i];
i++;
if (i == 2)
{
flag = true;
i = 0;
}
}
}
thanks for the update, though it doesn't seem to work with the longer wires in my case.
I suspect the "interrupt" is causing trouble while the slave code is in the "process_it" stage, and am looking into some sort of "handshake" scheme such that when the slave is doing "process_it", the master will wait til the slave is done "process_it", then send out the bytes. Hope that will solve the problem.