Go Down

Topic: mpu6050 interrupt handling (Read 361 times) previous topic - next topic

garfius

I am trying to manage 2 interrupt handled devices from an arduino Nano, one of these is an MPU6050.

The good example: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/MPU6050/examples/MPU6050_DMP6/MPU6050_DMP6.ino

Works well, but the i2c reading is done outside the interrupt handling function. So, if the whole program takes more than 10ms to check data again i get a "FIFO overflow!"... and happens often.

So, i lose data, sure. And i guess i get wrong readings. :smiley-cry:

Obviously, i have tried to read everything on the interrupt handling function... but it crashes HARD.

"mpu.getIntStatus()" inside the interrupt function leads to arduino freeze, unrecoverable. :smiley-eek-blue:

Any idea? :D


GolamMostafa

Please, post your complete sketch covering both interrupts.

garfius

2nd interrupt is not needed to archieve full crash.

Just reading datawithin attached interrupt function is enough, it goes as follows:

This code works:
Code: [Select]
#include <Arduino.h>  //works
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"

MPU6050 mpu;
#define loopDelay 10
bool dmpReady = false;
uint8_t mpuIntStatus;
uint8_t devStatus;
uint16_t packetSize;
uint16_t fifoCount;
uint8_t fifoBuffer[64];
unsigned long time;

Quaternion q;
VectorFloat gravity;
float euler[3];
float ypr[3];

volatile bool mpuInterrupt = false;

void dmpDataReady() {
mpuInterrupt = true;
}

void giroRefresh() {
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!, giro descompensat!!"));
Serial.print("&");
// otherwise, check for DMP data ready interrupt (this should happen frequently)
}
else if (mpuIntStatus & 0x02) {
Serial.print("%");
// wait for correct available data length, should be a VERY short wait
time = millis();
while ((fifoCount < packetSize) && ((millis() - time) < 2)) {
Serial.println("|" + (String)((millis() - time) < 2));
Serial.println((String)(fifoCount < packetSize));
fifoCount = mpu.getFIFOCount();
}
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);

// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;

// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);

}
mpuInterrupt = false;
}

void giroPrint() {
Serial.print("ypr\t");
Serial.print(ypr[0] * 180 / M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180 / M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180 / M_PI);
}
void giroinit() {
Wire.begin();
TWBR = 24;
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();

Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();

mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip

if (devStatus == 0) {
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();

Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;

packetSize = mpu.dmpGetFIFOPacketSize();
}
else {
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
}

void setup() {

Serial.begin(115200);

giroinit();

}

void loop() {
//delay(loopDelay);

if (mpuInterrupt) {
giroRefresh();
giroPrint();
}

}


This code doesn't:
Code: [Select]
#include <Arduino.h>//crashes
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"

MPU6050 mpu;
#define loopDelay 10
bool dmpReady = false;
uint8_t mpuIntStatus;
uint8_t devStatus;
uint16_t packetSize;
uint16_t fifoCount;
uint8_t fifoBuffer[64];
unsigned long time;
// orientation/motion vars
Quaternion q;
VectorFloat gravity;
float euler[3];
float ypr[3];

void dmpDataReady() {
giroRefresh();
}

void giroRefresh() {
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!, giro descompensat!!"));
Serial.print("&");
// otherwise, check for DMP data ready interrupt (this should happen frequently)
}
else if (mpuIntStatus & 0x02) {
Serial.print("%");
// wait for correct available data length, should be a VERY short wait
time = millis();
while ((fifoCount < packetSize) && ((millis() - time) < 2)) {
Serial.println("|" + (String)((millis() - time) < 2));
Serial.println((String)(fifoCount < packetSize));
fifoCount = mpu.getFIFOCount();
}
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);

// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;

mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
}
}

void giroPrint() {
Serial.print("ypr\t");
Serial.print(ypr[0] * 180 / M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180 / M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180 / M_PI);
}
void giroinit() {
Wire.begin();
TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();

// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();

// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788);

if (devStatus == 0) {
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);

Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();

Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;

packetSize = mpu.dmpGetFIFOPacketSize();
}
else {
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
}
void setup() {

Serial.begin(115200);

giroinit();

}
void loop() {
//delay(loopDelay);

/*if (mpuInterrupt) {
}*/
giroPrint();
}



As you see , i just changed the place where data is being read.

Also, i know this is very inefficient code, and interrupt functions must be kept small, but it's for demonstration pruposes.

Thanks.

jremington

#3
Dec 02, 2019, 07:21 pm Last Edit: Dec 02, 2019, 07:23 pm by jremington
It doesn't work because the interrupt routine is too large and slow, and you attempt to print from within an interrupt. Never print within an interrupt.

gilshultz

I would restructure your code so that the interrupt routines only set flags or at worse stuff incoming data into memory.  "The AVR hardware clears the global interrupt flag in SREG before entering an interrupt vector. Thus, normally interrupts will remain disabled inside the handler until the handler exits, where the RETI instruction (that is emitted by the compiler as part of the normal function epilogue for an interrupt handler) will eventually re-enable further interrupts. For that reason, interrupt handlers normally do not nest. For most interrupt handlers, this is the desired behaviour, for some it is even required in order to prevent infinitely recursive interrupts (like UART interrupts, or level-triggered external interrupts). In rare circumstances though it might be desired to re-enable the global interrupt flag as early as possible in the interrupt handler, in order to not defer any other interrupt more than absolutely needed. This could be done using an sei() instruction right at the beginning of the interrupt handler, but this still leaves few instructions inside the compiler-generated function prologue to run with global interrupts disabled."  This answer was found on: https://stackoverflow.com/questions/5111393/do-interrupts-interrupt-other-interrupts-on-arduino
This response is to help you get started in solving your problem, not solve it for you.
Good Luck & Have Fun!
Gil

garfius

oK.

I will try using volatile variables inside loop to prevent registry changes, and recoding all reusing most flags as possible.

Thanks, great answer.


jremington

#6
Dec 02, 2019, 11:53 pm Last Edit: Dec 02, 2019, 11:53 pm by jremington
The interrupt routine is exactly as it should be, as posted in the "working code".

Code: [Select]
volatile bool mpuInterrupt = false;
void dmpDataReady() {
mpuInterrupt = true;
}

Go Up