Start Elkretssimulator Karnaughdiagram Quine McCluskey


Arduino Interrupt

Exempel som använder interrupt


Allmänt om interrupt

Interrupt är engelska och betyder avbrott. Processorn gör ett avbrott från sin normala programkörning för att köra en speciell programkod. När denna kod är avslutad återgår processorn till att göra vad den gjorde innan.

Det här är alltså en asynkron grej. Dvs, processorn kör koden du skrivit som vanligt ("synkron verksamhet" i sammanhanget) och när ett interrupt inträffar - antingen pga en timer-händelse ("timerinterrupt") eller elektrisk händelse utanför processorn ("signalinterrupt") - då avbryter processorn tillfälligt den vanliga kodkörningen för att istället köra koden som hör till interruptet. När denna kod körts färdigt, då återvänder processorn till den vanliga körningen koden.



2 olika typer av interrupt

Det finns 2 olika kategorier av interrupt

Timer -interrupt

Vi sätter en timer och när den räknat ner - eller upp - till ett visst värde, så genereras ett interrupt. Samtidigt som interruptet genereras nollställs räknaren och börjar på nytt räkna ner - eller upp. På detta sätt kan vi skapa ett interrupt t.ex. 1 gång per sekund eller 100 gånger per sekund. Användbart i t.ex. en klocka eller där någonting skall uppdateras eller läsas av med en viss frekvens.

I botten ligger processorns klockfrekvens, som är väldigt hög. Så vi delar ner denna klockfrekvens flera gånger för att få en hanterbar hastighet på räknare i vår timer. Att få fram ett lämpligt värde här, dvs räkna ut inställningarna för interruptet, det handlar alltså både om att dela ner klockfrekvensen till lämplig hastighet och att räkna ut hur stort värde vi skall räkna till eller räkna ner från. Mer detaljer längre ner.

Signal -interrupt

Dessa interrupt triggas när en signal på en ledning ändras. Så vi kan t.ex. koppla en knapp till en ingång, som anropar ett interrupt varje gång knappen trycks på. Eller t.ex. en givare eller en sensor på en ingång, som triggar ett interrupt. Denna metod kan användas för att t.ex. läsa av i vilken position en BLDC -motor befinner sig, så att vi vet vilka elektromagneter i motorn som skall magnetiseras.

Signal -interrupt varianter: Pin change vs External interrupt

Bland signal-interrupten finns i sin tur 2 olika typer, det ena är "pin-change-interrupt" och det andra är "external interrupt". Den sublima skillnaden är att pin-change-interrupt'en delar interruptvektor (interruptvektor = interruptfunktion som anropas vid interruptet) medans external interrupt -pinnarna har egna interrupt-vektorer. Nästan vilken pinne som helst kan konfigureras som pin-change-interrupt men bara 2 stycken kan fungera som external interrupt.

Nedan ser du vilka 2 pinnar som kan användas som external interrupt (egen interruptvektor), det är INT1 och INT0, dvs de blå pinnarna 2 och 3.



Dessa kan användas som t.ex. som i exemplet där jag räknar antalet kontaktstuds. Det väsentliga här är att interruptpinnen triggar funktionen knapptryck() nedan och inget annat. Dvs en sådan här pinne "external interrupt", den triggar sin egen funktion. Det finns 2 stycken dito att tillgå, så du kan skapa 2 stycken sådana här funktioner.

volatile int antal_knapptryck = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);
  attachInterrupt(0, knapptryck, FALLING);
}

void loop()
{
  Serial.println(antal_knapptryck);
  delay(1000);
}

void knapptryck()
{
  antal_knapptryck++;
}


Pin-change interrupt

Pin-change delar interruptvektor. Det betyder att vi kan konfigurera en eller flera pinnar att generera ett interrupt och när signalen förändras (låg blir hög eller hög blir låg) då anropas sedan en gemensam funktion för dessa pinnar. Denna funktion/interruptvektor får sedan klura ut själv exakt vilket pinne det var som orsakade interruptet, ifall det behövs.

Vi kan ta exemplet där vi låter en BLDC -motors hallsensorer trigga interrupt.

