Author Topic: AVR External Interrupt  (Read 2710 times)

0 Members and 1 Guest are viewing this topic.

Offline dan3460Topic starter

  • Frequent Contributor
  • **
  • Posts: 326
  • Country: us
AVR External Interrupt
« on: January 06, 2017, 02:29:26 am »
I have a very basic question, can I use the same button to call int0 from inside the ISR?
I want to use only one button (actually is the push function on a rotary encoder) to call a setup program inside the ISR, but once inside the ISR pressing that same button will call the ISR again.
Normally I answer this kind of questions doing experiments, but I'm sure one of you guys knows the answer.

Thanks
 

Offline Buriedcode

  • Super Contributor
  • ***
  • Posts: 1611
  • Country: gb
Re: AVR External Interrupt
« Reply #1 on: January 06, 2017, 02:49:35 am »
Assuming you're using the Atmega series, I believe each interrupt has its own program vector, and priority - with reset and INT0 having the highest priority.

I think the global interrupt enable flag is cleared when an interrupt is triggered, prevented further interrupts whilst in the ISR for that interrupt vector. However, once you're out of that, it will jump to the interrupt that occurred whilst servicing the previous one.

So, assuming you're in your ISR, whilst I don't recommend it, you could theoretically manually enabled the global interrupt enable flag when you enter the ISR (that was just cleared in hardware as you entered the ISR) allowing that interrupt to be interrupted by other, in your case INT0.

Quick test:
Create a timer interrupt with a delay loop inside - purely to waste time. If the timer fires every 1ms, and the delay is less than this, you could spend almost all your time in the ISR - awful practice for any real-world project, but this is just a test.  In that delay loop it checks a single variable, if its 0xFF. If it is, it lights an LED.
A second interrupt from INT0 simply sets that file register as 0xFF.

This way, if you really are going to another interrupt vector whilst in an ISR, the LED will light. If it only enters the interrupt *after* your primary ISR has finished, nothing will light.

If the ISR in your project is fired regularly enough, you could always just check the state of your input, or give it its own interrupt (INT0) but use this ISR to just set a flag that you can check in other ISR's.
 

Online cv007

  • Frequent Contributor
  • **
  • Posts: 825
Re: AVR External Interrupt
« Reply #2 on: January 06, 2017, 06:45:38 am »
Quote
call a setup program inside the ISR
Calling other functions inside an isr is a good sign you are probably doing too much inside the isr.

Quote
but once inside the ISR pressing that same button will call the ISR again
When an interrupt occurs, interrupts are disabled until enabled again by either the  'reti' instruction (generated by compiler, at end of isr), or you deciding to enable interrupts via sei() function (bad idea). (trivia- If I remember correctly, on the avr you will always get at least one instruction to run after interrupts are enabled before an interrupt will fire- so after an isr returns it will always run 1 instruction even if another isr was pending. /trivia)

The biggest reason not to enable interrupts within an isr is you can very easily get the stack to run into your data unless you have a very good handle on which interrupts are enabled and how they all could potentially interact. Another good reason is because there is no need to do anything like that.

I'm not quite sure what you are doing, but lets say you have a rotary encoder which fires INT0, here is probably a minimal isr which will take care of what you need (I haven't used avr in a long time, and this is just to get you possibly thinking differently than before)

volatile uint8_t int0_pinb_save;
ISR(PCINT0_vect)
{
    int0_pinb_save = PINB; //save state of encoder pins
    disable_this_int0_isr(); //no more int0 until main loop sees int0 data
}

//the_main_loop
...
if( int0_flag_is_set() )
{
    process_int0_encoder(); //using int0_pinb_save
    clear_int0_flag();
    enable_int0_isr(); //ready for next int0 event
}
...


the encoder could be processed in the isr (setting flags to notify of press or turn), I was just trying to show a minimal isr that could do the job.
 

Offline dan3460Topic starter

  • Frequent Contributor
  • **
  • Posts: 326
  • Country: us
Re: AVR External Interrupt
« Reply #3 on: January 06, 2017, 12:33:01 pm »
Thanks for the answers, but opens new questions. Maybe I should clear a little what I'm doing. I have a button attached to INT0, when pressed jumps into the ISR for INT0. In there it shows to the user a small menu where some parameter can be set. Pressing the same button again exits the ISR back to the program.
If I understood correctly, once in the ISR the interrupts are disabled, so pressing the button attached to INT0 will not trigger the interrupt again. Is that correct?

