(AVR) Interrupts: Unterschied zwischen den Versionen

Aus TippvomTibb
Zur Navigation springen Zur Suche springen
Zeile 68: Zeile 68:
 
Es sei hier bemerkt, dass innerhalb eines Interrupt z.B. kein delay() und millis() funktioniert. Sobald man etwas benutzt was auch Interrupts benutzt ist der Ärger mit einprogrammiert. Am besten programmiert man nach dem Motto:  
 
Es sei hier bemerkt, dass innerhalb eines Interrupt z.B. kein delay() und millis() funktioniert. Sobald man etwas benutzt was auch Interrupts benutzt ist der Ärger mit einprogrammiert. Am besten programmiert man nach dem Motto:  
  
  Nur das Notwendigste erledigen und direkt wieder raus, d.h. am besten nur Variablen setzen und keine Unterroutinen  
+
  Nur das Notwendigste erledigen und direkt wieder raus, d.h. am besten nur Variablen setzen und keine Unterroutinen.
  
 
Wenn ich zeitkritische Routinen geschrieben habe, bin ich auch schon mal auf Inline-Assembler ausgewichen, habe Taktzyklen der Opcodes gezählt und die Gesamtlaufzeit durch I/O-Pin-Trigger auf einem Oszi nachgemessen:-)
 
Wenn ich zeitkritische Routinen geschrieben habe, bin ich auch schon mal auf Inline-Assembler ausgewichen, habe Taktzyklen der Opcodes gezählt und die Gesamtlaufzeit durch I/O-Pin-Trigger auf einem Oszi nachgemessen:-)
 
  
 
==PCINT0-23==
 
==PCINT0-23==

Version vom 10. März 2021, 12:26 Uhr

Externe Interrupts können im Prinzip über/von jedem I/O-Pin ausgelöst werden.

Zu beachten:

  • Ein Interrupt ist erst nach 4 Taktzyklen durch PCIF erkennbar. Bei 20 MHZ Takfrequenz sind das immerhin 200ns (4x50ns) oder 0,2us.
  • Nur INT0 und INT1 sind "vollwertige" Interrupts
  • Die 3 PCIx sind jeweils für eine Gruppe von I/O-Pins zuständig (PCI0 für PCINT7..0, PCI1 für PCINT14..8 , PCI2 für PCINT23..16).

INT1 INT0

  • Können auf fallende und steigende Flanke und 0-Pegel triggern (-> EICRA)
  • Bei der 0-Pegel wird der Triger solange ausgelöst bis wieder 1-Pegel anliegt.

An- und Ausschalten kann man die beiden Interrupt im EIMSK – External Interrupt Mask Register. Mask sollte vielleicht besser Enable heißen.

Das EICRA – External Interrupt Control Register A (0x69) dient ausschließlich (Interrupt Sense Control Bit0-3) der Triggereinstellung von INT0 und INT1

00 0-Pegel

01 Pegelwechsel

10 fallende Flanke

11 steigende Flanke

Anwendungsbeispiel

Immer mal wieder habe ich gelesen, dass ein Taster am Interrupt eine schlechte Idee sei. Wegen Tastenprellen und so. Manche behaupten sogar es ginge gar nicht. Hier der Gegenbeweis. Funktioniert bei mir völlig zufriedenstellend.

 1 #include <Arduino.h>
 2 #include <avr/interrupt.h> // fuer ISR Makro
 3 
 4 
 5 const byte ledPin = 5;
 6 volatile byte ledState = LOW;
 7 byte lastledState;
 8 
 9 const byte interruptPin = 3;
10 
11 ISR (INT1_vect){
12   EIFR|=(1<<INTF1); // Interrupt Flag zu Sicherheit schon mal loeschen
13   EIMSK&=~(1<<INT1); // Interrupt abschalten
14   ledState = !ledState;
15 }
16 
17 
18 void setup(){
19   Serial.begin(115200); // vielleicht brauche ich es 
20 
21   pinMode(ledPin, OUTPUT);  // LED-Anschluss auf Ausgang
22   digitalWrite(ledPin, LOW); // LED ausschalten
23 
24   pinMode(interruptPin, INPUT); // dies ist nicht zwingend notwendig, der Interrupt funktioniert auch im Output-Mode
25 
26   EICRA |= (1<<ISC11); // auf fallende Flanke reagieren (Der Taster tastet nach GND über einen Pullup-Widerstand.)
27   EIMSK |= (1<<INT1); // INT1-Interrupt einschalten
28 }
29 
30 void loop(){ 
31   if (lastledState!=ledState){
32     digitalWrite(ledPin, ledState);
33     delay(200);  // entprellen
34     EIFR|=(1<<INTF1); // Interruptflag (nochmal) loeschen
35     EIMSK|=(1<<INT1);  // Interrupt wieder einschalten
36     lastledState=ledState; // Veraenderung merken
37   }
38 }

Es sei hier bemerkt, dass innerhalb eines Interrupt z.B. kein delay() und millis() funktioniert. Sobald man etwas benutzt was auch Interrupts benutzt ist der Ärger mit einprogrammiert. Am besten programmiert man nach dem Motto:

Nur das Notwendigste erledigen und direkt wieder raus, d.h. am besten nur Variablen setzen und keine Unterroutinen. 

Wenn ich zeitkritische Routinen geschrieben habe, bin ich auch schon mal auf Inline-Assembler ausgewichen, habe Taktzyklen der Opcodes gezählt und die Gesamtlaufzeit durch I/O-Pin-Trigger auf einem Oszi nachgemessen:-)

PCINT0-23