0

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 Nov 20 '16 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 ovf – Julian Blanco Nov 20 '16 at 20:38
  • Why "float tiempo"? Why not "unsigned long"? – Mikael Patel Nov 20 '16 at 21:12
  • Good catch, fixed – Julian Blanco Nov 20 '16 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 Nov 21 '16 at 9:26
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.