MPU 6050 update function in interrupt

Hello!

Code summery:

#include <Wire.h>
#include <MPU6050_light.h>

MPU6050 mpu(Wire);

void setup(void) {

  Serial.begin(9600);
  Wire.begin();
  byte status = mpu.begin();

  Serial.print(F("MPU6050 status: "));
  Serial.println(status);
  while(status!=0){ } // stop everything if could not connect to MPU6050

 //Timer configuration. 16 MHz clock
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1B |= (1<<CS11);
  TCCR1B |= (1<<CS10); //Prescaler 64
  TIMSK1 |= (1<<TOIE1); //Enable timer overflow
 }

ISR(TIMER1_OVF_vect){
  counter = counter + 1;
  Serial.println(counter);
}

void loop(){
  mpu.update();

*
*
CODE
*
*
}

This way works! In the Serial Monitor it prints out the value of counter just like expected!

In the main loop I use the Gyro value and get nice results. But for a reason would need to have the mpu.update() function call more often, i.e in the interrupt. This way I will always have more up to date Gyro values.

So in code below I move mpu.update() to interrupt function

ISR(TIMER1_OVF_vect){
  counter = counter + 1;
  Serial.println(counter);

  Serial.println("Before update");
  mpu.update();
  Serial.println("After update");
}

void loop(){
*
*
CODE
*
*
}

Now to the problem: When I do this, the program gets stuck. It will print out the "Be", (first 2 letters of the print before mpu.update();
But then it gets stuck and nothing more gets printed.

Does anyone have an idea why this is?

Remember that the interrupt logic is disabled before the control goes to ISR. The Serial.print() method is not supposed to work in ISR as the said method requires enabled interrupt logic for its working. So, move all the print() commands to the loop() function and things should be alright.

But won't the mpu.update() rely on I2C, which requires interrupts too?

It sure does seem like that. Even after removing the print statements in the interrupt and only having mpu.update() it does not work.
Is there any way to be able to call the mpu.update() function in the interrupt?

Or maybe, is there another way I can get more frequent updates without writing mpu.update() every other line?

Are you sure about that?

1 Like

Your interrupt happens 3.8 times a second. If your loop() is faster than that you would get a higher sample rate by calling mpu.update() in loop(). If you loop() runs slower than that, calling mpu.update() in the ISR (if that was possible) would fetch data that would never be processed.

Either way, calling mpu.update() in loop() is the right way to do it.

The following simple experiment shows that the MCU disables the Global Interrupt Enable Logic before arriving at the ISR and yet the string "Hello" is being printed nicely at 1-sec interval in the ISR which indicates that the interrupt logic has been re-enabled.

However, this facility has been (probably) added for debugging purposes.
Test Sketch:

void setup()
{
  Serial.begin(9600);
  TCCR1A = 0x00;
  TCCR1B = 0x00;
  TCNT1 = 49911;  //1-sec time delay
  TCCR1B = 0x05;
  bitSet(TIMSK1, TOIE1);
}

void loop()
{

}

ISR(TIMER1_OVF_vect)
{
  Serial.println(bitRead(SREG, 7));
  Serial.println("Hello!");
}

Output:

void setup()
{
  Serial.begin(9600);
  TCCR1A = 0x00;
  TCCR1B = 0x00;
  TCNT1 = 49911;  //1-sec time delay
  TCCR1B = 0x05;
  bitSet(TIMSK1, TOIE1);
}

void loop()
{

}

ISR(TIMER1_OVF_vect)
{
  Serial.println(bitRead(SREG, 7));  //printing I-bit (Global Interrupt Enable Bit) of SREG
  Serial.println("Hello!");
}

Output:

0
Hello!
0
Hello!

@dendanne
You may play around with the following sketch taken from the Examples of the IDE. The sketch works fine with my MPU6050 and try to print the angles in the ISR; the result is same as yours meaning prints only two characters.

So, execute the update() method in loop() function, change the time parameter to suit your update rates.
Sketch:

#include "Wire.h"
#include <MPU6050_light.h>

MPU6050 mpu(Wire);
unsigned long timer = 0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  
  byte status = mpu.begin();
  Serial.print(F("MPU6050 status: "));
  Serial.println(status);
  while(status!=0){ } // stop everything if could not connect to MPU6050
  
  Serial.println(F("Calculating offsets, do not move MPU6050"));
  delay(1000);
  // mpu.upsideDownMounting = true; // uncomment this line if the MPU6050 is mounted upside-down
  mpu.calcOffsets(); // gyro and accelero
  Serial.println("Done!\n");
}

void loop() {
  mpu.update();
  
  if((millis()-timer)>10){ // print data every 10ms
	Serial.print("X : ");
	Serial.print(mpu.getAngleX());
	Serial.print("\tY : ");
	Serial.print(mpu.getAngleY());
	Serial.print("\tZ : ");
	Serial.println(mpu.getAngleZ());
	timer = millis();  
  }
}

Output:

MPU6050 status: 0
Calculating offsets, do not move MPU6050
Done!

X : 37.38	Y : 51.99	Z : -2.64
X : 34.48	Y : 47.95	Z : -2.66
X : 31.80	Y : 44.22	Z : -2.67
X : 29.32	Y : 40.82	Z : -2.69

Interrupt Version:

#include "Wire.h"
#include <MPU6050_light.h>

MPU6050 mpu(Wire);
unsigned long timer = 0;

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  byte status = mpu.begin();
  Serial.print(F("MPU6050 status: "));
  Serial.println(status);
  while (status != 0) { } // stop everything if could not connect to MPU6050

  Serial.println(F("Calculating offsets, do not move MPU6050"));
  delay(1000);
  // mpu.upsideDownMounting = true; // uncomment this line if the MPU6050 is mounted upside-down
  mpu.calcOffsets(); // gyro and accelero
  Serial.println("Done!\n");
  //-------------------------
  TCCR1A = 0x00;
  TCCR1B = 0x00;
  TCNT1 = 49911;  //1-sec time delay
  TCCR1B = 0x05;   //division factor 1024
  bitSet(TIMSK1, TOIE1);

}

void loop()
{
 /* mpu.update();

  if ((millis() - timer) > 10) { // print data every 1-sec
    Serial.print("X : ");
    Serial.print(mpu.getAngleX());
    Serial.print("\tY : ");
    Serial.print(mpu.getAngleY());
    Serial.print("\tZ : ");
    Serial.println(mpu.getAngleZ());
    timer = millis();
  }*/
}

ISR(TIMER1_OVF_vect)
{
  mpu.update();
  Serial.print("X : ");
  Serial.print(mpu.getAngleX());
  Serial.print("\tY : ");
  Serial.print(mpu.getAngleY());
  Serial.print("\tZ : ");
  Serial.println(mpu.getAngleZ());
}

Output:

DoMPU6050 status: 0
Calculating offsets, do not move MPU6050
Do

Your experiment needs a redesign.

Redesign to achieve what -- please, give a hint?

We already know that a serial print can work in interrupt context, the contentious part is what happens when the output buffer is filled in interrupt context

Maybe something more along these lines.

  asm("cli");
  for (size_t i = 0; i < 20; i++) {
    Serial.println("This should fill the buffer up nicely.");
  }
  Serial.println("And we are still here.");
  Serial.flush();
  asm("sei");

Depends on which Arduino core you are using. The AVR core switches to polling when the buffer is full and interrupts are disabled.

  // If the output buffer is full, there's nothing for it other than to 
  // wait for the interrupt handler to empty it a bit
  while (i == _tx_buffer_tail) {
    if (bit_is_clear(SREG, SREG_I)) {
      // Interrupts are disabled, so we'll have to poll the data
      // register empty flag ourselves. If it is set, pretend an
      // interrupt has happened and call the handler to free up
      // space for us.
      if(bit_is_set(*_ucsra, UDRE0))
	_tx_udr_empty_irq();
    } else {
      // nop, the interrupt handler will free up space for us
    }
  }

Thanks for your responses!
Here is the situation:
I mpu.update() in a main loop so I can get gyro values. No problemos!

This works fine as long as I do not have any delay() in the main loop to.
But I have delays! And what happens then is that the gyro says the correct angle, but after the delay the first values it says is totally random (anywhere from -150 to 150 degrees). This I think is because it takes a couple of mpu.update() in a row for the angle to stabilize.

The angle values being say -50 when it is actually say 0 degrees really messes with my code and gives me head-scratching bugs.

So what I was thinking therefore was to have the mpu.update() in a timer interrupt so that it is always updated, even though there is a delay() going on. But this clearly did not work.

Does anyone have an idea what I could try to get a continuous update on the mcu. (note that the delay(3000) I use does not have to be exactly 3000, I don't care if it becomes 2500 or 3500 if that helps with the solution).

Call me simple, but it looks to me like delays are your problem.
If only there were some well-documented way to eliminated them.

Well yes that was what I said and I am kind of new to arduino and this microcontroller stuff, maybe you could explain the alternative Captain Genius.

Have you looked at any of the IDE's examples qualified with the phrase "without delay"?

Captain Genius explains

Oi!
I am Captain Genius.
And so is my wife.

Solution was using millis(), beautiful Captains!