Path: Home =>
AVR overview =>
Interrupt programming => Interrupt execution
Interrupt programming in AVR Assembler
Here it is decribed how interrupt-driven programs basically work. To demonstrate the
principles, an example with multiple interrupts can work together.
Warning! Here you will learn a completely new concept!
For all those who have previously programmed in higher-level languages or have written
loop-controlled assembler programs so far: here is a helpful warning! Forget what you
learned about program execution so far, the following will require you to learn a very
different approach. If you stick with your previous views, you'll not be able to
understand the different concept. So better forget that for a while.
Interrupt-driven programs are like this:
If you have difficulties to understand or feel inconvenient with that concept: do not
try to fall back to linear programming. I guarantee that if you overcome this early
difficulties, you'll be happy to have absolved that, because those interrupt-driven
programs are more efficient, are simpler and elegant, avoid unnecessary loops and
offer simple debugging opportunities.
- all execution is focussed on those interrupts,
- the interrupts control all subsequent processes,
- its well-designed execution (generation, execution, controlled release, execution
time, etc.) is the center of the program,
- all other parts of the program wait for it to execute and are slaves of the
Execution of interrupt-driven programs
The standard execution of an interrupt-driven program in general form shows the
This is how an interrupt-driven program executes:
It is crucial to understand that while an interrupt executes any other interrupts
are disabled by the clearing of the I bit in SREG. So, execution of another interrupt
is blocked until the running interrupt ends with RETI. Therefore: keep those
interrupt service routines as short as possible and perform lengthy operations
outside the ISR. And: never forget the RETI.
- If the device resets, it starts executing the Reset vector at address 0000.
There, a jump instruction routes program execution to the Main: routine.
This routine initializes the stack, which is needed in interrupts. It then sets
up the hardware (the pin for INT0 and TC0) and enables their interrupts.
- The init routine then runs into a loop: the controller is send to sleep, and
only wakes up if one of the interrupt is executed. When woken up, the interrupt
executes and program execution continues by checking the two flags. If one or
both of those are set, the respective reaction is executed, following clearing
of the flag. All those routines loop back to the SLEEP instruction.
- If the user pushes the INT0 button (no matter when this happens), the
That routine sets flag A and then returns from interrupt:
- clears the interrupt flag I in the status register,
- throws the current execution address to the stack, and
- jumps to the INT0 vector at address 1 (single-word) or 2 (two-word).
There, a (R)JMP diverts to the interrupt service routine of INT0.
That ensures that, following the execution of the interrupt service routine,
the controller always returns back to the original execution address.
- the jump address is extracted from the stack (two POP operations) and
written to the program counter, and
- the interrupt flag in the status register is set.
- If the timer reaches its interrupt condition, whenever this occurs, the
same happens, but now with the TC0 vector address. If, within the interrupt
service routine of TC0, e. g. a counter is downcounted and reaches
zero, the flag B is set. If, after being woken up, the TC0-ISR returns from
interrupt, flag B is checked and the respective reaction occurs.
If, by chance, the user pushes the button exactly in the short moment when TC0
reaches its interrupt condition, the INT0-ISR is executed first because it is
located at the lower address. But the TC0 interrupt is not missed: its ISR is
executed immediately after the INT0-ISR terminates.
If your program has to execute very quick, e. g. for the generation of an
infrared signal with 40 kHz, hence you have only 12.5µs until the
next interrupt occurs, you'll have to ensure that the interrupt service routine
does not block further interrupts and misses interrupts. This can require counting
clock cycles, and checking execution times very exactly.
A general rule: interrupt service routines have to communicate via flags with the
normal program execution.
Did I promise too much at the beginning? The two interrupts control the whole
program execution. All other routines react on those interrupts, and only execute
if the interrupts have requested them via the flags. No wait loops, no unnecessary
extra rounds but only necessary instructions. And: a clear, simple and consequent
program structure. And easy to debug: just make sure that the interrupts are
executed when necessary, and make sure that the reactions are as expected.
To the top of that page
©2009-2019 by http://www.avr-asm-tutorial.net