sparkfun / SparkFun_BNO080_Arduino_Library

An Arduino Library for the BNO080 IMU combination triple axis accelerometer/gyro/magnetometer packaged with an ARM Cortex M0+ running powerful algorithms.
Other
81 stars 62 forks source link

Issue with SPI data updates #42

Open DaudiGabriel opened 4 years ago

DaudiGabriel commented 4 years ago

Subject of the issue

If the main loop evaluation time in an Arduino sketch is slightly larger than the update rate of the sensor, the Sensor will not report any updates via SPI any more. Not sure, whether this is a library problem or whether there is some internal (inside the BNO080) buffer overflow, which stops the sensor from working properly.

Your workbench

Steps to reproduce

Slightly modified version of SPI example "Example1-RotationVector"

//These pins can be any GPIO byte imuCSPin = 10; byte imuWAKPin = 14; byte imuINTPin = 16; byte imuRSTPin = 15;

unsigned long startTime; //Used for calc'ing Hz long measurements = 0; //Used for calc'ing Hz

void setup() { Serial.begin(115200); Serial.println(); Serial.println("BNO080 SPI Read Example");

if(myIMU.beginSPI(imuCSPin, imuWAKPin, imuINTPin, imuRSTPin) == false) { Serial.println("BNO080 over SPI not detected. Are you sure you have all 6 connections? Freezing..."); while(1); }

myIMU.enableRotationVector(10); //Send data update every 10ms

startTime = millis(); }

void loop() { Serial.println("Doing other things"); delay(9); //You can do many other things. We spend most of our time printing and delaying.

//Look for reports from the IMU if (myIMU.dataAvailable() == true) { measurements++;

Serial.print((float)measurements / ((millis() - startTime) / 1000.0), 2);
Serial.print(F("Hz"));

Serial.println();

} }


