Interrupts Funktionsprinzip Interrupts bei ATmega128 Beispiel in C Funktionsprinzip 1
Was ist ein Interrupt? C muss auf Ereignisse reagieren können, z.b.: - jemand drückt eine Taste - USART hat Daten empfangen - im ADC steht ein Wandlungsergebnis bereit Möglichkeiten des C: - aktiv auf ein Ereignis warten (Polling) Nachteil: C kann nichts anderes machen und muss nacheinander auf Ereignisse warten - von einem Ereignis benachrichtigt werden (Interrupt) C bearbeitet ein Programm, unterbricht bei eingehender Benachrichtigung die Bearbeitung, reagiert auf das Ereignis und nimmt danach die Bearbeitung des ursprünglichen Programms an der gleichen Stelle wieder auf Vorteil: C kann andere Aufgaben bearbeiten und mehrere Quellen können in beliebiger Reihenfolge Ereignisse auslösen Funktionsprinzip C PC IRQ IACK Datenbus 1 2 3 6 ROM 4 5 Interrupt- Service- Routine ISR1 0 Adresse IACK IRQ Datenbus IACK IRQ Datenbus ISR0 Adresse Interrupt-Vektor 0 Interrupt-Vektor 1 1 ISR1 2 Adresse Interrupt-Quellen (ext. Geräte, interne IO-Komponenten, Timer,...) ISR2 3 Adresse ISR3 Interrupt-Vektor- Tabelle 2
Funktionsprinzip (Erläuterung) 1. Interrupt-Quelle schickt einen Interrupt-Request über die IRQ- Leitung an C (Ereignis) C schickt über IACK-Leitung ein Interrupt- Acknowledge (ok) an die Quelle, falls der Interrupt zugelassen ist 3. Interrupt-Quelle legt ihren Interrupt-Vektor (eindeutige Kennung der Quelle) auf den Datenbus C sucht aus der Interrupt-Vektortabelle die Adresse der zum Interrupt-Vektor zugehörigen Interrupt-Service-Routine heraus (ISR, spezielles Unterprogramm zur Reaktion auf das Ereignis) C legt den aktuellen Inhalt des Program Counter (PC) auf den Stack und lädt den PC mit der Adresse der ISR C arbeitet die ISR ab (unterbricht Abarbeitung des aktuellen Programms) Nach Abarbeitung der ISR wird der alte Inhalt des PC wieder vom Stack geholt und der C bearbeitet das ursprüngliche Programm weiter Interrupt-Management Interrupt-Vektoren werden priorisiert - Priorität des Interrupt-Vektors entscheidet bei gleichzeitigem Eintreffen mehrerer Interrupts, welcher zuerst bearbeitet wird - bei Atmel-Controllern gilt: der Interrupt-Vektor mit der kleineren Nummer (niedrigeren Adresse) hat die höhere Priorität - die "unterlegene" Interrupt-Quelle hält ihren Interrupt-Request aufrecht und C bearbeitet IRQ später nicht-maskierbare Interrupts können jederzeit auftreten (z.b. bei Reset, schwerem Fehler) maskierbare Interrupts können zugelassen oder nicht zugelassen werden - um z.b. zu verhindern, dass die Bearbeitung einer ISR durch einen neuen IRQ unterbrochen wird - globales Erlauben/Verbieten von Interrupts (I-Flag im Status- Register) - quellenspezifisches Erlauben/Verbieten von Interrupts (xien-bits in Steuerregistern von Interrupt-Quellen) 3
Wann tritt ein Interrupt auf? wenn: I-Flag = 1 (SREG): Interrupts C-weit erlaubt sind und quellenspezifisches Interrupt-Enable-Bit (xie) für entsprechendes Ereignis gesetzt ist, z.b. - USART-Interrupt (Enable Bits): Receive Complete (RXCIE), Transmit Complete (TXCIE), Data Register Empty (UDRIE) - ADC -Interrupt (Enable Bit): ADIE (Conversion complete) und Quelle einen Interrupt-Request sendet Interrupts bei ATmega128 4
Programmieren mit Interrupts ATmega128 Interrupts erlauben I-Flag in SREG setzen Befehl C: sei(); //set enable interrupt quellespezifischen Interrupt freischalten Maskenbits setzen z.b. ADC: ADIE... Conversion complete Interrupt Enable ISR einbinden Die ISR muss an der Adresse im ROM zu finden sein, die im Datenblatt (Interrupt-Vektortabelle) für den speziellen Interrupt definiert ist C: in \io\interrupt.h t ist ein Makro ISR(interrupt_vektor) definiert, das die ISR richtig einbindet ISR programmieren ISR einbinden Interrupt-Vektor-Tabelle datasheet. ATmega128.pdf S. 60/61 Adressen sind als Konstanten definiert (in avr\interrupt.h) C: interruptvektorname= Name_aus_dieser_Tabelle + _vect z.b. ADC_vect, USART0_TX_vect, TIMER0_OVF_vect 5
ISR einbinden in C AVR-Lib stellt Funktionalität zum Interrupt-Handling zur Verfügung - Einbinden von avr/interrupt.h Makro sorgt für Einbindung der Interrupt-Service- Routine ISR(interruptvektorname_vect) { // tu was, aber fass Dich kurz interrupt_vektorname t - ist eindeutig für jede mögliche Interrupt-Quelle - kann dem Datenblatt (siehe Ausschnitt auf Folie Interrupt- Vektor-Tabelle) entnommen werden - ACHTUNG! Compiler warnt, wenn Name falsch geschrieben ISR einbinden Beispiel in C Interrupt-Service-Routine für ADC - sieht aus wie eine Funktion ohne Parameter und Rückgabewert - wird aufgerufen, sobald der ADC eine Wandlung fertig hat (Messwert steht zur Verfügung) #include <avr/interrupt.h> // I-Flag in SREG setzen sei(); // Interrupt für ADC // einschalten ADCSRA = (1 << ADIE); // Interrupt-Serviceroutine (ISR) ISR(ADC_vect) { // tu was Interrupts erlauben quellespezifischen Interrupt freischalten ISR einbinden ISR programmieren 6
Eigenschaften ISR unterbricht an beliebigen bi Stellen das Hauptprogramm! darf keine Register verändern (in ASM Registerinhalte auf Stack retten und wiederherstellen!) darf das Statusregister nicht verändern (in ASM SREG auf Stack retten und wiederherstellen!) muss so kurz wie möglich sein - keine aufwändigen Schleifen! Race-conditions müssen vermieden werden! ISR programmieren in C Register und SREG werden automatisch beim Eintritt in die ISR gesichert und hinterher wiederhergestellt Interrupts werden bei Eintritt in die ISR automatisch verboten (I-Flag) auf 0 und hinterher wieder erlaubt sollen Daten mit dem Hauptprogramm ausgetauscht werden, erfolgt das über globale Variablen (ISR hat keinen Rückgabewert) volatile unsigned char g_value=0; int main(void){... while(1){ PORTB = ~g_value; ISR und Hauptprogramm haben Zugriff auf ISR(ADC_vect){ g_value= ADCH; die globale Variable, welche außerhalb jedes Blocks deklariert wird -> Datenaustausch zwischen ISR und main (oder Funktionen) ist möglich 7
Race-Condition-Problem tritt auf, wenn mehrere Interrupt-Quellen Interrupts auslösen können falls die ISR voneinander abhängig sind, kann es zur Race- Condition kommen, z.b. : 1. Zähler Z=0 -> SRAM 2. ISR1: liest Z=0 aus SRAM, inkrementiert Z -> Z=Z+1, speichert Z =1 3. ISR2: liest Z=1 aus SRAM, inkrementiert Z um 2 -> Z=Z+2, speichert Z=3 4. ISR1:... wenn ISR2 ISR1 unterbricht: 1. Zähler Z=0 -> SRAM 2. ISR1: liest Z=0 aus SRAM, inkrementiert Z -> Z=Z+1 3. ISR2: liest Z=0 aus SRAM, inkrementiert Z -> Z=Z+2, speichert Z=2 4. ISR1: speichert Z -> Z=1 Abhilfe: Critical section Critical Section Geschützter Bereich ISR(xx1_vect) { cli(); //Interrupts t verbieten Z=Z+1; sei(); //Interrupts erlauben bei ATMEL-Prozessoren wird das I-Flag automatisch be o esso e d das ag auto at sc bei Eintritt in die ISR gelöscht (alle Interrupts verboten) und bei Austritt aus der ISR wieder gesetzt Race-Conditions sind hier nur möglich, wenn man Interrupts in der ISR bewusst zulässt 8
Beispiele in C (mit ADC) Beispiel1: - Interrupt-Service-Routine: Messung der Spannung am Poti und Ausgabe der Binärwerte (höchstwertige 8 Bit) auf LEDs - Hauptschleife im main: tut nichts Beispiel2: - Interrupt-Service-Routine: startet Messung, misst die Spannung am Poti (7 höchstwertige Bit) und berechnet einen Wert, der die 8. LED nach x Messungen toggelt - Hauptschleife im main: setzt Messwert und Toggle-Wert zusammen und gibt sie auf die LEDs aus 9