Interrupts mit dem AVR GCC Compiler (WinAVR)
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien interrupt.h der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich signal.h).
// fuer sei(), cli() und ISR():
#include <avr/interrupt.h>
|
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.
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen Zustands die Interrupts aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
//...
void NichtUnterbrechenBitte(void)
{
uint8_t tmp_sreg; // temporaerer Speicher fuer das Statusregister
tmp_sreg = SREG; // Statusregister (also auch das I-Flag darin) sichern
cli(); // Interrupts global deaktivieren
/* hier "unterbrechnungsfreier" Code */
/* Beispiel Anfang
JTAG-Interface eines ATmega16 per Software deaktivieren
und damit die JTAG-Pins an PORTC für "general I/O" nutzbar machen
ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine "timed sequence"
einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229):
Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben
werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde
die erforderliche Sequenz "brechen", das JTAG-Interface bliebe
weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */
MCUCSR |= (1<<JTD);
MCUCSR |= (1<<JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information
/* Beispiel Ende */
SREG = tmp_sreg; // Status-Register wieder herstellen
// somit auch das I-Flag auf gesicherten Zustand setzen
}
void NichtSoGut(void)
{
cli();
/* hier "unterbrechnungsfreier" Code */
sei();
}
int main(void)
{
//...
cli();
// Interrupts global deaktiviert
NichtUnterbrechenBitte();
// auch nach Aufruf der Funktion deaktiviert
sei();
// Interrupts global aktiviert
NichtUnterbrechenBitte();
// weiterhin aktiviert
//...
/* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */
cli();
// Interrupts jetzt global deaktiviert
NichtSoGut();
// nach Aufruf der Funktion sind Interrupts global aktiviert
// dies ist mglw. ungewollt!
//...
}
|
Zu den aktivierten Interrupts ist eine Funktion zu programmieren, deren Code aufgerufen wird, wenn der betreffende Interrupt auftritt (Interrupt-Handler, Interrupt-Service-Routine). Dazu existiert die Definition (ein Makro) ISR.
|