Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
abcminiuser
PostPosted: Feb 16, 2010 - 04:04 AM
Moderator


Joined: Jan 23, 2004
Posts: 9820
Location: Trondheim, Norway

For an updated version of this tutorial in PDF format, please see this page of my website.

Newbie's Guide to AVR Interrupts
By Dean Camera, 2010

Hi all. This is a small guide to teach those new to the embedded world about the wonders of processor interrupts. Feedback appreciated.


Part 1: What are Interrupts?

One of the most fundamental and useful principles of modern embedded processors are interrupts. There seems to be a lot of confusion about interrupts; what are they, and how do we use them? In short, an interrupt is a way for an external (or, sometimes, internal) event to pause the current processor's activity, so that it can complete a brief task before resuming execution where it left off. Consider the following:

Let's say we are at home, writing an excellent tutorial on how a principle of modern embedded processors works. We are very interested in this topic, so we are devoting all our concentration to our keyboard. However, half way though, the phone rings. Despite not being by the phone waiting for a call, we are able to stop what we are doing, take the call, and go back where we left off once we have hung up.

This is how a processor interrupt works. We can set up the processor so that it is looking for a specific external event (like a pin going low, or a timer overflowing) to become true, while it goes on and performs other tasks. When these events occur, we stop the current task, handle the event, and resume back where we left off. This gives us a great deal of flexibility; rather than having to actively poll our events to see if they have happened, we can instead just ignore them completely and trust our interrupt routines to process them instead.

What we are doing is called asynchronous processing - that is, we are processing the interrupt events outside the regular "execution thread" of the main program. It's important to note that we are not doing two things at once (although it may appear that way) - the regular program is stopped while an interrupt routine runs.

When correctly set up, we can link a specific interrupt source to a specific handler routine, called an Interrupt Service Routine, or ISR for short.

Part 2: What can cause interrupts?

There are two main sources of interrupts:

Arrow Hardware Interrupts, which occur in response to a changing external event such as a pin going low, or a timer reaching a preset value
Arrow Software Interrupts, which occur in response to a command issued in software

The 8-bit AVRs lack software interrupts, which are usually used for special operating system tasks like switching between user and kernel space, or for handling exceptions. Because of this, we'll only be looking at hardware interrupts.

Each AVR model contains a different set of interrupt sources. We can find out which interrupts our chosen model has by looking in the "Interrupts" chapter of the AVR's datasheet. This chapter contains a table, similar to the following fragment from the AT90USB1287 datasheet:

Code:
Vector No. | Address | Source | Interrupt Definition
2          | $0002   | INT0   | External Interrupt Request 0
3          | $0004   | INT1   | External Interrupt Request 1
4          | $0006   | INT2   | External Interrupt Request 2


This contains a complete list of all the possible interrupts in our AVR, giving each interrupt's Vector table address, Source Interrupt Name, and Source Interrupt Description. Take a look at your AVR's list - see the variety of interrupt sources, from all sorts of on-chip peripherals of the AVR.

Hardware interrupts can be useful to process sparingly occurring events, such as buttons presses or alarm inputs. They can also be useful for situations where a low, fixed latency is required, for example when processing inputs from a rotary encoder.

Part 3: So how to we define an ISR?

How we do interrupts varies between programming languages, but at the lowest level, we are simply making a general AVR function somewhere in the AVR's FLASH memory space, and "linking" it to a specific interrupt source by placing a JMP or RJMP instruction to this function at the address specified in the table we just looked at. At the start of the AVR's FLASH memory space lies the Interrupt Vector Table, which is simply a set of hardwired addresses which the AVR's processor will jump to when each of the interrupts fire. By placing a jump instruction at each interrupt's address in the table, we can make the AVR processor jump to our ISR which lies elsewhere.

For an ISR to be called, we need three conditions to be true:

Arrow Firstly, the AVR's global Interrupts Enable bit (I) must be set in the MCU control register SREG. This allows the AVR's core to process interrupts via ISRs when set, and prevents them from running when cleared. It defaults to being cleared on power up, so we need to set it.

Arrow Secondly, the individual interrupt source's enable bit must be set. Each interrupt source has a separate interrupt enable bit in the related peripheral's control registers, which turns on the ISR for that interrupt. This must also be set, so that when the interrupt event occurs the processor runs the associated ISR.

Arrow Thirdly, The condition for the interrupt must be met - for example, for the USART Receive Complete (USART RX) interrupt, a character must have been received.

When all three conditions are met, the AVR will fire our ISR each time the interrupt event occurs.

Again, the method used to define an ISR differs between langages and compilers, so I'll just put the AVR-GCC and AVR ASM versions here.

Code:
#include <avr/interrupt.h>

ISR({Vector Source}_vect)
{
   // ISR code to execute here
}


Code:
.org 0x{Vector Address}
jmp MyISRHandler


MyISRHandler:
  ; ISR code to execute here
  reti