Vi sätter upp så att pinne 8,9,10 genererar ett interrupt vid förändring. Oavsett vilken av dessa pinnar 8,9 eller 10 som genererar interruptet så anropas samma funktion här. Denna funktion får sedan klura ut vad som egentligen hände här. I detta fall så tar interruptfunktionen samtliga 3 signaler 8,9,10 och använder dem tillsammans som en pekare i en tabell - eftersom det i just detta fall avgör vilken lindning i BLDC -motorn som skall elektrifieras. Men man kan tänka sig att 8,9,10 också var 3 stycken knappar eller vad som helst. Då hade interruptfunktionen fått klura ut själv exakt vilken pinne som genererade interruptet. Detta kan göras t.ex. genom att lagra tidigare tillstånd och jämföra vid varje interrupt.

// testprogram för hallgivare och styrsignaler

byte clockwise[8]=
{
  B000000,
  B000110,
  B011000,
  B010010,
  B100001,
  B100100,
  B001001,
  B000000
};

byte counterClockwise[8]=
{
  B000000,
  B001001,
  B100100,
  B100001,
  B010010,
  B011000,
  B000110,
  B000000
};

byte phase = 0;
byte lstPhase =0;

void setup() 
{   
  pinMode( 8, INPUT_PULLUP);  // HC Grön kabel
  pinMode( 9, INPUT_PULLUP);  // HB Blå kabel
  pinMode(10, INPUT_PULLUP);  // HA Gul kabel

  // pin change på pinne 8,9,10
  PCICR  = 1;
  PCMSK0 = 7;
  
  Serial.begin(9600); 
}

// pin change på 8,9,10 anropas nedan ISR
ISR (PCINT0_vect)
{
  phase = PINB & 7;
}

String toBinary(int n, int size)
{
    String r;
    while(n!=0) {r=(n%2==0 ?"0":"1")+r; n/=2;}
    while(r.length()<size)
      r="0"+r;
    return r;
}

void loop() 
{
  if(phase!=lstPhase)
  {
    Serial.print("Hall=(");
    Serial.print(phase);
    Serial.print(")=");
    Serial.print(toBinary(phase,3));
    Serial.print(" Medurs=");
    Serial.print(toBinary(clockwise[phase],6));
    Serial.print(" Moturs=");
    Serial.print(toBinary(counterClockwise[phase],6));
    Serial.println();
    lstPhase = phase;
  }
}


Okej, då dyker vi lite djupare. Nedan handlar huvudsakligen om Atmega 328. Databladet för Atmega 328 finns här.

Timer -interrupt

Denna typ av interrupt konfigurerar du så att de sker med ett visst tidsintervall. Varje gång avbrottet görs anropas en speciell funktion som du skrivit, när funktionen är färdig så återgår processorn till vad den gjorde tidigare.

Timer -interrupt är användbart t.ex. för att läsa en signal med ett visst tidsintervall eller uppdatera en skärm med en viss frekvens. Eller för att hålla eller mäta tiden i någon applikation.

Atmegan har 3 stycken timer -räknare, timer0 (8 bitar), timer1 (16 bitar) och timer2 (8 bitar). Dessa räknare tickar upp varje gång timer -klockan ger signal så ska ske. Dessa räknare fungerar nästan på samma sätt. Timer1 är lite mer komplex men jag går inte in på det nu.

CTC Mode, Clear Timer on Compare Match

Ett CTC timer interrupt sker när en räknare har nått ett på förhand inställt värde i compare match registren OCR0A, OCR1A eller OCR2A. När detta värde nåtts nollas även räknaren och processen börjar om där den räknar upp till det förvalda värdet varpå ett nytt interrupt äger rum osv.

Genom att räkna ut ett lämpligt compare match value samt ställa in hastigheten på timer-klockan som räknar upp dessa 3 timers (vilket görs med TCCR0B, TCCR1B eller TCCR2B), så kan vi skapa ett system med timer-interrupt med de exakta tids-intervall vi önskar.

Prescaler

Atmegan kör på en klockfrekvens som är 16 Mhz. Det är en fasligt hög hastighet. Vi vill justera timerklockan (som räknar upp timer0, timer1, timer2) till ett lägre värde. Detta gör vi genom att sätta en prescaler. Vi delar alltså ner klockfrekvensen med en prescaler (pre=först, scaler=skala). Vi sätter prescalern i Timer/Counter Control Registrets 3 första bitar; TCCR0B, TCCR1B eller TCCR2B beroende på timer.

Här nedan ser du att vi kan sätta de 3 första bitarna, Clock Select Bits.



