I am working on creating sensor poller that is going to be polling multiple sensors and then, on request, provides its data to the master.
The master polls the slave (SPI) at 50hz and the slave is polling it's sensor (BNO055) (I2C) at 100hz.
The transaction works most of the time but every once in a while it returns an overflow.
Any ideas why this might occur?
Slave code:
/*************************************************************************
*
* objeeAirlines
* __________________
*
* Written by Julian Blanco
*
* Spi slave code written by
Nick Gammon
February 2011
*/
#include "Adafruit_BNO055.h"
float yawInput=0;
float yawOffset=0;
float pitchInput=0;
float rollInput=0;
int calibration=0;
volatile byte command = 0;
Adafruit_BNO055 bno = Adafruit_BNO055();
void setup (void)
{
if(!bno.begin())while(1){};
bno.setExtCrystalUse(true);
// have to send on master in, *slave out*
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// turn on interrupts
SPCR |= _BV(SPIE);
} // end of setup
// SPI interrupt routine
ISR (SPI_STC_vect)
{
union first_union{
float f;
byte b[4];}
data;
byte c = SPDR;
data.f = yawInput;
command = c;
switch (command)
{
// no command? then this is the command
case 0:
SPDR = 0;
break;
// incoming byte, return byte result
case 'a':
SPDR = data.b[0];
break;
// incoming byte, return byte result
case 'b':
SPDR = data.b[1];
break;
// incoming byte, return byte result
case 'c':
SPDR = data.b[2];
break;
// incoming byte, return byte result
case 'd':
SPDR = data.b[3];
break;
} // end of switch
} // end of interrupt service routine (ISR) SPI_STC_vect
float tiempo=millis();
void loop (void)
{
// if SPI not active, clear current command
if (digitalRead (SS) == HIGH) command = 0;
if(millis()>(tiempo+10))
{
tiempo=millis();
SampleGyro(bno);
}
} // end of loop
void SampleGyro(Adafruit_BNO055 &gyroIMU){
/* Display the floating point data */
imu::Vector<3> euler = gyroIMU.getVector(Adafruit_BNO055::VECTOR_EULER);
yawInput=euler.x()-yawOffset;
if (yawInput >= 360)
{
yawInput -= 360;
}
else
{if (yawInput < 0)yawInput += 360;
}
pitchInput=euler.y();
//rollInput=fmod((euler.z()+(360+90)), 360)-180;
rollInput=euler.z();
uint8_t system, gyro, accel, mag = 0;
gyroIMU.getCalibration(&system, &gyro, &accel, &mag);
calibration=system;
}
Master Code:
#include <SPI.h>
#define SS 10
void setup (void)
{
pinMode(MOSI, OUTPUT);
pinMode(MISO, INPUT);
pinMode(SCK, OUTPUT);
pinMode(SS, OUTPUT);
Serial.begin (115200);
Serial.println ();
digitalWrite(SS, HIGH);
SPI.begin ();
SPI.setClockDivider(SPI_CLOCK_DIV8);
}
byte transferAndWait (const byte what)
{
byte a = SPI.transfer (what);
delayMicroseconds (10);
return a;
}
union first_union{
float f;
byte b[4];}
data;
void loop (void)
{
digitalWrite(SS, LOW);
transferAndWait ('a');
data.b[0] = transferAndWait ('b');
data.b[1] = transferAndWait ('c');
data.b[2] = transferAndWait ('d');
data.b[3] = transferAndWait (0);
digitalWrite(SS, HIGH);
Serial.print("data.f = ");Serial.println(data.f);
//Serial.print("data.b[0] = ");Serial.println(data.b[0]);
//Serial.print("data.b[1] = ");Serial.println(data.b[1]);
//Serial.print("data.b[2] = ");Serial.println(data.b[2]);
//Serial.print("data.b[3] = ");Serial.println(data.b[3]);
delay(10);
}
-
Try reducing the SPI clock and/or increasing the micro seconds delay in transferAndWait (10 us is not very much).Mikael Patel– Mikael Patel11/20/2016 11:27:45Commented Nov 20, 2016 at 11:27
-
Thanks for responding, but no dice, halved the speed of the spi bus and doubled the delay, same results, 97%ish success with the occasional ovfJulian Blanco– Julian Blanco11/20/2016 20:38:08Commented Nov 20, 2016 at 20:38
-
Why "float tiempo"? Why not "unsigned long"?Mikael Patel– Mikael Patel11/20/2016 21:12:27Commented Nov 20, 2016 at 21:12
-
Good catch, fixedJulian Blanco– Julian Blanco11/20/2016 23:47:44Commented Nov 20, 2016 at 23:47
-
Might need to mention that the Arduino millis() ISR/Timer is not accurate. It is a bit slow and will actually to a double increment from time to time.Mikael Patel– Mikael Patel11/21/2016 09:26:04Commented Nov 21, 2016 at 9:26
1 Answer 1
It is likely that your SPI interrupt on the slave is firing in the middle of updating your global yawInput
variable. This would cause the value to be partially updated when you send it back to the master. You might even be sending part of the old value and part of the new value back to the master. It depends on when the interrupts fire.
You need to lock your SampleGyro
function somehow. The easiest way would be to disable interrupts when modifying any interrupt accessible variables (i.e. yawInput
).
Something like this should tell you if the interrupt timing is the issue:
void SampleGyro(Adafruit_BNO055 &gyroIMU){
/* Display the floating point data */
imu::Vector<3> euler = gyroIMU.getVector(Adafruit_BNO055::VECTOR_EULER);
uint8_t system, gyro, accel, mag = 0;
gyroIMU.getCalibration(&system, &gyro, &accel, &mag);
// Disable interrupts while modifying the global variable
noInterrupts()
yawInput=euler.x()-yawOffset;
if (yawInput >= 360) {
yawInput -= 360;
} else if (yawInput < 0) {
yawInput += 360;
}
pitchInput=euler.y();
//rollInput=fmod((euler.z()+(360+90)), 360)-180;
rollInput=euler.z();
calibration=system;
// re-enable interrupts once your global variable
// modification is done.
interrupts();
}
It's also worth noting that you want to put as little code between the noInterrupts()
and interrupts()
calls as possible. Things like I2C and SPI and basically any other communication will likely not work inside of that block. I rearranged your SampleGyro
function to put all calls to the gyro outside of the lock. This is to prevent the sensor reading from failing.
-
Getting closer but no dice, see photo Julian Blanco– Julian Blanco11/21/2016 20:23:58Commented Nov 21, 2016 at 20:23
Explore related questions
See similar questions with these tags.