Note that in the case of the assembly version, we need to add a "reti" instruction at the end of our interrupt instead of the usual "ret" instruction to return to the main program's execution; this special instruction has the dual function of exiting the ISR, and automatically re-enabling the Global Interrupt Enable bit. This happens inside the C version too when the function returns, we just don't see it normally.

This raises the next point; by default, interrupts are themselves not interruptable. When an interrupt fires, the AVR CPU will automatically disable the Global Interrupt Enable bit, to prevent the ISR from being itself interrupted. This is to prevent stack overflows from too many interrupts occurring at once and to prevent the ISRs from running too long, as most uses of interrupts are to have minimal latency when processing an event. It's perfectly possible to set the Global Interrupt Enable bit again as part of the ISR so that nested interrupts can occur, but this is highly not recommended as it is dangerous.

Part 4: Enabling a Specific Interrupt?

If you simply add in an ISR to your existing program, you will find that it appears to do nothing when you try to fire it. This is because while we have defined an ISR, we haven't enabled the interrupt source! As mentioned in part 3, we need to meet all three criteria for the ISR to fire.

Firstly, we need to set the I bit in the SREG register. This is the Global Interrupt Enable bit, without which the AVR will simply ignore any and all interrupts. In assembly, we have two special single-cycle instructions for dealing with the I bit:

sei, which SEts the I flag
cli, which CLears the I flag

While in C, we have to use special macros from our libc library's header. In the case of AVR-GCC and its avr-libc library, we just use the sei() and cli() macro equivalents defined in <avr/interrupt.h>:

Code:
sei ; Enable Global Interrupts


Code:
sei(); // Enable Global Interrupts



Next, we also need to enable a specific interrupt source, to satisfy the second condition for firing an ISR. The way to do this varies greatly between interrupt sources, but always involves setting a specific flag in one of the peripheral registers. Let's set an interrupt on the USART Receive Complete (USART RX) interrupt. According to the datasheet, we want to set the RXCIE bit in the UCSRB register:

Code:
in r16, UCSRB
ori r16, (1 << RXCIE)
out UCSRB, r16


Code:
UCSRB |= (1 << RXCIE);



That should be enough to make the AVR's execution jump to the appropriate ISR when the interrupt occurs.

Part 5: Things you Need to Know

There are a few things to keep in mind when using interrupts in your program:

1) When an interrupt is executing, your main program isn't. That means that if your application isn't robust and your receive many interrupts in a very short space of time, your main program's execution will slow to a halt. Keep this in mind when you decided to attach a 4MHz clock input to an external interrupt pin.

2) Data shared between the ISR and your main program must be both volatile and global in scope in the C language. Without the volatile keyword, the compiler may optimize out accesses to a variable you update in an ISR, as the C language itself has no concept of different execution threads. Take the following example:

Code:
#include <avr/interrupt.h>

int MyValue;

ISR(SomeVector_vect)
{
   MyValue++;
}

int main(void)
{
   SetupInterrupts();

   while (MyValue == 0); // Wait for interrupt

   TurnOnLED();
}


There is a good chance the program will freeze forever in the while loop, as (according to the optimizer) the "MyValue" global's value never changes in the loop. By making the global variable volatile, you are declaring to the compiler that the value may change due to circumstances it is not aware of, forcing it to generate code to reload the value each time.

3) There's another subtle bug in the above code sample; it's possible at the machine code level for an interrupt to occur half way between the fetching of the two bytes of the integer variable MyValue, causing corruption if the variable's value is altered inside the ISR. This can be avoided by making the fetching of the variable's value in the main program code "atomic", by disabling the global interrupt enable bit while the fetching is taking place. In avr-libc this can be done in the following manner:

Code:
#include <avr/interrupt.h>
#include <util/atomic.h>

volatile int MyValue;

ISR(SomeVector_vect)
{
   MyValue++;
}

int main(void)
{
   SetupInterrupts();

   int MyValue_Local;

   do
   {
      ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
      {
         MyValue_Local = MyValue;
      }
   } while (MyValue_Local == 0) // Wait for interrupt

   TurnOnLED();
}


Your compiler may vary.

4) Each interrupt source has exactly one flag to indicate that the event occurred. If two or more of the same interrupt event occurs while you are in the middle of processing an ISR, you will only receive one ISR call for the event when the current ISR terminates, and the remaining interrupt events will be lost.

5) The interrupt source flag is usually cleared when the ISR fires. This isn't always the case (some interrupt flags are cleared when a particular register is read, such as the USART receive complete flag) however this is more of an exception to the rule. The upshot of this is that if you receive another interrupt to the source you are currently processing while in its ISR, you'll get another call to the ISR once the current one terminates. Again, you are still limited to a single "event occurred" flag for each interrupt, so you can miss interrupts if many happen at once before you have a chance to process them.

Part 6: Putting it all together

Rather than waste my efforts by repeating what I've already done before here, I will instead direct readers to my previous tutorial on Interrupt Driven USART Communications for a short, practical example of how to use interrupts in an application.