- Sample time of 10 ms, delay of 11 ms  in main loop --> measurements will stop after a few updates
```#include <SPI.h>
#include "SparkFun_BNO080_Arduino_Library.h"
BNO080 myIMU;

//These pins can be any GPIO
byte imuCSPin = 10;
byte imuWAKPin = 14;
byte imuINTPin = 16;
byte imuRSTPin = 15;

unsigned long startTime; //Used for calc'ing Hz
long measurements = 0;   //Used for calc'ing Hz

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println("BNO080 SPI Read Example");

  if(myIMU.beginSPI(imuCSPin, imuWAKPin, imuINTPin, imuRSTPin) == false)
  {
    Serial.println("BNO080 over SPI not detected. Are you sure you have all 6 connections? Freezing...");
    while(1);
  }

  myIMU.enableRotationVector(10); //Send data update every 10ms

  startTime = millis();
}

void loop()
{
  Serial.println("Doing other things");
  delay(9); //You can do many other things. We spend most of our time printing and delaying.

  //Look for reports from the IMU
  if (myIMU.dataAvailable() == true)
  {
    measurements++;

    Serial.print((float)measurements / ((millis() - startTime) / 1000.0), 2);
    Serial.print(F("Hz"));

    Serial.println();
  }
}

Expected behavior

Measurements over SPI should still be available

Actual behavior

Can not read any data from sensor

PaulZC commented 4 years ago

Hi @DaudiGabriel , Just an update to let you know that we are investigating this issue. I can replicate your issue using a SparkFun RedBoard (ATmega328P) and the following code:

#include <SPI.h>

#include "SparkFun_BNO080_Arduino_Library.h"
BNO080 myIMU;

//These pins can be any GPIO
byte imuCSPin = 10;
byte imuWAKPin = 9;
byte imuINTPin = 8;
byte imuRSTPin = 7;

unsigned long startTime; //Used for calc'ing Hz
long measurements = 0; //Used for calc'ing Hz

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println("BNO080 SPI Read Example");

  myIMU.enableDebugging(Serial); //Pipe debug messages to Serial port

  if(myIMU.beginSPI(imuCSPin, imuWAKPin, imuINTPin, imuRSTPin) == false)
  {
    Serial.println("BNO080 over SPI not detected. Are you sure you have all 6 connections? Freezing...");
    while(1);
  }

  //You can also call begin with SPI clock speed and SPI port hardware
  //myIMU.beginSPI(imuCSPin, imuWAKPin, imuINTPin, imuRSTPin, 1000000);
  //myIMU.beginSPI(imuCSPin, imuWAKPin, imuINTPin, imuRSTPin, 1000000, SPI1);

  //The IMU is now connected over SPI
  //Please see the other examples for library functions that you can call

  myIMU.enableRotationVector(10); //Send data update every 10ms

  Serial.println(F("Rotation vector enabled"));
  Serial.println(F("Output in form i, j, k, real, accuracy"));

  startTime = millis();
}

void loop()
{
  Serial.println("Doing other things");
  delay(6); //You can do many other things. We spend most of our time printing and delaying.

  //Look for reports from the IMU
  if (myIMU.dataAvailable() == true)
  {
    float quatI = myIMU.getQuatI();
    float quatJ = myIMU.getQuatJ();
    float quatK = myIMU.getQuatK();
    float quatReal = myIMU.getQuatReal();
    float quatRadianAccuracy = myIMU.getQuatRadianAccuracy();
    measurements++;

    Serial.print(quatI, 2);
    Serial.print(F(","));
    Serial.print(quatJ, 2);
    Serial.print(F(","));
    Serial.print(quatK, 2);
    Serial.print(F(","));
    Serial.print(quatReal, 2);
    Serial.print(F(","));
    Serial.print(quatRadianAccuracy, 2);
    Serial.print(F(","));
    Serial.print((float)measurements / ((millis() - startTime) / 1000.0), 2);
    Serial.print(F("Hz"));

    Serial.println();
  }

}

If I decrease the delay inside the loop to 5ms, the code works as expected. With the delay set to 6ms, the updates freeze after 1 or 2 seconds. I will keep investigating! Best wishes, Paul

PaulZC commented 4 years ago

I’m starting to understand this. If we set the enableRotationVector interval to 40ms or more, the BNO080 does not seem to care if we take more than 40ms to acknowledge the interrupt and read the data. If we set the interval to less than that, then it seems we MUST respond to the INT within one interval otherwise the BNO080 freezes. More investigation tomorrow...

FidencioYzaguirreIII commented 4 years ago

The datasheet for the BNO080, in section 1.3.4.2, mentions that it may be necessary to wake the chip when communicating with it using SPI.

https://cdn.sparkfun.com/assets/1/3/4/5/9/BNO080_Datasheet_v1.3.pdf

I suspect that during the case that the loop is slower then the update rate that the chip puts itself to sleep. I have yet to discover a way to flat out disable this sleep method from the very beginning.

I do have some code though that I believe should wake the chip

For the .h file void wake(); // Wake sensor up

For the .cpp file

void BNO080::wake() {

    digitalWrite(_wake, LOW);   // Begin  Wake operation

    if (_i2cPort == NULL) {
        bool chipSelect = false;

        for (int counter = 0; counter < 300; counter++)
        {
            if (digitalRead(_int) == LOW) {
                digitalWrite(_cs, LOW);
                chipSelect = true;
            }

            if (chipSelect && (digitalRead(_int) == HIGH)) {
                break;
            }

            //if (_printDebug == true)
            //    _debugPort->println(F("SPI Wait"));
        }

        if (_printDebug == true && !chipSelect)
            _debugPort->println(F("SPI INT timeout on wake"));
    }
    digitalWrite(_cs, HIGH);
    digitalWrite(_wake, HIGH); 
}

I attempted to use implement the above code with a smart waker system in my Arduino Sketch. However I have seen limited success. There is either an issue with the code above or my smart waker.

Also note to the original poster. I'm the Factorem guy who reported a similar issue in the Sparkfun forums. I thought I was done with the issue back then but looks like I have ran into it again. Just happened to be doing some searching on the web when I ran into your post on the pjrc forums. Noticed your post linked back to my original post on the issue and I felt compelled to give any assistance I could.

FidencioYzaguirreIII commented 4 years ago

I spent today testing and can't seem to wake the chip back up. There is probably an issue with the wake code I provided. Will attempt to look into this again tomorrow.

PaulZC commented 4 years ago

Hi @FidencioYzaguirreIII , Thank you for your comments - they are very useful. In the following plot: the purple trace is the SPI CS; the yellow trace is the INT signal. At the center of the trace, the BNO080 pulls INT low briefly but then it goes high again - returning to a voltage which is not quite as high as normal. The high state will cause dataAvailable to return false. My assumption was that a buffer was filling up, causing the BNO080 to go into some kind of error state. But sleep is a good explanation too. Except I cannot explain why the chip would go to sleep so soon after an SPI transfer? 12

FidencioYzaguirreIII commented 4 years ago

I've attempted contacting CEVA, they now own Hillcrest Labs, to see if there is a way to disable the sleep function. If I get a response I will be sure to post about it. Also thank you for the oscilloscope data.

PaulZC commented 4 years ago

Thank you @FidencioYzaguirreIII & @DaudiGabriel , We have contacted Hillcrest Labs too. I will come back to you if they are able to offer any more advice. At the moment, our advice has to be: if you set the the Rotation Vector Report Interval to 10ms, you need to make sure that you are able to read it at that rate. Delaying the read on every second report seems to be acceptable, but delaying the read on every report does cause the SPI interface to lock up. We don't believe the BNO080 is going to sleep, an internal buffer filling up seems more likely. Best wishes, Paul

FidencioYzaguirreIII commented 4 years ago

Found out the datasheet I was looking at was out of date.

Current Version https://www.ceva-dsp.com/wp-content/uploads/2019/10/BNO080_085-Datasheet.pdf

Turns out that there is a watchdog timer on the BNO080 that resets the sensor.

The BNO085 on the other hand will timeout and retry the operation. (Hint to Sparkfun make a BNO085 module) The BNO085 is suppose to be backwards compatible with the BNO080. The changes are for new software libraries.

DaudiGabriel commented 4 years ago

Thanks all for the effort so far. At least I know now that I was not to stupid to connect the sensor the right way and that it is a sensor problem!

I am curious whether this problem also occurs when using I2C with the interrupt pin, but unfortunately I have no time to test this right now...

FidencioYzaguirreIII commented 4 years ago

@DaudiGabriel I believe the issue will not occur on I2C from what I gather from the datasheet. Only issue will be dealing with the a speed bottleneck since SPI is faster then I2C.

PaulZC commented 4 years ago

We are now in contact with CEVA about this. More updates to follow soon!

PaulZC commented 4 years ago

We now understand more about what is happening to the chip. Being slow to read the SPI data does appear to be filling an internal buffer and causes the chip to perform a reset (“Initialize (unsolicited)”). At the moment, our advice is still: if you set the the Rotation Vector Report Interval to 10ms, you need to make sure that you are able to read it at that rate.

PaulZC commented 4 years ago

Hi @DaudiGabriel & @FidencioYzaguirreIII , CEVA have come back with more information about this issue. It is normal behavior for the BNO080 and is documented in section 1.2.4.1 of the datasheet: image

printPacket reveals this:

Header: 17 00 03 10 Body: FB 2B FF FF FF 05 10 01 00 7E 03 B5 04 48 DC C8 34 81 10 Length:23 Channel:Sensor-report
Header: 17 00 03 11 Body: FB 15 00 00 00 05 11 01 00 7F 03 B5 04 47 DC C7 34 83 10 Length:23 Channel:Sensor-report
Header: 17 00 03 12 Body: FB 17 00 00 00 05 12 01 00 7F 03 B5 04 48 DC C7 34 83 10 Length:23 Channel:Sensor-report
Header: 17 00 03 13 Body: FB D0 FF FF FF 05 13 01 00 7F 03 B6 04 47 DC C7 34 84 10 Length:23 Channel:Sensor-report
Header: 17 00 03 14 Body: FB 80 FF FF FF 05 14 01 00 80 03 B6 04 47 DC C7 34 86 10 Length:23 Channel:Sensor-report
Header: 17 00 03 15 Body: FB 7C FF FF FF 05 15 01 00 80 03 B6 04 48 DC C7 34 87 10 Length:23 Channel:Sensor-report
Header: 14 01 00 00 Body: 00 01 04 00 00 00 00 80 06 31 2E 30 2E 30 00 02 Length:276 Channel:Command
Header: 14 00 02 00 Body: F1 00 84 00 00 00 01 00 00 00 00 00 00 00 00 00 Length:20 Channel:Control
Header: 05 00 01 00 Body: 01 Length:5 Channel:Executable

The penultimate Control Channel packet (length 20 (4 + 16)) is a command response (0xF1) for command 0x84 which is “Initialize (unsolicited)”. The ultimate Executable Channel packet (length 5 (4 + 1)) is a “reset complete” (0x01).

This issue is now resolved - but I am going to leave it open and mark it as sticky to help future users with the same issue. Thank you again for your help on this. Best wishes, Paul