Sie sind auf Seite 1von 11

Interrupttechnik mit dem ATmega32

Unter einem Interrupt kann man sich einen durch Hardware ausgelösten
Unterprogrammaufruf vorstellen.

Aufgrund einer Interruptanforderung wird das laufende Programm unterbrochen und


das hierfür vorgesehene Unterprogramm ausgeführt. Danach wird das unterbrochene
Programm an der Unterbrechungsstelle wieder fortgesetzt.

Interrupts werden bei Mikrocontrollern dazu eingesetzt, um auf äußere und interne
Systemzustände augenblicklich reagieren zu können. So kann zum Beispiel in einem
gesteuerten Prozess ein gefährlicher Betriebszustand eintreten oder ein interner
Prozessorzustand erfordert eine Reaktion, zum Beispiel beim Empfang von Daten an
der seriellen Schnittstelle.

Ein Programm mit Interruptsteuerung hat folgende Merkmale:

Es muss eine Interrupt-Service-Routine vorhanden sein (das auszuführende


Unterprogramm)
Die Interruptsteuerung muss an Anfang des Hauptprogramms aktiviert worden sein.
Die folgenden Ereignisse können einen Interrupt auf einem AVR ATmega32 auslösen,
wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts entspricht:
Allgemeines über die Interruptabarbeitung

Wenn ein Interrupt eintrifft, wird automatisch das Global Interrupt Enable Bit GIE
im Status Register SREG gelöscht und alle weiteren Interrupts unterbunden.

Obwohl es möglich ist, zu diesem Zeitpunkt bereits wieder das GIE-Bit zu setzen,
wird dringend davon abgeraten. Dieses wird nämlich automatisch gesetzt, wenn die
Interruptroutine beendet wird.

Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen


Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-
Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann eigentlich nur dann
zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer
Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit
niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr
kurz gehalten werden sollen.

Interrupts mit dem AVR GCC Compiler (WinAVR)

Funktionen zur Interrupt-Verarbeitung werden in der Includedatei interrupt.h


der avr-libc zur Verfügung gestellt :

#include <avr/interrupt.h> // fuer sei(), cli() und ISR():

Das Makro sei() schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht,
als das Global Interrupt Enable Bit im Status Register gesetzt.

Das Makro cli() schaltet die Interrupts aus, oder anders gesagt, das Global
Interrupt Enable Bit im Status Register wird gelöscht.

Die Interrupt-Service_Routinen heißen alle ISR und haben als Parameter für den
betreffenden Interrupt einen Interruptvektor.

Mit Hilfe des Interruptvektors sorgt der Compiler dafür, dass die ISR an der
richtigen Interrupt-Einsprungadresse im Controller platziert wird.
Damit eine ISR aufgerufen werden kann, muss der Interrupt im Hauptprogramm
aktiviert werden. Diese Aktivierung besteht aus der Aktivierung des jeweiligen
Interrupts und aus der allgemeinen Interruptfreigabe mit sei();
Tabelle der Interruptvektoren des Atmega32

_VECTOR(1) INT0_vect External Interrupt Request 0


_VECTOR(2) INT1_vect External Interrupt Request 1
_VECTOR(3) INT2_vect External Interrupt Request 2
_VECTOR(4) TIMER2_COMP_vect Timer/Counter2 Compare Match
_VECTOR(5) TIMER2_OVF_vect Timer/Counter2 Overflow
_VECTOR(6) TIMER1_CAPT_vect Timer/Counter1 Capture Event
_VECTOR(7) TIMER1_COMPA_vect Timer/Counter1 Compare Match A
_VECTOR(8) TIMER1_COMPB_vect Timer/Counter1 Compare Match B
_VECTOR(9) TIMER1_OVF_vect Timer/Counter1 Overflow
_VECTOR(10) TIMER0_COMP_vect Timer/Counter0 Compare Match
_VECTOR(11) TIMER0_OVF_vect Timer/Counter0 Overflow
_VECTOR(12) SPI_STC_vect Serial Transfer Complete
_VECTOR(13) USART_RXC_vect USART, Rx Complete
_VECTOR(14) USART_UDRE_vect USART Data Register Empty
_VECTOR(15) USART_TXC_vect USART, Tx Complete
_VECTOR(16) ADC_vect ADC Conversion Complete
_VECTOR(17) EE_RDY_vect EEPROM Ready
_VECTOR(18) ANA_COMP_vect Analog Comparator
_VECTOR(19) TWI_vect 2-wire Serial Interface
_VECTOR(20) SPM_RDY_vect Store Program Memory Ready
Beispiel 1:
Interruptgesteuerte Fußgängerampel mit dem externen Interrupt INT0:

Die Abbildung zeigt die Lage der Pin-Anschlüsse der externen Interrupts
INT0, INT1 und INT2.

Initialisierung von INT0:

GICR|=(1<<INT0); // INT0 enable im General Interrupt Control Register


MCUCR|=(1<<ISC01)|(1<<ISC00); //Steigende Flanke löst aus
sei();

Die Interrupt-Service-Routine:

Die ISR wird automatisch aufgerufen, wenn ein Fußgänger beim Drücken der
Ruftaste an PD2 eine ansteigende Signalflanke erzeugt.
Die Ampel durchläuft dann die 6 Steuerphasen, um anschließend wieder in den
Grundzustand zurückzukehren.
Die recht lang dauernde ISR entspricht eigentlich nicht den Regeln!
Da das Hauptprogramm nach den Start jedoch nichts mehr macht, geht das in
Ordnung.

ISR(INT0_vect){
uint8_t n;
for(n=0;n<6;n++){
PORTC=sampel[n];
PORTD=fampel[n];
warten_ns(zeit[n]);
}
}
Das komplette Programm:

//Steuerung einer Fußgängerampel


//Anwendung von INT0
//W.Tschallener 6.11.2007

//Verbindungen MCU - APL-Karte:


//PortC - SV4, PortD - SV5
//---------------------------------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include "warten.h"
const uint8_t fampel[6]={0x20,0x10,0x10,0x10,0x30,0x40},
sampel[6]={0x30,0x30,0x40,0x30,0x30,0x30},
zeit[6] = {1, 1, 5, 1, 1, 5};
//----------------------------------------------------------------
void FussgaengerAmpel_init(void){

//Datenrichtungsregister setzen:
DDRC=0xf0;//Straßenampel PC4(ROT),PC5(Gelb),PC6(Grün)
DDRD=0xf0;//Fussgängertaste PD2,
//Fussgängerampel PD4(ROT),PD5(Rot),PD6(Grün)

//Ampel in Grundstellung bringen:


PORTC=sampel[5];//Straßenampel
PORTD=fampel[5];//Fußgängerampel

//Interrupt initialisieren:
MCUCR|=(1<<ISC01)|(1<<ISC00); //Steigende Flanke löst aus
GICR|=(1<<INT0); // INT0 enable
// Interrupt starten
sei();
}
//----------------------------------------------------------------
ISR(INT0_vect){
uint8_t n;
for(n=0;n<6;n++){
PORTC=sampel[n];
PORTD=fampel[n];
warten_ns(zeit[n]);
}
}
//----------------------------------------------------------------
int main(void){
FussgaengerAmpel_init();

while(1);//Endlosschleife, die nichts macht


return 0; }
Beispiel 2

Software-Uhr mit Timer/Counter0 im CTC-Mode


(CTC: Clear Timer On Compare Match)

Timer/Counter0 Compare Match Interrupt:


Im Timer Interrupt Masken Register muss das Bit OCIE0 = 1 gesetzt werden.
(OCIE0 : Output Compare Interrupt Enable 0)

Das Control-Register TCCR0 wird auf CTC-Mode eingestellt:


WGM00=0, WGM01=1
Der Ausgang OC0 ist nicht angeschlossen:
COM00=0, COM01=0
Der Prescaler wird auf den Teilungsfaktor 256 eingestellt:
CS02=1, CS01=0, CS00=0
Hieraus ergibt sich eine Taktfrequenz von 8MHz/256=31 250 Hz.

Stellt man nun das Register OCR0 auf 249, so wird das Zählregister TCNT0
125-mal in der Sekunde zurückgesetzt (31250/250=125).
Damit können 125 OutputCompare0-Interruptsausgelöst werden, die das Timing
einer Softwareuhr bewirken
//Einfaches Uhrenprogramm mit Timer/Counter0 im CTC-Modus
//---------------------------------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include "lcd_port.h"

volatile uint8_t z, stunde=23,minute=59,sekunde=50;

//Die ISR wird 125-mal pro Sekunde aufgerufen


ISR(TIMER0_COMP_vect){
z++;
if(z==125) {sekunde++; z=0;
//Das nachfolgende Stellen der Uhr könnte auch in main erfolgen!
if(sekunde==60){sekunde=0; minute++;}
if(minute==60){minute=0;stunde++;}
if(stunde==24)stunde=0;
}
}

TIMER0_interrupt_init(void){
z=0; //ISR-Zähler = 0
TCNT0=0; //Anfangszählerstand = 0
OCR0=249; //Zähler zählt bis 250: 31250/250 = 125
TCCR0=0x0c; //CTC-Modus: Takt intern von 8 Mhz /256 = 31250Hz

//Timer/Counter0 Compare Match Interrupt aktivieren:


TIMSK|=(1<<OCIE0);
sei();
}

void uhrzeit_anzeigen(void){
lcd_gotoxy(0,0);lcd_putui_0(stunde,2);
lcd_putc(':'); lcd_putui_0(minute,2);
lcd_putc(':'); lcd_putui_0(sekunde,2);
}
void main(){
lcd_init(12);
TIMER0_interrupt_init();
do{

uhrzeit_anzeigen();

}while(1);
}