For an updated version of this tutorial in PDF format, please see this page of my website.

- Dean Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.


Last edited by abcminiuser on Feb 04, 2012 - 02:18 PM; edited 4 times in total
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
siddhant3s
PostPosted: Feb 16, 2010 - 08:48 AM
Newbie


Joined: Feb 01, 2010
Posts: 5


Awesome. Another great tutorial from the man.
One addition to what I know about this:
Many times, the source name of the interrupt contains spaces ( Eg:TIMER1 COMPA. In this case, while writing the ISR, just replace all the spaces with underscores.
As a rule of thumb, ``just replace all the spaces with underscore and add a _vect"
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Feb 16, 2010 - 11:04 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

siddhant3s,

I wouldn't guess at interrupt vector routine names as you suggest - far better is to look them up in the compiler's manual. As Dean mentions GCC above the manual page in question is:

http://www.nongnu.org/avr-libc/user-man ... rupts.html

specifically the "Choosing the vector: Interrupt vector names" section.

Cliff

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
Kartman
PostPosted: Feb 18, 2010 - 05:50 AM
Raving lunatic


Joined: Dec 30, 2004
Posts: 8730
Location: Melbourne,Australia

Dean, your sample code has a shared int that isn't accessed atomically! Debateable as to the impact on the example given since only 0 is tested.
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: Feb 18, 2010 - 05:57 AM
Moderator


Joined: Jan 23, 2004
Posts: 9820
Location: Trondheim, Norway

Good point Kartman, I've updated the text.

This isn't one of my finest works, I wrote it while I wasn't in the mood to write, so it's not particularly gripping. I'll re-write it at some point in the future -- or someone else can make their own version using the information here as a guide.

- Dean Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
stu_san
PostPosted: Feb 18, 2010 - 06:03 PM
Raving lunatic


Joined: Dec 30, 2005
Posts: 2327
Location: Fort Collins, CO USA

Good job, Dean! This encompasses all of the items (plus a few more) that I had set aside for my tutorial on the same subject. Even if you consider it not one of your finest works, it is a good and needed tutorial.

If I might suggest an item or two:

- Make all of your full program examples include a "forever" loop at the end of main, as well as a "return 0;". Many newbies quite literally grab your examples out of the tutorial. Mistakes in the example programs will always show up on the board.

- In your copious spare time Wink would you add a section on when an interrupt makes sense and when it does not? The classic example of this is external switch handling, with debounce and all that. It may be too much for here, but perhaps something to the effect of, "processors are fast. very fast. humans are mind-numbingly slow compared to processors. Anything that needs to interact with a human almost never needs a hardware interrupt. Instead, simple polling of the pin is preferable."

Again, thanks for your work! I (and many of us) know how little extra time one has when attending a university.

Stu

_________________
Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!
 
 View user's profile Send private message  
Reply with quote Back to top
mcuaust
PostPosted: Feb 21, 2010 - 09:32 AM
Newbie


Joined: Jan 06, 2009
Posts: 19
Location: NSW Australia

Thanks for the tutorial Dean.
If you could post it in your standard PDF format that'd be appreciated too.
 
 View user's profile Send private message  
Reply with quote Back to top
pak_soft
PostPosted: Feb 21, 2010 - 12:00 PM
Wannabe


Joined: May 16, 2008
Posts: 50
Location: Singapore

while (MyValue_Local == 0); // <----

It may be typo error.
Thank Dean for your tutorial, can you post some examples about "atomic" usage?
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Feb 21, 2010 - 01:18 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

Quote:

If you could post it in your standard PDF format that'd be appreciated too

Here's a PDF of the above but you could have done this just as easiy as I did. Just highlighted Dean's post, Ctrl-C, switch to MS Word, Ctrl-V into a new document then Print... and use a PDF writer (I use CutePDF).

Cliff

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
mcuaust
PostPosted: Feb 21, 2010 - 11:28 PM
Newbie


Joined: Jan 06, 2009
Posts: 19
Location: NSW Australia

Thanks for doing that Cliff, and for the tip.
 
 View user's profile Send private message  
Reply with quote Back to top
hooverphonique
PostPosted: Feb 25, 2010 - 10:09 PM
Wannabe


Joined: Aug 29, 2008
Posts: 87
Location: Denmark

nice tut...

since this is labeled as an "AVR" tutorial and not "just" an "ATMega" tutorial, I have a few points:

On the ATMega, a "cli" and "ret" instruction works just as well as the usual "reti" to exit an interrupt.
On the XMega's however, it is extremely important to always use "reti", otherwise the mcu will stay at the current interrupt level (yes, the XMega has 3 nesting interrupt levels, in case you didn't know Smile

this is especially important for OS's that use an ISR for task switching by switching the stack, because the new stack in effect when exiting the ISR may not have the required number of "reti"s to back out of the interrupted state.
 
 View user's profile Send private message  
Reply with quote Back to top
Koshchi
PostPosted: Feb 25, 2010 - 10:26 PM
10k+ Postman


Joined: Nov 17, 2004
Posts: 13815
Location: Vancouver, BC

Quote:
On the ATMega, a "cli" and "ret" instruction works just as well as the usual "reti" to exit an interrupt.

But they are not completely equivalent. If you do that then there is a possibility that the ISR will be entered again without any opcodes of the main thread being run. This is not likely to make a differance, but you should be aware of it. It is best to simply use RETI.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
hooverphonique
PostPosted: Feb 25, 2010 - 10:40 PM
Wannabe


Joined: Aug 29, 2008
Posts: 87
Location: Denmark

Koshchi wrote:
Quote:
On the ATMega, a "sei" and "ret" instruction works just as well as the usual "reti" to exit an interrupt.

But they are not completely equivalent. If you do that then there is a possibility that the ISR will be entered again without any opcodes of the main thread being run. This is not likely to make a differance, but you should be aware of it. It is best to simply use RETI.


you are correct, sir.. it may prevent one instruction from the main thread to be executed, before an isr could fire again..
FreeRTOS exhibits this behaviour in certain scenarios because the code path generates this exact instruction sequence sometimes (which is partly why it doesn't work properly on xmegas).

I meant "sei" earlier, btw, not "cli"..
 
 View user's profile Send private message  
Reply with quote Back to top
boaz_o
PostPosted: Apr 05, 2010 - 07:47 PM
Newbie


Joined: Apr 02, 2008
Posts: 1


hi,
can you explain the difference between PCINT and INT0 ?

thanks for the effort writing this tut..

regards

boaz
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Apr 05, 2010 - 07:54 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

Quote:

can you explain the difference between PCINT and INT0 ?

PCINTs can only detect changes in state and they are grouped in 8's so each ISR is for 8 pins and you then have to work out in the ISR which pin it was that changed and triggered the ISR.

INTn's have an ISR each and can be configured for all of high-level, low-level, rising-edge and falling-edge.

So you get more functionality with INTn's but there's less of them.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
ayeosq
PostPosted: May 03, 2010 - 06:22 AM
Newbie


Joined: Mar 24, 2010
Posts: 12


Code:

void SetupInterrupts(void)
{
    sei();//Enable Global Interrupts
    EIMSK=0x01;
    EICRA=0x03;
}
ISR(INT0_vect)
{
     PORTC=0xFE;
}

I am pretty new to this interrupt thing, first time doing avr programming
Am I doing it right?
I am working on a ATmega88...
 
 View user's profile Send private message  
Reply with quote Back to top
ayeosq
PostPosted: May 03, 2010 - 08:52 AM
Newbie


Joined: Mar 24, 2010
Posts: 12


Ok, my bad, push the wrong input!
Cheers!
 
 View user's profile Send private message  
Reply with quote Back to top
ayeosq
PostPosted: May 03, 2010 - 09:02 AM
Newbie


Joined: Mar 24, 2010
Posts: 12


Now I need to time the INTO is pushed...
Any one can help?
 
 View user's profile Send private message  
Reply with quote Back to top
qli029
PostPosted: Jul 21, 2010 - 01:26 AM
Newbie


Joined: Jun 30, 2010
Posts: 13


very useful,
 
 View user's profile Send private message  
Reply with quote Back to top
jay.chhatpar
PostPosted: Jul 21, 2010 - 10:22 PM
Newbie


Joined: Jul 21, 2010
Posts: 2


Hi! I am using a Rotary Encoder.Can someone please help me with the code. I have encoder sensor connected at PORTD pin2 INT0, Motor Connected at PORTB pin0,1.The encoder sensor gives pulses when motor rotates. I need to stop the motor after pulse count=30.
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jul 22, 2010 - 10:25 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

Do NOT hijack threads - your question has nothing to do with discussion of this tutorial article. Try posting this question as a new thread in AVR Forum.

Moderator

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
jay.chhatpar
PostPosted: Jul 24, 2010 - 07:14 AM
Newbie


Joined: Jul 21, 2010
Posts: 2


It was the first time on forum didn't know i was hijacking the thread. I am sorry
 
 View user's profile Send private message  
Reply with quote Back to top
dpaulsen
PostPosted: Aug 25, 2010 - 07:15 PM
Hangaround


Joined: Apr 27, 2006
Posts: 257
Location: San Jose, CA

I wish this tutorial was around when I first started. Would have saved me a lot of grief in the beginning. Kudos.

I have one suggestion.
abcminiuser wrote:
4) Each interrupt source has exactly one flag to indicate that the event occurred. If two such interrupts occur while you are in the middle of processing another interrupt's ISR, you will only receive one ISR call when the current ISR terminates. This means that if you have lots of interrupts occurring in a short space of time, you can miss interrupts.


When I first read this I thought you were saying that if two different interrupts occur then you will only service one of them, which I knew was wrong. Then when I read it a few times I realized you meant that if the same interrupt occurs in rapid succession. I think this could be made a little more clear for newbies.

Just a suggestion, take it or leave it.

-Daniel
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Aug 25, 2010 - 08:57 PM
10k+ Postman


Joined: Mar 27, 2002
Posts: 18524
Location: Lund, Sweden

Dean (abcminiuser) has several excellent qualities:

1. He writes top-notch tutorials.
2. He get a lot of praise for them, but that does not go to his head.
3. He does not leave his tutorials without attention. He reacts to reasonable suggestions for change.

Your suggestion seems reasonable. I'm sure that Dean will take you suggestion. Thank you for making AVRfreaks better!

Also, Dean, isn't it so that it does not have to be another interrupt that creates the blocking. Eg a for slow pin change ISR and the pin changing so fast it does so three times during the execution of the ISR, the first change will be serviced immediately, the second will be "noted by hardware" and the ISR will be executed again as soon as the first instance exits (or an SEI...), and the third one will be lost. Correct?
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
abcminiuser
PostPosted: Aug 25, 2010 - 11:41 PM
Moderator


Joined: Jan 23, 2004
Posts: 9820
Location: Trondheim, Norway

Quote:

When I first read this I thought you were saying that if two different interrupts occur then you will only service one of them, which I knew was wrong. Then when I read it a few times I realized you meant that if the same interrupt occurs in rapid succession. I think this could be made a little more clear for newbies.


I've altered the paragraph to make my intention clearer. This really wasn't one of my better tutorials, but any information on a subject is better than none, so I posted it anyway under the guise of "waste not, want not".

Quote:

Also, Dean, isn't it so that it does not have to be another interrupt that creates the blocking. Eg a for slow pin change ISR and the pin changing so fast it does so three times during the execution of the ISR, the first change will be serviced immediately, the second will be "noted by hardware" and the ISR will be executed again as soon as the first instance exits (or an SEI...), and the third one will be lost. Correct?


Correct as ever Johan - any blocking ISR thread can potentially cause you to miss events, if two or more of the same event occurs while in an ISR. This is (for the benefit of others) due to each interrupt source having only a single flag to denote when the ISR event precondition has occurred.


One thing I forgot to mention - but which isn't really all that useful to most people - is this:

Quote:
and the ISR will be executed again as soon as the first instance exits (or an SEI...)


Isn't quite 100% right, assuming you interpret "immediately" to mean "the next clock cycle". On the AVR processors, it is guaranteed that at least one clock cycle of the main execution thread will run after a RETI before the next ISR can execute. If your app is tight enough that running a single cycle of the main thread between interrupts makes any difference at all however, you really need to rethink your choice of clock and (possibly) processor.

- Dean Twisted Evil

_________________
Atmel Studio 6.1 is now released, grab it here.
Report AS6/ASF bugs here.
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
JohanEkdahl
PostPosted: Aug 26, 2010 - 08:13 AM
10k+ Postman


Joined: Mar 27, 2002
Posts: 18524
Location: Lund, Sweden

Quote:
Isn't quite 100% right, assuming you interpret "immediately" to mean "the next clock cycle".

No, didn't intend to mean "next cycle". I wrestled a bit with finding different wording, but nothing came out nice. "Right away", "with no delay other than that of the hardware in a non-interrupt-blocking state" etc..

"Without waiting being blocked" might have done it.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
clawson
PostPosted: Aug 26, 2010 - 09:16 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

Quote:

it is guaranteed that at least one clock cycle of the main execution thread will run after a RETI before the next ISR can execute

OK can I be pedantic then? If you have a non-blocking ISR then when a nested ISR performs RETI it will be one opcode of that previous ISR and not mainline that will be executed before the next IF flag is serviced.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
mcuaust
PostPosted: Aug 26, 2010 - 10:56 AM
Newbie


Joined: Jan 06, 2009
Posts: 19
Location: NSW Australia

Dean, Johan, etc.,
your willingness to help others, and your preparedness to work together, makes AVR Freaks such a resource.

Well done! (from a novice in the field).
 
 View user's profile Send private message  
Reply with quote Back to top
nobbyv
PostPosted: Sep 10, 2010 - 05:18 PM
Hangaround


Joined: Mar 17, 2008
Posts: 249


abcminiuser wrote:
[
5) The interrupt source flag is usually cleared when the ISR fires. This isn't always the case (some interrupt flags are cleared when a particular register is read, such as the USART receive complete flag) however this is more of an exception to the rule. The upshot of this is that if you receive another interrupt to the source you are currently processing while in its ISR, you'll get another call to the ISR once the current one terminates.
- Dean Twisted Evil


Just want to make sure I understand this point: so I have one interrupt set up. It fires and I enter my ISR and process some code. The same interrupt then fires again while processing the code in my ISR. The quote above means that I will complete my ISR, which I am already in, then start my ISR over again? Is this correct? Thanks
 
 View user's profile Send private message  
Reply with quote Back to top
Koshchi
PostPosted: Sep 10, 2010 - 05:46 PM
10k+ Postman


Joined: Nov 17, 2004
Posts: 13815
Location: Vancouver, BC

Quote:
Is this correct?
Yes. Keep in mind though, if the same interrupt fires more than once while an ISR is being processed, then all but one of those triggerings will be lost.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Sep 10, 2010 - 05:50 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

Also note that ONE opcode of the interrupted code will be called between each successive ISR entry. If your main (non interrupt code) is doing anything but an empty loop it probably won't do you any favours for the design to only allow one opcode of it to execute between each ISR. Usually in embedded design you move the "heavy work" out of the ISR and put it in the main code if it's not timing critical. Then just do the timing critical stuff in the ISR and then set a flag to let main handle the rest when it gets a chance.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
mcuaust
PostPosted: Sep 11, 2010 - 04:33 AM
Newbie


Joined: Jan 06, 2009
Posts: 19
Location: NSW Australia

clawson wrote:
Also note that ONE opcode of the interrupted code will be called between each successive ISR entry.

Please explain what this means.
 
 View user's profile Send private message  
Reply with quote Back to top
Koshchi
PostPosted: Sep 11, 2010 - 05:02 AM
10k+ Postman


Joined: Nov 17, 2004
Posts: 13815
Location: Vancouver, BC

It means exactly what it says: when an ISR finishes, at least one opcode of the code that was interrupted will be run before any other ISR is serviced.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
mcuaust
PostPosted: Sep 11, 2010 - 05:43 AM
Newbie


Joined: Jan 06, 2009
Posts: 19
Location: NSW Australia

Thanks Koshchi, I understand it now.
 
 View user's profile Send private message  
Reply with quote Back to top
nobbyv
PostPosted: Sep 13, 2010 - 03:43 PM
Hangaround


Joined: Mar 17, 2008
Posts: 249


Koshchi wrote:
Quote:
Is this correct?
Yes. Keep in mind though, if the same interrupt fires more than once while an ISR is being processed, then all but one of those triggerings will be lost.


Understood. Thanks for the replies.
 
 View user's profile Send private message  
Reply with quote Back to top
macmad1984
PostPosted: Sep 28, 2010 - 06:05 PM
Newbie


Joined: Jan 18, 2010
Posts: 16


Very good, thank you. This cleared up a lot I could not be bothered to read in the datasheets. Yes, I know, I'm lazy...

As a recommendation, more about the flow of the program would be helpful. For example, what happens to the PC during an interrupt? Does it get pushed to the stack like during a call? I gather from what you said about stack overflows that this may be the case. Sorry if I somehow missed this bit, and I don't mean to sound negative.

In short:
You have been very helpful, thanks a bunch for this great tutorial!Very Happy
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Sep 28, 2010 - 06:35 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

Quote:

As a recommendation, more about the flow of the program would be helpful. For example, what happens to the PC during an interrupt? Does it get pushed to the stack like during a call?

Yes. I know you don't like reading manuals (not a great way to start out as a programmer as about 15-20% of the job is reading data/manuals) but you can see this yourself by simulating a simple timer interrupt program in AVR Studio and breakpointing at the entry to the ISR. You will see the interrupted execution address at the base of the stack.

For example build this for mega328P and simulate in Studio Simulator 2:
Code:
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t count;

int main(void)
{
   // build and simulate (V2) this for mega328P
   TIMSK0 |= (1<<TOIE0);
   TCCR0B |= (1<<CS00);
   sei();
   while(1) {
      PORTB = count;
      PORTD = ~count;
   }
}

ISR(TIMER0_OVF_vect) {
   count++;
}

The code builds to:
Code:
00000090 <main>:
volatile uint8_t count;

int main(void)
{
   // build and simulate (V2) this for mega328P
   TIMSK0 |= (1<<TOIE0);
  90:   80 91 6e 00    lds   r24, 0x006E
  94:   81 60          ori   r24, 0x01   ; 1
  96:   80 93 6e 00    sts   0x006E, r24
   TCCR0B |= (1<<CS00);
  9a:   85 b5          in   r24, 0x25   ; 37
  9c:   81 60          ori   r24, 0x01   ; 1
  9e:   85 bd          out   0x25, r24   ; 37
   sei();
  a0:   78 94          sei
   while(1) {
      PORTB = count;
  a2:   80 91 00 01    lds   r24, 0x0100
  a6:   85 b9          out   0x05, r24   ; 5
      PORTD = ~count;
  a8:   80 91 00 01    lds   r24, 0x0100
  ac:   80 95          com   r24
  ae:   8b b9          out   0x0b, r24   ; 11
  b0:   f8 cf          rjmp   .-16        ; 0xa2 <main+0x12>

000000b2 <__vector_16>:
   }
}

ISR(TIMER0_OVF_vect) {
  b2:   1f 92          push   r1
  b4:   0f 92          push   r0
  b6:   0f b6          in   r0, 0x3f   ; 63
  b8:   0f 92          push   r0
  ba:   11 24          eor   r1, r1
  bc:   8f 93          push   r24
   count++;
  be:   80 91 00 01    lds   r24, 0x0100
  c2:   8f 5f          subi   r24, 0xFF   ; 255
  c4:   80 93 00 01    sts   0x0100, r24
}
  c8:   8f 91          pop   r24
  ca:   0f 90          pop   r0
  cc:   0f be          out   0x3f, r0   ; 63
  ce:   0f 90          pop   r0
  d0:   1f 90          pop   r1
  d2:   18 95          reti

Put a breakpoint on the "push r1" at the start of the ISR.

Run the code. When it hits the breakpoint you will see that the SP value is 0x08FB. If you now open a memory window set the droplist to "Data" and then look at around 0x8F? You will see that the memory at 0x8FC contains 0x00 0x56. Looking at the code of the while(1) loop in main:
Code:
11:          sei();
+00000050:   9478        SEI                      Global Interrupt Enable
13:             PORTB = count;
+00000051:   91800100    LDS       R24,0x0100     Load direct from data space
+00000053:   B985        OUT       0x05,R24       Out to I/O location
14:             PORTD = ~count;
+00000054:   91800100    LDS       R24,0x0100     Load direct from data space
+00000056:   9580        LAT       R24            Load and Toggle
+00000057:   B98B        OUT       0x0B,R24       Out to I/O location
+00000058:   CFF8        RJMP      PC-0x0007      Relative jump

you'll see that 0x0056 is the LAT opcode. The system jumped to the vector after finishing the LDS opcode at location 0x0054

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
badbyte
PostPosted: Nov 06, 2010 - 09:03 PM
Newbie


Joined: Nov 06, 2010
Posts: 3


Hi People,
Im new in using the avr, wanted to try out the code with an interrupt example.
When reaching the line "MCUCR|=(1<<ISC01)|(1<<ISC00);" in debug mode, it instantly jumps to the ISR.
Any idea why?
Here is the code:
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

int main(void)
{
DDRD=0x00;
DDRD=0xff;
// Enable INT0 External Interrupt
GICR |= (1<<INT0);

// Enable Interrupts
sei();
// Falling-Edge Triggered INT0
MCUCR|=(1<<ISC01)|(1<<ISC00);

while (1)
{

}
}

// External Interrupt 0 ISR
ISR(INT0_vect)
{
PORTB++;
}
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Nov 06, 2010 - 09:16 PM
10k+ Postman


Joined: Mar 27, 2002
Posts: 18524
Location: Lund, Sweden

Hey, badbyte!

We need to double check the bit numbers and stuff against the data sheet, but you're not telling what AVR model you're having.

Still, I have this from one AVR data sheet:
Quote:
When changing the ISCn1/ISCn0 bits, the interrupt must be disabled by clearing its Interrupt Enable bit in the EIMSK Register. Otherwise an interrupt can occur when the bits are change


I have this text right below the table describing the ISCnn bits. Yours too, perhaps?
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
badbyte
PostPosted: Nov 06, 2010 - 09:18 PM
Newbie


Joined: Nov 06, 2010
Posts: 3


Ah right. I'm using the Atmega32.
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Nov 06, 2010 - 09:24 PM
10k+ Postman


Joined: Mar 27, 2002
Posts: 18524
Location: Lund, Sweden

Quote:

I'm using the Atmega32.

And did the External Interrupt section in the data sheet have anything to say about the order in which SFR register bits should be manipulated?
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
badbyte
PostPosted: Nov 06, 2010 - 10:17 PM
Newbie


Joined: Nov 06, 2010
Posts: 3


Ummm, scratch that question I just solved it.
 
 View user's profile Send private message  
Reply with quote Back to top
gtpats
PostPosted: Dec 01, 2010 - 05:32 AM
Newbie


Joined: Mar 05, 2009
Posts: 19


hi,

Thank you for the tutorial, gives me few questions.

1.I am trying to know how much time (micro secs) can the control be inside the ISR function. Does it have a timeout, or can we make it stay as long as we want - [The main program halts here]

2-as far as i know , 100 cycles is max, each cycle is 1/8000000 secs ; 0.125 uSec [for an 8MHZ, atmega128]
But when programming in C we do not know how many cycles we are using, is there a way to find the No.of cycles in ISR through any way?
3- Interrupt Latent time is min 4cycles
4- from data sheet "A return from an interrupt handling routine takes four clock cycles. During these 4clock cycles,the Program Counter (two bytes) is popped back from the Stack, the Stack Pointer is incremented by two, and the I-bit in SREG is set."

Can someone confirm 1. pls

- gt
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Dec 01, 2010 - 12:59 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

1) You said it yourself, main() (and it's called functions and other interrupts) are prevented from executing while an ISR is being serviced. As such ISRs should always be designed to operate for the absolute minimum time possible. Usually this need be nothing more than collecting a data byte from the interrupting device or setting some output state. If more is to be done set a volatile variable "flag" in the ISR then, back in main() check this flag from time to time and if set do the rest of the none time critical work triggered by the ISR.

2) Where on earth did you get this 100 cycle figure from? Sure (as in 1 above) it's a guideline to keep ISRs short and if you could make them 100 cycles or less that'd be great - but nothing forces this - they could be 532 million 581 thousand 295 cycles if you really want (though it's poor design).

3) There's certainly an overhead for jumping via the vector but, because of the minimum work usually performed in an ISR it's tricky to make them much less than about 30 cycles. Often this makes main loop polling a faster solution than an interrupt.

Cliff

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
ereihani
PostPosted: Dec 21, 2010 - 08:14 PM
Wannabe


Joined: Mar 07, 2005
Posts: 77
Location: Mashhad, Iran

Thanks Dean.
I was confused in my program for the volatile definition of my variable.
 
 View user's profile Send private message  
Reply with quote Back to top
hobadirl
PostPosted: Feb 04, 2011 - 01:54 PM
Newbie


Joined: Jul 11, 2010
Posts: 8


Nice work thank you.........
 
 View user's profile Send private message  
Reply with quote Back to top
arian88
PostPosted: Jul 14, 2011 - 02:43 PM
Rookie


Joined: Jul 03, 2009
Posts: 30
Location: Oslo

Thank you for so good tutorial, I read it.. but have one question..
what about if instead of UART Rx you want to check status of one pin..
Feks Port C, Pin7 (or others pin), to give event for that to check this pin is low or high and do something...

How can you do this trough Interrupts? Lets say we use Atmega8..

Code:
int main(void){
   setOutput(&DDRC, 0);
   setLow(&PORTC, 0);

   //enable global Interrups   
   sei();
   //give status, give sign for any logical
   setLow(&MCUCR, ISC01);
   setHigh(&MCUCR, ISC00);
   //enable INT0, PortD.2 (Atmega8 use PIND2 as INT0)
   setHigh(&GIMSK, INT0);


}


ISR(PCINT_value)
{
      setHigh(&PORTC, 0);
   _delay_ms(500);
   setLow(&PORTC, 0);
   _delay_ms(500);
}


I have write this code, and I know about something here is wrong..
1- But I dont understand how can I connect the port and pin to correctly Interrupt?
2- Can I choose wich of PCINT for wich port I want? Or each PCINT is for specific Pin?

NB! setHigh() and setLow() is just function i write it for myself for turn High and Low, and they works Wink )
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jul 14, 2011 - 02:49 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62220
Location: (using avr-gcc in) Finchingfield, Essex, England

Quote:

Port C, Pin7

Quote:

Atmega8

For that you would need divine intervention from God herself! The mega8 only has PC0..PC5 - there is no PC7.

BTW the mega8 is one of the ancient AVRs so only has external/pin change interrupts on two pins (INT0/INT1, physical pins 4 and 5, also PD2, PD3). Use a modern chip like a mega88 if you want interrupts from all the IO.

(oh and your post is off-topic for the tutorial forum - it's not for diagnosing problems - it's for discussing the tutorial article in the first post of each thread).

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
arian88
PostPosted: Jul 14, 2011 - 03:23 PM
Rookie


Joined: Jul 03, 2009
Posts: 30
Location: Oslo

Ok, thanks anyway.. Maybe I should post new Thread and dont continue here, have still problem to understand how this tutorial can works with PD2 or PD3 to check they are high or low...

Again, thanks for this good tutorial
 
 View user's profile Send private message  
Reply with quote Back to top
mihaescuflorin90
PostPosted: Oct 29, 2011 - 08:16 AM
Newbie


Joined: Oct 18, 2011
Posts: 1


Excellent tutorial,Dean buddy!
Congratulations!
 
 View user's profile Send private message  
Reply with quote Back to top
Nadwaga
PostPosted: Oct 10, 2012 - 03:36 PM
Newbie


Joined: Oct 10, 2012
Posts: 1


Awesome tutorial that helped me a lot, thanks for sharing!

_________________
dieta dukana i
tabletki na odchudzanie
 
 View user's profile Send private message  
Reply with quote Back to top
behzadh47
PostPosted: Dec 07, 2012 - 12:53 PM
Wannabe


Joined: Nov 23, 2012
Posts: 50


Knowing that each address of program memory is 16 bits wide and PC is less than 16 bits wide, why the address of each interrupt vector differs from the previous one by two?
For instance:
the INT1 vector is located at address $0004 and the INT2 vector is located at address $ 0006.
 
 View user's profile Send private message  
Reply with quote Back to top
webpower
PostPosted: May 02, 2013 - 12:24 AM
Newbie


Joined: Apr 30, 2013
Posts: 13


Thanks for posting this. Very useful.
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits