PDA

View Full Version : Interrupt handling



Resilient
05-23-2009, 10:54 PM
I am trying to get rid of a little but in my wheel encoder code dealing with interrupts.

I have an int that keeps track of the number of encoder bars that have passed that is updated by an interrupt.

Then I have a function that does stuff with that int. A problem arises when, at the exact instant I am doing stuff with that int the interrupt occurs.

So I was planning on doing something like


foo()
disable encoder interrupt
doStuff()
enable encoder interruptbut then if the interrupt occurs while the interrupt is disabled, it will simply get ignored right? Is there a way to still catch the interrupt but simply delay processing it till after doStuff() is done?

lnxfergy
05-23-2009, 11:08 PM
I would do the following:

foo(){
disable_interrupt;
x = counter_value;
enable_interrupt;
do stuff with x.....

}

Then you really have the interrupt disabled for what, 3-5 clock cycles? That's almost nothing.. If foo() is called at a regular interval, and the counting values are fairly high every time foo is called, then its pretty much insignificant if you miss a pulse.


Is there a way to still catch the interrupt but simply delay processing it till after doStuff() is done?

<EDIT: SEE BELOW>

-Fergs

Resilient
05-24-2009, 01:42 AM
Thanks for the information. Its kind of what I had guessed. I was just sort of curious if there were a better way from a theoretical standpoint.

And yeah, it's AVR.

Thanks!

Adrenalynn
05-24-2009, 02:27 AM
Not all AVR are created equal. Some have many more hardware interrupts than others, allowing for a more functionally parallel execution path.

lnxfergy
05-24-2009, 09:40 AM
Not all AVR are created equal. Some have many more hardware interrupts than others, allowing for a more functionally parallel execution path.

Regardless of how many interrupts are possible on the chip... I believe that all 8-bit AVR architectures disable GIMSK when they jump into an ISR, and reenable it on the way out of the ISR.

And now as I think about it more, I realize I mispoke above... During the execution of an ISR, if another condition occurs that would normally trigger an interrupt, the flag will still be set, however because GIMSK is disabled, the interrupt will not occur then. That means as you exit the ISR, and GIMSK is re-enabled, the interrupt will occur. HOWEVER -- if the interrupt should have been triggered twice or more, it will only occur once, at the end of the other ISR... so you may still miss some counts.

Also, as an ISR exits, it clears the flag for the interrupt it just executed, so if you have an interrupt counting pulses, and it takes too long, it won't see a second pulse during execution of the ISR (since although the flag will be set again, it will be cleared on exit from the ISR).

-Fergs

ScuD
05-24-2009, 12:21 PM
I don't know how the interrupt handling of an AVR works specifically, but with PIC and 8052 cores I do know that an instruction in-progress will be completed before jumping to the ISR, if the interrupt happened right after the instruction was fetched.

Personally I always use - as Fergs suggests above - a 'shadow variable' that is loaded once with the value of the register that's changed by the ISR (eg. X = counter_value as per Fergs). Since that instruction will still be executed, you'll know it can be used for anything else.
It gets tricky if you're using a core without a single-instruction move though. But then, given a correct ISR save/restore, that shouldn't be an issue.

In most cases though there's a slight hardware latency for a given interrupt so the core might actually process another two or three instructions if you're running the thing fast enough.

Resilient
05-24-2009, 01:19 PM
I was reading through the mega640 data sheet and it looks like there are two kinds of interrupts, some that throw a flag and others that dont.

I am a little confused as to which is which. It sounds to me like if it is set up to be edge triggered, then it will set a flag when it detects an edge even if it is disabled. Whereas if it is level triggered, then it will only throw an interrupt if it is enabled and the level is held low for at least 5 clock cycles.

So it sounds like what I want is an edge triggered interrupt so that I can disable it, read the value, enable it and then it can handle the interrupt if the flag was set while reading the value. Can anyone confirm or deny this?

I read it here: http://www.atmel.com/dyn/resources/prod_documents/doc2549.pdf

Pages: top of 18 and 112

robologist
05-24-2009, 03:25 PM
What if you were to add a chip to take care of encoder counts on its own, and read from when needed ?
http://usdigital.com/products/interfaces/ics/lfls7166/
http://usdigital.com/products/interfaces/ics/lfls7266r1/

ScuD
05-24-2009, 04:03 PM
What I think is usually completely different from what I post, especially when not in my native tongue, but with my previous post I kinda sorta left out the part 'meaning you do not need to disable the interrupt if you use a shadow variable, if the instruction 'move regA to regB' is a single-clock instruction.
However I'm by no means an AVR expert so can anyone back this up?

Robologist's solution is a very fine one indeed though!

lnxfergy
05-24-2009, 06:30 PM
What I think is usually completely different from what I post, especially when not in my native tongue, but with my previous post I kinda sorta left out the part 'meaning you do not need to disable the interrupt if you use a shadow variable, if the instruction 'move regA to regB' is a single-clock instruction.
However I'm by no means an AVR expert so can anyone back this up?


The issue here is that on the AVR architecture (8-bit), an integer in AVR-GCC defaults to 16-bits... there are only 3 "virtual 16-bit" registers (X,Y,Z), which are typically used for memory addressing. Those are the only registers where you could put a 16-bit value and read in 1 clock cycle. I'm not entirely sure of the internals of AVR-GCC, but I really don't think it can optimize code in this way, and I would think it would be tough to write an assembly program that does such, since X,Y,Z are fairly well used for their specia 16-bit properties already.

Regardless, if he is reading a 16-bit timer count register, there is no instruction that can read both the H & L bytes in a single instruction. So you really do want to stop ints, copy, and re-enable interrupts.

-Fergs

lnxfergy
05-25-2009, 12:43 AM
I was reading through the mega640 data sheet and it looks like there are two kinds of interrupts, some that throw a flag and others that dont.

I am a little confused as to which is which. It sounds to me like if it is set up to be edge triggered, then it will set a flag when it detects an edge even if it is disabled. Whereas if it is level triggered, then it will only throw an interrupt if it is enabled and the level is held low for at least 5 clock cycles.

So it sounds like what I want is an edge triggered interrupt so that I can disable it, read the value, enable it and then it can handle the interrupt if the flag was set while reading the value. Can anyone confirm or deny this?

I read it here: http://www.atmel.com/dyn/resources/prod_documents/doc2549.pdf

Pages: top of 18 and 112

A little background in the AVR architecture can help explain this some I think. Back in the original days of the AVR, all interrupts acted in the following way: if a condition occured AND the particular interrupt was enabled a flag was set. If interrupts were enabled (the I-bit in the SREG set to 1) then the ISR would be called. When an ISR was entered, the I-bit was disabled, when RETI was called at the end of the ISR, the I-bit was enabled again and the particular flag was cleared. Under this setup, even if an event occurs while handling an interrupt, the flag will be set (note, the issue of a particular event happening twice and the second occurance being missed, may still occur, as you can't set a one-bit flag to represent the number of times an event occured).

But there was a problem here. Most mid-size (28 and 40 pin) AVRs had several interrupts related to each counter or serial device, but they typically only had 2-3 external interrupts. Then along came the PIN CHANGE INTERRUPTS. These interrupts are slightly limited compared to the external interrupts we had/have. They of course route all the pins on a particular port to a shared interrupt vector, you can only interrupt on pin-change (unlike external interrupts, which can be set to RISING, FALLING, or LOW level).

All of the above interrupts (external, pin-change, counter, serial) use flags. There are a couple warnings in there, that pin-change and rising/falling external interrupts are asynchronously generated, but that a low-level external interrupt is not (so you can't reliably detect a low-pulse shorter than the AVR clock-cycle, but you can detect the high-to-low transition...).

I'm really not sure where these "flagless" interrupts exist.... however, I think it is safe to say: you probably won't be using them right now.

If I am correct, you are using interrupts to read quadrature encoders? In such a case, I recommend using the INTx hardware external interrupts, with either a rising or falling configuration. Note that if you use the pin-change interrupts, you end up doubling the number of incoming pulses, but typically it makes the quadrature decoding more tedious, which can cause an ISR to become too long (especially if you have more than 1 channel on a particular pin-change vector active as an interrupt, and have to start reading pins and crud like that....)

-Fergs

Resilient
05-25-2009, 01:02 AM
Thanks Fergs,
I am using it for quadrature encoders and I will set it up with external hardware interrupts. But I will be doing some stuff with timers soon and know there are going to be some bugs to deal with there so I am trying to learn all I can before it gets too complex. :)

Thanks!