Also, would be possible to explain why ISRs should be kept as minimal as possible? Just want to learn, in this case the only external functions that I'm calling are LCD instructions to show the user what to do.

 

Online cv007

  • Frequent Contributor
  • **
  • Posts: 825
Re: AVR External Interrupt
« Reply #4 on: January 06, 2017, 02:36:02 pm »
Quote
I have a button attached to INT0, when pressed jumps into the ISR for INT0
Rephrase that to- I have a button attached to INT0, when pressed notifies may main loop that a button was pressed

Quote
Is that correct?
Yes. Interrupts are disabled by hardware when the avr decides to process an interrupt. Interrupt flags will still get set , though.

Quote
In there it shows to the user a small menu where some parameter can be set

When you are in the isr, all other isr's are blocked (isr's are now disabled), so when your user decides to change a setting, he could take 10 minutes to fiddle around with your menu, all the while nothing else happens- all other interrupts will not run. Maybe INT0 is your only interrupt for now, but lets say you also have an RX isr , or a timer isr keeping track of time- these are now all halted while stuck in your INT0 isr waiting for a user to complete your menu- no more RX characters received, no more updating of time, etc.

Here's what happens when one tries to get clever by re-enabling interrupts inside interrupts-
INT0 irq fires- about ~18 registers get pushed (saved) on the stack
you are in INT0
you enable irq's inside
button gets pressed- button probably bounces a few time (lookup button bounce)
INT0 fires again- another 18 registers pushed on the stack
second bounce- repeat- another 18
(stack is growing down and eventually will run into your data)

lets stop here, no need to go any farther- where am I in your menu system now?- did I even get into the menu with the second bounce, or did I exit the menu?

You could spend some time to analyze what is actually happening (the above is probably incorrect to some degree)- but you will end up concluding its  bad idea because you really cannot determine what is happening- add a few more isr's and it becomes even more difficult.

I'm sure others can explain better.
« Last Edit: January 06, 2017, 03:29:09 pm by cv007 »
 

Offline Buriedcode

  • Super Contributor
  • ***
  • Posts: 1611
  • Country: gb
Re: AVR External Interrupt
« Reply #5 on: January 06, 2017, 10:02:45 pm »
I'm sure others can explain better.

Not sure I could have  :-+

Generally the ISR is for capturing *important* (as in time-critical) events, be it a rising edge on an input that indicates a major fault, a rising edge for timing something, received serial data which is soon to be overwritten by more incoming data etc.. It should do its job, and go back to the main as quickly as possibly as to not block other events - as stated by cv007. For things that dont' happen very often, or quickly, like user button presses, regular polling can work but then how often the software checks is down to what the software is currently doing.

I suggest for your project you either have a regular timer tick for reading the state of buttons, or its own small ISR that just captures its state.  Generally I have ISR's just store data, and set a flag, with the main routine checking these flags one by one, and 'doing stuff' if it needs to be done.

Some LCD 'routines' can be rather long - like printing a string on the screen.  Longer than I would like to have in an ISR.  This should be part of your main routine, with the ISR's servicing your user inputs (again, probably with a timer for debouching).  Googling 'Arduino menu systems' could help.
 

Offline dan3460Topic starter

  • Frequent Contributor
  • **
  • Posts: 326
  • Country: us
Re: AVR External Interrupt
« Reply #6 on: January 07, 2017, 01:28:28 am »
Point taken, thanks for the information. My first goal is to have a working prototype, I will keep the call in the way that I have it for now. But I will look into moving the setup function away from the ISR.

Thanks guys, I very much appreciate the input and the time you take in answering.
 

Offline kcbanner

  • Contributor
  • Posts: 27
  • Country: ca
Re: AVR External Interrupt
« Reply #7 on: January 09, 2017, 04:10:17 pm »
The ISR for the button push should simply be used to set a flag. Your main loop then can check that flag and change states in your menu system accordingly. Being in an ISR shouldn't be used as a "mode", as anything else going on is halted. I'm not sure what you are building, but say your micro is also talking over i2c or USB to another device, all that communication is now going to be stalled.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf