Friday, August 24, 2012

AVR Assembly Interrupt Handlers

Here's an example of assembly interrupt handler routines for AVR using WinAVR / gcc. Hopefully I can save you the couple of hours it took me to figure this out.

Using AVR Studio 4 with gcc, write your assembly code like this:

.global INT0_vect ; tells the linker this is where INT0_vect lives


The .global directive tells the linker "here's the interrupt service routine" and the appropriate jump instruction is filled into the vector table. When the interrupt fires, the program counter moves to that spot in the vector table, the jmp is issued and your ISR code is run, then returns from interrupt with the reti instruction.

You can find the correct interrupt service routine labels to use by looking inside your device-specific io file, e.g., io328p.h
/* Interrupt Vectors */
/* Interrupt Vector 0 is the reset vector. */
#define INT0_vect         _VECTOR(1)   /* External Interrupt Request 0 */
#define INT1_vect         _VECTOR(2)   /* External Interrupt Request 1 */
#define PCINT0_vect       _VECTOR(3)   /* Pin Change Interrupt Request 0 */
#define PCINT1_vect       _VECTOR(4)   /* Pin Change Interrupt Request 0 */
If you want to check to be sure your interrupt routines are getting populated within the interrupt vector table (starting at address 0x0000), then do:

avr-objdump -h -S -z AVRcam.elf > AVRcam.lss

...and edit the file, and look for the __vectors table, excerpt below. You can see below that I have vectors 1 and 2 defined and several undefined. These interrupts would result in jumps to __bad_interrupt, halting the MCU.

Disassembly of section .text:

00000000 <__vectors>:
       0:       0c 94 34 00     jmp     0x68    ; 0x68 <__ctors_end>
       4:       0c 94 95 01     jmp     0x32a   ; 0x32a <__vector_1>
       8:       0c 94 96 01     jmp     0x32c   ; 0x32c <__vector_2>
       c:       0c 94 51 00     jmp     0xa2    ; 0xa2 <__bad_interrupt>
      10:       0c 94 51 00     jmp     0xa2    ; 0xa2 <__bad_interrupt>
      14:       0c 94 51 00     jmp     0xa2    ; 0xa2 <__bad_interrupt>

So, what are these mysterious __vector_XX addresses?

You can find out by consulting your datasheet. For example, in the ATmega328P datasheet, reproduced here for educational purposes: Vector 1 is INT0, vector 2 is INT1, and so on.

1 comment: