Compass.read() inside an ISR

Hi everyone!

Lately I have been trying wirting a code for an arduino controlled motor car.

During this program I trigger an interrupt every 1sec. in this interrupt I try to read the azimuth from QMC5883L digital compass (I2C).

#include  <Arduino.h>
#include  <Wire.h>
#include  <QMC5883LCompass.h>


// #ifdef    F_CPU
// #undef    F_CPU
// #define   F_CPU   16000000UL
// #endif

// Define motor control pins
#define   RIGHT_FORWARD   2
#define   RIGHT_BACKWARD  3
#define   LEFT_FORWARD    6
#define   LEFT_BACKWARD   7

// Define motor speeds
#define   FS    0x8F    // Full Speed
#define   CS    0x55    // Correct Speed
#define   LS    0x50    // Low Speed
#define   STOP  0x00

bool  ledState = 0;
byte  state = 0;

QMC5883LCompass compass;

int  A;
void setup() {

  Serial.begin(9600);
  Wire.begin();

  pinMode(13, OUTPUT);
  digitalWrite(13, 0);

  pinMode(RIGHT_FORWARD, OUTPUT);
  pinMode(RIGHT_BACKWARD, OUTPUT);
  pinMode(LEFT_FORWARD, OUTPUT);
  pinMode(LEFT_BACKWARD, OUTPUT);

  compass.init();
  compass.setCalibration(-1666, 32, -353, 1371, 0, 1112);
  compass.read();
  A = compass.getAzimuth();
  Serial.println(A);
  // Setting right motor PWM
  // Drive D2 & D3 HIGH when TCNT3 < OCR3x while up-counting
  // Drive D2 & D3 LOW when TCNT3 > OCR3x while up-counting
  //>>>>>>>> DATASHEETS PG. 154
  // Set outputs to work in Fast PWM mode
  // >>>>>>>> DATASHEETS PG. 145 <<<<<<<<
  // Set timers to count without prescalar
  // >>>>>>>> DATASHEETS PG. 156 <<<<<<<<
   {
  TCCR3A = 0b00101001;    // Pin toggling mode (HIGH to LOW)
  TCCR3B |= 0b00001001;   // Prescalar and Fast PWM
  TCCR3B &= 0b11111001;
  TIMSK3 |= 0b00001100;   // Enable On-Compare-Interrupt B&C
  OCR3B = 0x00;           // RIGHT_FORWARD speed (MAX is 0xFF)
  OCR3C = 0x00;           // RIGHT_BACKWARD speed (MAX is 0xFF)
  }

  // Setting right motor PWM
  //Drive D6 & D7 HIGH when TCNT4 < OCR4x while up-counting
  // Drive D6 & D7 LOW when TCNT4 > OCRx while up-counting
  //>>>>>>>> DATASHEETS PG. 154
  // Set outputs to work in Fast PWM mode
  // >>>>>>>> DATASHEETS PG. 145 <<<<<<<<
  // Set timers to count without prescalar
  // >>>>>>>> DATASHEETS PG. 156 <<<<<<<<
  {
  TCCR4A = 0b10100001;    // Pin toggling mode (HIGH to LOW)
  TCCR4B |= 0b00001001;   // Prescalar and Fast PWM
  TCCR4B &= 0b11111001;
  TIMSK4 |= 0b00000110;   // Enable On-Compare-Interrupt B&C
  OCR4A = 0x00;           // LEFT_FORWARD speed (MAX is 0xFF)
  OCR4B = 0x00;           // LEFT_BACKWARD speed (MAX is 0xFF)
}

  // LED blink interrupt
  {
  TCCR1A = 0;
  TCCR1B = 0b00000100;
  TCNT1 = 0;
  TIMSK1 = 0x02;
  OCR1A = 65000;
  }

  sei();

}

void loop() {

}

ISR(TIMER1_COMPA_vect){

  TCNT1 = 0;
  
  // compass.read();
  // int a = compass.getAzimuth();
  // Serial.println(a);
  
  digitalWrite(13, ledState);
  ledState = !ledState;

}

ISR(TIMER3_COMPB_vect){}  // RIGHT_FORWARD

ISR(TIMER3_COMPC_vect){}  // RIGHT_BACKWARD

ISR(TIMER4_COMPA_vect){}  // LEFT_FORWARD

ISR(TIMER4_COMPB_vect){}  // LEFT_BACKWARD

Inside the interrupt the LED is blinking for a visual indication. The problem begins when I add the lines under comment inside the ISR;

  compass.read();
  int a = compass.getAzimuth();
  Serial.println(a);

For some reason the LED stops blinking, which means for me that the interrupt does not occur. Does anyone have an idea why it appens? does the I2C uses one of my timers??

I will really appreciate your help guys!

Thank in advance,

Bnaya.

Hi @bnaya255

Unfortunately, it's not possible call the I2C Wire library from within an ISR, since it uses an interrupt service routine itself.

A workaround is to get the ISR to set a flag variable then get the main loop() function to test it and read the compass if an interrupt has occured.

1 Like

Why are you using an interrupt for that, at all?

Because he found a shiny new knife and had to play with it. Interrupts are complicated and will cause problems for the unwary.

1 Like

@bnaya255 you posted in the section that said not for your project with your project!

Moved it here.
Please read the forum headings before posting in future.

You may want to read this before you proceed:-
how to get the best out of this forum

By now, the previously offered comments will have made you aware of some "best practices" using Arduino interrupts. You can educate yourself on the correct usage here
https://circuitdigest.com/microcontroller-projects/arduino-interrupt-tutorial-with-examples

Yes, of course!

Everyone who has read and understood the basic background material understands that within an interrupt routine, interrupts are turned off and not functional.

Therefore, an interrupt service routine normally cannot call a function that requires interrupts to be functioning.

Incidentally, the tutorial linked above in post #6 is wrong and IS NOT RECOMMENDED.

That tutorial suggests that it is OK to print from within an interrupt. IT IS NOT OK. Attempting to do so on most processors will hang or crash the system.

1 Like

Thank you so much! Your explaination make sense for me and now i understand.

But don't iterrupts have hierarchy? I mean, one iterrupt has higher priority than another one? I'm asking just for understandig. Not for the specific issue.

Yes, there is usually an interrupt hierarchy, but it depends on the processor. The processor data sheet has the details.

Just have the timer interrupt routine increase a counter every 1s. Then have the main code in loop() check the counter and read from the compass each time the counter increases. Or have the interrupt routine run every 1ms and increase a counter. Have loop() check the counter and every time it increases by 1000, read the compass.

Wait a second... there already is an interrupt every 1ms that increases a counter... You could just use that! :wink:

2 Likes

Not on the AVR used in Uno, Mega, etc. Interrupts there are first-come first-served.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.