Följande alternativ har vi. Dvs vi kan i praktiken välja mellan att dividera klockfrekvensen med 1, 8, 64, 256 eller 1024. Vi kan också räkna upp timern med en extern klockkälla. Det är inte aktuellt för oss just nu.



Timerhastigheten i Hz = Klockfrekvens (som är 16MHz) / prescaler

Vår timer räknare räknas alltså upp med Timerhastigheten här ovan. Om vi väljer en prescaler på 1024 så får vi en Timerhastighet enligt.

Timerhastigheten i Hz = Klockfrekvens (som är 16MHz) / 1024 = 16 KHz

Detta betyder vidare att om vi väljer ett "compare match value" på 255 så kommer vi får ett interrupt 16.000 / 255 = cirka 60 gånger per sekund. Vi kan skriva en ekvation för hela uttrycket.

Interrupfrekvens (Hz) = 16 MHz / (prescaler * (compare match register + 1))

Man kan såklart möblera om denna ekvation och får då

compare match register = ( 16MHz/ (prescaler * önskad interruptfrekvens)) - 1

Säg att vi vill ha ett timerinterrupt 100 gånger per sekund för att läsa av en signal. Vi väljer t.ex. en prescaler till 1024 och kan då räkna ut vårt compare match register -värde enligt.

compare match register = ( 16MHz/ (1024 * 100)) - 1 = 155

Detta värde får plats i alla timer's så du kan välja timer0, timer1 eller timer2 för detta. Men säg att du önskar ett interrupt per sekund för att läsa av en avståndsmätare eller något annat.

compare match register = ( 16MHz/ (1024 * 1)) - 1 = 15624

Detta värde får inte plats i timer0 (8 biter dvs 0-255) eller timer2 (8 biter dvs 0-255) men däremot i timer 1 (16 bitar dvs 0-65025).

Sammanfattning

För att sätta upp ett timer -interrupt behöver vi välja en lämplig prescaler (1, 8, 64, 256 eller 1024) och räkna ut ett lämpligt värde till vårt compare match register. Mer konkret gör vi såhär:

Vi börjar med att nolla registren vi arbetar med så att vi vet vilka flaggor vi sätter och inte.

TCCR1A = 0;
TCCR1B = 0;
TCNT1  = 0;

Jag stoppar in vårt uträknade compare match -värde i OCR1A. Jag vill ha 1 interrupt per sekund.

OCR1A = 15624;

Sedan sätter vi prescaler, i detta fall 1024 för 1 interrupt per sekund. Om du tittar i tabellen ovan ser du att prescaler för 1024 innebär att vi ska sätta de 3 minst signifikata bitarna i TCCR1B = 101. Decimala siffran för 101 är 5.

Det finns olika sätt att få dit dessa bitar. Vi kan skriva så här:

TCCR1B |= 5;

Vi kan skriva såhär också, vissa tycker det är tydligare:

TCCR1B |= (1 << 2) | (1 << 0);

Eller så kan vi utnyttja de makron som är definierade för de olika bitpositionerna. AVR har kodat en väldig massa makron i sina include -filer, t.ex. kan man i avr\include\avr i filen för atmega328 (iom328p.h, sök på datorn så hittar du den) hitta följande makron

#define CS10 0
#define CS11 1
#define CS12 2
#define WGM12 3
#define WGM13 4
#define ICES1 6
#define ICNC1 7

Vi kan alltså sätta bit 0 och 2 i TCCR1B genom att skriva som nedan.

TCCR1B |= (1 << CS12) | (1 << CS10);

När detta är gjort slår vi också på enable timer compare interrupt genom att sätta bit 1 i TIMSK1 -registret. Vi kan som ovan beskrivet göra detta på flera sätt t.ex. använda makrot i filen iom328p.h

TIMSK1 |= (1 << OCIE1A);

Aktiverar CTC mode genom att sätta bit 1 i TCCR1A

TCCR1A |= (1 << WGM11);

När ovan är gjort kan vi förvänta oss att en ISR anropas, nämligen nedan. Så allt som återstår är lite kod i denna rutin

ISR(TIMER1_COMPA_vect)
{

}

Exempel. Ett interrupt med 1 Hz, dvs 1 gång per sekund.

void setup()
{
  cli(); // innan vi ändrar något stäng interrupt
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei(); // när vi är färdiga dra igång interrupt
}

ISR(TIMER1_COMPA_vect)
{
  // anropas en gång per sekund
}

Det som händer här ovan är alltså att TCNT1 räknas upp och när värdet når OCR1A, dvs 15624, vilket sker efter exakt en sekund, så triggas ett interrupt samtidigt som TCNT1 nollas och uppräkningen börjar om.

Exakt i vilket tempo TCNT1 räknas upp bestäms alltså av prescalern. Om prescaler är 1024, så motsvaras 15624 av 1 sekund.

Är det oklart hur prescaler -bitarna sätts, titta nedan (från ATMEGA328 datablad).





Overflow Interrupt

Istället för att TCNT1 räknar upp till OCR1A med ett visst tempo (som avgörs av prescaler), så finns en mode där man istället sätter TCNT1 till ett värde som sedan istället räknas ner och när räknaren når noll så triggas ett interrupt. Dvs, helt omvänt.



I min stegmotorstyrning med h-brygga har jag använt detta för att enkelt kunna kontrollera tiden mellan interrupt via en potentiometer. Se nedan kod.

class ElstStepper
{
  public:
  ElstStepper(int p1, int p2, int p3, int p4)
  {
    pinMode(pin1 = p1, OUTPUT);
    pinMode(pin2 = p2, OUTPUT);
    pinMode(pin3 = p3, OUTPUT);
    pinMode(pin4 = p4, OUTPUT);
    lastStep = 0;
  }
  
  void StepMotor(int dir)
  {
    digitalWrite(pin1,pin1_phase[lastStep]);
    digitalWrite(pin2,pin2_phase[lastStep]);
    digitalWrite(pin3,pin3_phase[lastStep]);
    digitalWrite(pin4,pin4_phase[lastStep]);    
    lastStep+=dir;
    if(lastStep<0)
      lastStep =3;    
    lastStep%=4;
  }

  private:

  int lastStep;
 
  int pin1;
  int pin2;
  int pin3;
  int pin4;

  int pin1_phase[4]={HIGH,LOW,LOW,HIGH};
  int pin2_phase[4]={LOW,HIGH,HIGH,LOW};
  int pin3_phase[4]={HIGH,HIGH,LOW,LOW};
  int pin4_phase[4]={LOW,LOW,HIGH,HIGH};  
};

long val;
int stepSpeed = 1;
int stepDir = 1;
ElstStepper stepmotor( 8,9,10,11);

void setup()
{
  //Serial.begin(9600);
  // initialize timer1 
  noInterrupts(); 
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = stepSpeed;
  TCCR1B |= (1 << CS11); // prescaler = 8
  TIMSK1 |= (1 << TOIE1); // overflow interrupt enable
  interrupts(); 
}

ISR(TIMER1_OVF_vect)
{
  TCNT1 = stepSpeed;
  stepmotor.StepMotor(stepDir);
}

void loop() 
{
  val=analogRead(0) -520;
  if(val>510)
    val = 510;
  if(val<-510)
    val=-510;

  stepSpeed = abs(val) * 75;
  if(val>0)
    stepDir=1;
  else
    stepDir=-1;  

  
  //delay(100);
  //Serial.println(val);
  
}

Det som händer här ovan är alltså att jag läser värdet från en potentiometer, kopplad som en spänningsdelare så att jag får ett värde 0-1023 på en analog ingång. När potentiometern står i mitten så står stegmotorn stilla, är tanken. När jag vrider åt ena eller andra hållet så skall stegmotorn snurra åt detta hål med stigande hastighet ju mer jag vrider. Jag sätter alltså stepSpeed till ett värde mellan 0 och max cirka 38000 (dvs 510 x 75 = 38250). Detta blir räknaren i interrupt-rutinen som räknas ner med en hastighet som är ungefär klockfrekvensen dividerat med prescalern.

Jag använder en prescaler på 8 och får då cirka 50 anrop per sekund som lägst.

Lite repetition

Så med en prescaler på 256 så får vi alltså 62500 timer-ticks per sekund.

16000000/256 = 62500

Detta betyder alltså att med prescaler på 256 så räknas räknaren upp till 62500 på en sekund eller 31000 på en halv sekund eller till 6250 på en tiondels sekund.

Med en prescaler på 8 får vi 2 miljoner timer-tics per sekund.

16000000/8 = 62500 = 2000000

