Strange behaviour with interrupts (i think) - Page 1
Forum section: electronics / general

author: Dan Moos
author_rank: Frequent Contributor
timestamp: March 04, 2017, 06:58:25 pm
content: Ok, I'm getting crashes that SEEM to be interrupt related. Without posting a pile of code, it is difficult to even know what to ask here, but I'll try. I'm willing to post everything, but that seems excessive.Ok, here's the hardware. ATMega1284P running at 8 Mhz. AVR Dragon using JTAGI'm building up the user interface for my project, and at the moment, it is all that is coded.I have one rotary encoder using INT0 and INT1.I have 7 push-buttons (including the one in the encoder) running into a 74HC165 shift register. The I/0 for the shift register is on Port D. I only mention the Port because INT0 and INT1 are also on Port D, in case by some wierd chance that is relevant to my trouble. The state of these buttons is retrieved by polling. On each iteration through the main() while loop, I run the code for the shift register, and return a byte with the button states.I have an HD44780 type LCD running off an I2C expander. The I2C code, and LCD code is all my own.In its current form, if working properly, the encoder should cycle through the alphabet and write the current character on the screenThe buttons should write the name of the button at a unique portion of the screen respective to each button. This is all just to test the functionality of the input system.Ok, I did the encoder code first. It runs flawlessly on its own.I did the push-button code next. It runs flawlessly with interrupts disabled.If I try and have everything enabled, I get one of two results (usually)If I don't touch the encoder, the buttons work fine. Until I turn the encoder. Then either the encoder works, but not the buttons, or everything locks up completely.If I tun the encoder first, again, either it works, or crashes, but the buttons don't work.So it seems that when the first interrupt is triggered, that's when things go awry.Every variable that the ISRs reference are volatile (I think. I'm open to having missed something at this point).Ok, here is maybe a clue. If I debug, with no breakpoints, literally every time I pause the session, it pauses in my I2C function who's sole purpose is to wait for the TWINT flag to clear. The only I2C device i'm using is the I/O expander for the LCD. Granted, since there is a screen update of some kind every while(1) iteration, and since most of my LCD functions will trigger multiple calls to this wait function, I'd expect my app to spend alot of time there.But it never pauses anywhere else. Ever. So I'm wondering if somehow the TWCR register is being corrupted when I use interrupts.Use of compiler optimization one way or the other seems to not matter.I'm completely stumped here.<edit>Forgot to mention. I tried disabling interrupts just when the Wait for TWINT function to is running. No dice.

author: WillHuang
author_rank: Contributor
timestamp: March 04, 2017, 11:52:58 pm
content: Are you updating the LCD at a very fast rate? I'm wondering if the executions to the LCD is going so fast that it can't keep up.Usually I have a FIFO buffer and timer to control writing to the lcd.Also I'm wondering if there might be a contention between your i2c devices on the bus

author: Dan Moos
author_rank: Frequent Contributor
timestamp: March 05, 2017, 12:49:38 am
content: Only the I/O expander for the LCD as far as I2C devices goI am updating the LCD constantly, I guess I could check that.Further use of debug has exposed another clue though.Like I said, the button routines work fine until I try and use the encoder. The button routines exist in the main() { while(1)}loopAs soon as I use the encoder, any breakpoint I place in the while(1) loop is never reached. Its like I get locked into the ISR routines somehow. Lets say I turn the encoder one click. In my code, that's 4 distinct events from the encoder, or 2 triggers each of INT0 and INT1. After that, I presume it should return to wherever things were when the first ISR was called. Yet, even if I just turn the encoder one click, it never seems to come back to the main() while loop. The encoder code keeps working, so the screen keeps updating the results of encoder action.Another strange thing that may suggest your theory about the LCD update rate causing weird things. I set a breakpoint at the start of one of the ISRs. I turned the encoder, and execution stopped there, as it should. Before resuming execution, I placed a breakpoint at the top of my while loop. This time, I got normal behavior. So as long as I didn't let the thing run free, things worked. But as soon as I disabled the breakpoints, the problem came back.Could there be some sort of contention bewtween the ingternall I2C interrupts, and my external ones?

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: March 05, 2017, 01:37:57 am
content: QuoteI do recall something on the NXP I2C module, which I subsequently found was extremely similar to the AvR one ( like all the state values are identical) .ISR the docs assume you're using interrupts and don't make it too clear how you're supposed to drive it from foreground tasks.When in foreground mode, not under interrupts, I think there is a situation where the time taken by an interrupt happening in the wrong place can cause it to hang waiting for something. It's not an issue when interrupt-driven as the interrupt gets serviced quickly enough for it not to be a problem.I have a feeling it was to do with the repeated startbit used when reading, and a condition where the state machine could transition through more then one state, at a rate related to the I2C clock, which is fairly slow compared to the CPU, and if the code polls for the first state, but gets interrupted just before it starts polling, by the time the interrupt returns, the hardware has transitioned to the second state so it never sees the first.It's many years ago, but I found this code for the NXP for that disables interrupts during part of the start generation, which may give a clue.It may be that I was using it "wrong" way by looking at the status reg values without referencing the interrupt flag state first.char iistart(char adr) // returns true if no ack{__disable_interrupt(); // atomic update in case of intI2C0CONSET=0x20; // startcondI2C0CONCLR=8; // clear previous int__enable_interrupt();while((I2C0CONSET & 0x08)==0);I2C0DAT=adr;I2C0CONCLR=0x28; // clear start+intwhile((I2C0CONSET & 0x08)==0); // ii address donereturn(I2C0STAT!=0x18);}

author: hamster_nz
author_rank: Super Contributor
timestamp: March 05, 2017, 04:45:03 am
content: Are you only doing minimal work in the interrupt code? For example, is the ISR for the encoder also calling the code to refresh the display?Perhaps try to keep ISRs very short - e.g. update the encoder value, then set a 'refresh display required' flag and return.Otherwise things quickly get tricky with nested interrupts and the associated mangement of interrupt masking.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: March 05, 2017, 12:58:36 pm
content: Quote from: hamster_nz on March 05, 2017, 04:45:03 amthat would be a REALLY BAD idea.

author: WillHuang
author_rank: Contributor
timestamp: March 06, 2017, 01:38:25 am
content: Quote from: mikeselectricstuff on March 05, 2017, 01:37:57 amI would recommend implementing this and see if it works.

author: Psi
author_rank: Super Contributor
timestamp: March 06, 2017, 05:11:39 am
content: Double check any references to GPIO, make sure you're not setting/clearing a pullup on an input that is enabled for interrupts. eg, If code in the handler triggers another interrupt of the same type it can get stuck looping.This could also be a problem if you have floating inputs that are active for interrupts that you're not aware of. Another IO changing may trigger an interrupt on a floating input.I think the AVR clears all flags in hardware, but double check on this.If you need to clear the flag manually and are not doing so then it will also keep looping.I think you write 1 to the flag to clear it on AVR.

author: KL27x
author_rank: Super Contributor
timestamp: March 06, 2017, 12:09:59 pm
content: Perhaps a simple debugging step is to replace your current button main code with a test code that just toggles a pin. Run that with your encoder ISR enabled and see what happens? Vice versa, you can run button code, but omit your entire ISR except for the trigger, clearing the flags (if you even need to do that), and the return (maybe also just toggle a pin, once again)? Take nothing for granted. It sure seems like ISR is not clearing or not popping/returning. But I take it your LCD stuff is in the main code, and that is obviously working with the ISR enabled... So maybe it's not the ISR, but something in the button code which is conflicting... that volatile memory thing perhaps, or a million other potential things. But if you can replace your ENTIRE button code with something else, and it works, then you can perhaps add back chunks of your button code until you break the code, again. W/e it is will most likely be obvious in hindsight, of course.There is potentially some code where you have to disable interrupts, temporarily. There are many such things with PIC. But it is probably faster to figure out what breaks your code rather than poring through the datasheet.