I have hit a snag in my project regarding long interrupt routines crashing when the program includes I2C communications. I understand that it's bad practice to have long interrupts in the first place and I might have to do things differently anyway, but the whole issue interests me so I thought I'd ask about it.
My project is basically a crude autopilot for a RC parkflyer. It includes an Arduino nano, a GPS, the usual gyroscope/accelerometer, a barometer, differential barometer and so on. I had all of these in my sketch and working easily fast enough (about 30 loops per second) using mostly Adafruit libraries, but the GPS parsing function takes long enough for there to be a noticeable pause, which isn't really good.
So, instead of breaking up the GPS parsing into small state machine steps as I just might have to do anyway, I got the bright idea of reading the gyroscope/accelerometer and update the control servos accordingly in a timer interrupt, with all the other less time-critical stuff like the GPS running along in the "backround". Turns out, it works magnificently and smoothly until the sketch crashes at a random point in time. It only happens if the running loop uses I2C, so I reasoned that if the long interrupt fires during communications, it will not only garble the data (which sometimes happens) but crash the sketch as well.
I got the idea to fix this by manually disabling the timer interrupt just before I2C communication starts and enabling it right afterwards, testing it using only the I2C-connected barometer without which the sketch doesn't crash. Although the program's stability increased a lot leading me to believe I'm on the right track, it didn't completely fix the issue so here I am now.
This is basically what I'm doing now, turning off timer interrupts on timer 1 which triggers my periodic "control" loop whenever I2C is used, modified part of library is attached...
TIMSK1 |= (0 << OCIE1A);
Wire.beginTransmission(MPL3115A2_ADDRESS); // start transmission to device
Wire.write(a); // sends register address to read from
Wire.endTransmission(false); // end transmission
Wire.requestFrom((uint8_t)MPL3115A2_ADDRESS, (uint8_t)1);// send data n-bytes read
byte info = Wire.read();
TIMSK1 |= (1 << OCIE1A);
This is the offending interrupt itself, the whole code is also attached.
ISR(TIMER1_COMPA_vect){
if(controlOn){
//if(interruptDiv >= 10){
sei();
event = BnoUpdate();
cli();
BnoCursorHandle();
if(digitalRead(11)== LOW){
DrawGPSData(GPS);
}
else{
DrawOrientationData(event);
}
SetContrast();
if (toggle2){
digitalWrite(boardLed,HIGH);
toggle2 = 0;
}
else{
digitalWrite(boardLed,LOW);
toggle2 = 1;
}
}
}
I realize this sort of solution might be a dead end, but I am curious what causes these interrupt-related crashes. The test program has a lot of stuff on it, but in this cut-down state it's completely stable without the barometer, and crashes with it so the long interrupt by itself works fine.
Adafruit_MPL3115A2.cpp (5.44 KB)
displaytest.ino (11.5 KB)