Detta betyder alltså att med prescaler på 8 så räknas räknaren upp till 2000000 på en sekund (går inte räkna till 2 miljoner) eller 200000 på en tiondels sekund (går inte heller) eller till 20000 på en hundradels sekund (går utmärkt med timer1), dvs 100 anrop per sekund.

Så med en prescaler på 8 och du räknar upp till ca 40000 så anropas alltså interrupt-funktionen 50 gånger per sekund.

Pin-Change -interrupt

Kapitel 17 (EXINT - External Interrupts) i manualen (se ovan länk) handlar om det vi vill göra.

Vi behöver göra 3 saker här. Det första är att aktivera pin change interrupt (PCICR). Det andra är att välja vilka pinnar detta skall angå (PCMSK0 för port B, PCMSK1 för port C, PCMSK2 för port D). Sist men inte minst behövs förstås en interruptfunktion.

1. Aktivera Pin Change Interrupt, PCICR



Observera att PCINT[...] avser pinnarna på själva kretsen. Du får alltså snegla på den här bilden...



...och sedan översätta till denna (eller omvänt)...



Dvs:

PCICR |= 0b00000001;    // Aktivera PB0-PB5
PCICR |= 0b00000010;    // Aktivera PC0-PC6
PCICR |= 0b00000100;    // Aktivera PD0-PD7
PCICR |= 0b00000111;    // Aktivera alla ovan

2. Sätt Pin Change Mask för pinnarna ifråga

I manualen hittar vi i bakvänd ordning följande register i vilket vi skall sätta flaggor för de pinnar vi vill ha våra pin change -interrupt på.







Dvs, ovan kokar ner till följande exempel.

PCMSK0 |= 0b00000010;    // aktivera pinne PB1
PCMSK1 |= 0b00010000;    // aktivera pinne PC4
PCMSK2 |= 0b01000010;    //aktivera PD1 & PD6


Skriv en interruptfunktion

Beroende på vilka pinnar vi valt att använda från de 3 portarna så behöver vi skriva kod i en eller flera av nedanstående ISR -funktioner. Observera att vi i dessa interrupt -funktioner måste testa vilken av pinnarna som genererade interruptet. Det är som tidigare nämt detta som är skillnaden mellan "pin-change -interrupt" och "external interrupt". External interrupt har enga interruptfunktioner (vektorer, avbrottsvektorer) och då vet vi alltid vad som hände när en sådan funktion anropas. Med pin-change måste vi ta reda på vilken pinne som orsakade interruptet själv. Men säg att man behöver 3 interrupt, då kan man iofs lägga dessa på var sin port (dvs en på Port B, en på Port C och en på Port D) och isåfall vet vi ju nedan vem som orsakade interruptet.

ISR(PCINT0_vect)  // Port B
{
  // kod som hanterar pin change interrupt
  // på port B
}   

ISR(PCINT1_vect) // Port C
{    
  // kod som hanterar pin change interrupt
  // på port C
}

ISR(PCINT2_vect) // Port D
{
  // kod som hanterar pin change interrupt
  // på port C
}    

BLDC -motorstyrning

Ett bra exempel, där pinchange -interrupt gör jobbet, är exemplet om vi vill kommutera en borstlös motor med hallsensorer. Vi vill då läsa av hallsensorerna när värdet från dessa ändras och beroende på hallsensorernas status så vill vi elektrifiera vissa kopparlindningar i motorn (EC Motor = Elektroniskt kommuterad motor). Det är detta som är den elektroniska kommutering som får en borstlös motor att snurra. Titta på nedan kod, som står beskrivet utförligt under hur man skapar elektronik för en BLDC -motor.

// driva borstlös motor med hallgivare

byte clockwise[8]=
{
  B000000,
  B000110,
  B011000,
  B010010,
  B100001,
  B100100,
  B001001,
  B000000
};

byte counterClockwise[8]=
{
  B000000,
  B001001,
  B100100,
  B100001,
  B010010,
  B011000,
  B000110,
  B000000
};

byte phase = 0;
byte lstPhase =0;

void setup() 
{   
  pinMode( 0, OUTPUT); 
  pinMode( 1, OUTPUT); 
  pinMode( 2, OUTPUT); // LIC Grå = PD2  
  pinMode( 3, OUTPUT); // HIC Grön = PD3 
  pinMode( 4, OUTPUT); // LIB Lila = PD4
  pinMode( 5, OUTPUT); // HIB Blå = PD5
  pinMode( 6, OUTPUT); // LIA Orange = PD6
  pinMode( 7, OUTPUT); // HIA Gul = PD7
  
  pinMode( 8, INPUT_PULLUP);  // HC Grön kabel
  pinMode( 9, INPUT_PULLUP);  // HB Blå kabel
  pinMode(10, INPUT_PULLUP);  // HA Gul kabel

  // pin change på pinne 8,9,10
  PCICR  = 1;
  PCMSK0 = 7;

  PORTD = clockwise[PINB & 7]<<2; // startläge
  Serial.begin(9600); 
}

// pin change på 8,9,10 anropas nedan ISR
ISR (PCINT0_vect)
{
  phase = PINB & 7;
  PORTD = clockwise[phase]<<2;
}

String toBinary(int n, int size)
{
    String r;
    while(n!=0) {r=(n%2==0 ?"0":"1")+r; n/=2;}
    while(r.length()<size)
      r="0"+r;
    return r;
}

void loop() 
{
  if(phase!=lstPhase)
  {
    Serial.print("Hall=(");
    Serial.print(phase);
    Serial.print(")=");
    Serial.print(toBinary(phase,3));
    Serial.print(" Medurs=");
    Serial.print(toBinary(clockwise[phase],6));
    Serial.print(" Moturs=");
    Serial.print(toBinary(counterClockwise[phase],6));
    Serial.println();
    lstPhase = phase;
    
  }
}

Det som gör jobbet här ovan är alltså att vi konfigurerar pinne 8,9 och 10 att generera ett interrupt när någon förändring sker på dessa pinnar. När en förändring skett så läser vi av signalerna från 8,9 och 10 och beroende på vad dessa signaler är så elektrifierar vi de lindningar i motorn vi önskar. Detta är pinchange -interrupt at work i ett mycket bra exempel.

External interrupts

För External Interrupts kan man använda följande funktion vilket gör allt enklare.

attachInterrupt(pin, ISR, mode)

Pinne är den pinne som det är önskvärt bevaka. ISR är den funktion som skall anropas och mode är vad som skall trigga interruptet signalmässigt.

LOW triggar ISR interruptet när pinnen är låg
CHANGE triggar ISR när pinnens värde skiftar.
RISING triggar ISR skiftar från LOW till HIGH.
FALLING triggar ISR skiftar från HIGH till LOW.

Exempel. När jag räknar kontaktstuds så skriver jag koden såhär.

volatile int antal_knapptryck = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);
  attachInterrupt(0, knapptryck, FALLING);
}

void loop()
{
  Serial.println(antal_knapptryck);
  delay(1000);
}

void knapptryck()
{
  antal_knapptryck++;
}


ATmega328P vektorer

    InterruptNamn
2 External Interrupt Request 0 INT0_vect
3 External Interrupt Request 1 INT1_vect
4 Pin Change Interrupt Request 0 PCINT0_vect
5 Pin Change Interrupt Request 1 PCINT1_vect
6 Pin Change Interrupt Request 2 PCINT2_vect
7 Watchdog Time-out Interrupt WDT_vect
8 Timer/Counter2 Compare Match A   TIMER2_COMPA_vect
9 Timer/Counter2 Compare Match B   TIMER2_COMPB_vect
10Timer/Counter2 Overflow TIMER2_OVF_vect
11Timer/Counter1 Capture Event TIMER1_CAPT_vect
12Timer/Counter1 Compare Match A   TIMER1_COMPA_vect
13Timer/Counter1 Compare Match B   TIMER1_COMPB_vect
14Timer/Counter1 Overflow TIMER1_OVF_vect
15Timer/Counter0 Compare Match A   TIMER0_COMPA_vect
16Timer/Counter0 Compare Match B   TIMER0_COMPB_vect
17Timer/Counter0 Overflow TIMER0_OVF_vect
18SPI Serial Transfer Complete SPI_STC_vect
19USART Rx Complete USART_RX_vect
20USART Data Register Empty USART_UDRE_vect
21USART Tx Complete USART_TX_vect
22ADC Conversion Complete ADC_vect
23EEPROM Ready EE_READY_vect
24Analog Comparator ANALOG_COMP_vect
25Two-wire Serial Interface TWI_vect
26 Store Program Memory Read SPM_READY_vect


Lycka till!

(sidan kommer antaligen byggas på om jag kommer på mer som kanske är användbart :))