Arduino Uno: Pin-Interrupt funktioniert nicht

Hallo Leute.

Ich wollte für einen Sketch einen Pin-Interrupt nutzen, der jedoch einfach nicht gehen will. Ich bin mir sicher dass das Problem zwischen den Ohren liegt, aber ich sehe es gerade einfach nicht.

Da der Interrupt in meinem richtigen Sketch nicht funktioniert hat, war der erste Schritt, dass ich den Sketch "Blink" aus der Arduino-Beispiel-Bibliothek entsprechend mit dem Interrupt modifiziert, um dadurch das Problem zu finden, aber ohne Erfolg.

Sowohl im folgenden Test-Sketch, als auch in meinem richtigen Sketch, soll folgendes passieren:
Nachdem ein Taster gedrückt wurde, der an Pin 2 des Arduino Uno hängt und am anderen Ende mit Masse verbunden ist, soll die Interrupt-Service-Routine ausgeführt werden, die den eingestellten Wert am Potentiometer (an Pin A1) ausließt und dieser wird dann entsprechend weiter genutzt. Im Beispiel-Programm soll einfach das Delay, das für das Blinken der LED zuständig ist, auf den, am Potentiometer ausgelesenen Wert (0 bis 1024) gesetzt werden, und dadurch die "Blink-Frequenz" der LED ändern.
Natürlich macht der Interrupt hier keinen Sinn, in meinem richtigen Sketch benötige ich ihn aber.

Ich habe den Interrupt direkt nach dem Arduino-Handbuch, durch setzen der Bits in den entsprechenden Registern erstellt (Diese sind im Handbuch des Atmega328P auf den Seiten 56 und 57 zu finden), jedoch wird dies in vielen Beispielen, die ich im Internet finde genau so gemacht.

Hier der Code des Test-Sketches:

//Variable für "Pause-Wert" von Poti
volatile int pause = 0 ;

//Setzen der Pins
//Pin 2 ist der Interrupt-Pin und wird auf High gesetzt
//Bei drücken des Tasters wird er auf Masse gezogen und dadurch ein "Pin-Change" herbeigeführt
void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(A1, INPUT);
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);  
  Setup_PIN_Change_Interrupt();
}

//Periodisches Blinken der On-Board-LED (Pin 13 oder "L")
void loop() {
  digitalWrite(13, HIGH);  // LED an
  delay(pause);                  // Pause
  digitalWrite(13, LOW);   // LED aus
  delay(pause);                  // Pause
}

//Setzen der entsprechenden Register zur Einstellung des Interrupts
void Setup_PIN_Change_Interrupt()
{
  //Pin 2 für Pin-Change-Interrupt aktivieren
  PCIFR = 0; //Interrupt-Flag-Register zurücksetzen
  PCICR = PCICR | (1 << PCIE0); //Interrupt für Gruppe: Pin 0 bis 7 aktivieren
  PCMSK0 = PCMSK0 | (1 << PCINT2); //Interrupt speziell für Pin 2 aktivieren
}

//Interrupt Service Routine die bei Auslösen des Interrupts ausgeführt werden soll
ISR(PCINT0_vect)
{
  pause = analogRead(A1); //Pause zwischen Leuchten der LED auf Poti-Wert setzen
}

Der entsprechende Test-Sketch ist zusätzlich als Datei angehängt.

Wenn jemand den Bock sieht, ich finde ihn auf jeden Fall grade nicht.

Freundliche Grüße

Arduino_Poti_Test.ino (1.19 KB)

Warum sind in deinem (Ziel) Programm Interrupts nötig?

Hallo combie,

es gäbe auch Möglichkeiten, das ganze ohne einen Interrupt zu machen (z.B. einfach periodisch, nachdem ein bestimmter counter-Wert erreicht ist (for schleife o.ä.) das Poti abzufragen etc.).

Da ich jedoch Interrupts etwas besser kennen lernen will, möchte ich gerne einen Pin-Interrupt zum laufen bringen (ob der an dieser Stelle sinnvoll ist, ist für mich erstmal nicht relevant).
Und ich komme gerade einfach nicht darauf, warum dieser kleine code-schnipsel nicht funktionieren will.

Ich habe schon das ganze ohne den Interrupt getestet, dann funktioniert alles (also die Einstellung der Blink-Frequenz per Poti), an einem falsch aufgebauten Stromkreis, bzw. defekten Bauteilen kann es also schomal nicht liegen.

Das einzige was ich mir noch vorstellen könnte wäre eine falsche Pin-Belegung, diese habe ich aber schon mehrfach nachkontrolliert.

Gruß

PCINT0_vect ist für Pin 8 bis 13 zuständig.
Gilt ebenso für PCMSK0
Bitte nochmal Datenblatt lesen.

Hallo nochmal,

Bist du dir da sicher ?

Beispielsweise in diesem Tutorial steht es genau anders herum, also ISR(PCINT0_vect) wäre für Port B, PCINT0 - PCINT7 zuständig.

Trotzdem danke für den Hinweis, ich werde es nochmal genau nachschauen.

Gruß

Hallo nochmal, ich habe jetzt folgendes herausgefunden:

Der Interrupt scheint irgendwie zu gehen, aber er macht genau das NICHT, was er eigentlich tun soll, nämlich auf den Pin-Change an Pin 2 zu reagieren.

Ich meine das wie folgt:
Wenn ich als Startwert für die pause-Variable einen bestimmten Wert einstelle, z.B. 3000, und an dem Poti ein Wert von (entsprechend in integer umgewandelt) 500 eingestellt ist, und ich anschließend den sketch uploade, passiert Folgendes:

Für ca. 6-7 Sekunden blinkt die LED langsam entsprechend dem vor-definierten Wert von 3000, dann fängt die LED plötzlich an, in dem am Poti eingestellten Wert von 500 (also viel schneller zu blinken) ohne dass der Taster gedrückt wurde.
Drehe ich an dem Poti (ohne davor oder danach den Taster zu drücken) wechselt die Blink-Frequenz der LED sofort auf den Poti-Wert, also blinkt schneller bzw. langsamer.
Ein drücken des Tasters (egal wann) ändert an diesem Vorgang garnichts.

Ich dachte zuerst dass der Sketch den Interrupt quasi komplett ignoriert, das stimmt aber auch nicht, denn wenn ich den Code in inder Interrupt-Service-Routine auskommentiere, blinkt die LED permanent in der bei der Initialisierung vorgegebenen Frequenz von 3000, und nimmt nicht den Wert des Potis an.

Die ISR updatet also permanent die pause-Variable, ohne das Drücken des Tasters zu berücksichtigen.

Das mit dem PCINT0 muss übrigens stimmen, denn wenn ich da 1 oder 2 eintrage, reagiert der Sketch ebenfalls nichtmehr auf das Poti.

Gruß

Wie hast Du denn den Taster angeschlossen? PullUp-/PullDown dran? Sonst hast Du eine prima Antenne, die alles unmögliche aufnimmt.

Gruß Tommy

Rabobsel:
Beispielsweise in diesem Tutorial steht es genau anders herum, also ISR(PCINT0_vect) wäre für Port B, PCINT0 - PCINT7 zuständig.

Dir ist aber schon klar, dass die Arduino-Pin Numerierung etwas anderes ist als die Numerierung im Datenblatt? PortB Bit0...Bit5 ( entspricht PCINT0...PCINT5 ) sind beim Arduino die Pins 8-13. Das ist das, was dir Combie gesagt hat.
Pin2 am Arduino entspricht PCINT18 ( PortD, Bit2 ). Und da könntest du auch gleich den INT0 verwenden, den Du direkt mit der Arduino-Interruptfunktion nutzen kannst.

Hallo nochmal,

Tommy56:
Wie hast Du denn den Taster angeschlossen? PullUp-/PullDown dran? Sonst hast Du eine prima Antenne, die alles unmögliche aufnimmt.

Gruß Tommy

Also der Taster Pin wird ja mit High initialisiert und wird dadurch vom internen Pull-Up hochgezogen. Bei gedrücktem Taster geht es über einen externen Pull-Up nach Masse (soweit richtig ?).

MicroBahner:
Dir ist aber schon klar, dass die Arduino-Pin Numerierung etwas anderes ist als die Numerierung im Datenblatt? PortB Bit0...Bit5 ( entspricht PCINT0...PCINT5 ) sind beim Arduino die Pins 8-13. Das ist das, was dir Combie gesagt hat.
Pin2 am Arduino entspricht PCINT18 ( PortD, Bit2 ). Und da könntest du auch gleich den INT0 verwenden, den Du direkt mit der Arduino-Interruptfunktion nutzen kannst.

Im Internet finden sich widersprüchliche Informationen, was die Pin-Belegung angeht. Ich habe jetzt noch folgende Liste gefunden, ist diese soweit zutreffend ?
Falls ja, dann liegt der Interrupt bei mit definitiv Falsch, aber umso mehr wundert es mich dann, dass der Interrupt ja etwas tut (nur halt nicht das Richtige).

Irgendwo habe ich noch überflogen, dass man bei den Pin-Change Interrupts noch einstellen muss, ob der Interrupt auf steigende oder fallende Flanke reagieren soll, allerdings finde ich im Manual kein Register, wo man das einstellen kann. Oder ist das eher eine Option aber kein Muss ?

Gute Nacht

Hallo,

IDE Hilfe /Reference

da steht eigendlich alles drin

allerdings finde ich im Manual kein Register, wo man das einstellen kann.

Doch, das Datenblatt, gibt Auskunft.

Oder ist das eher eine Option aber kein Muss ?

Auch optionale Einstellungen, haben einen Ort.

Rabobsel:
Ich habe jetzt noch folgende Liste gefunden, ist diese soweit zutreffend ?
Falls ja, dann liegt der Interrupt bei mit definitiv Falsch, aber umso mehr wundert es mich dann, dass der Interrupt ja etwas tut (nur halt nicht das Richtige).

Die Liste scheint auf den ersten Blick ok zu sein. Du findest die Info aber auch auf arduino.cc wenn Du nach dem Stichwort 'pin mapping' suchst.
Da der Interrupt jetzt bei Dir auf einem Pin liegt, den Du nicht initiiert hast ( Standard: Input ohne Pullup ) dürfte der alles möglich auffangen.

Hallo nochmal,

Also erstmal, der Interrupt funktioniert nun, das Problem lag tatsächlich an falscher Pin-Belegung meinerseits, also ihr habt recht gehabt.
Im Handbuch findet sich auch ein Bild der SMD-Version des Atmega 328P bei dem man die richtigen Pin-Nummern, ablesen kann, man muss es nur erstmal finden.

Erstmal danke dafür.

combie:
Doch, das Datenblatt, gibt Auskunft.
Auch optionale Einstellungen, haben einen Ort.

Also wenn ich das Datenblatt richtig interpretiere, gibt es External-Interrupts und Pin-Change-Interrupts.
Die Pin-Change-Interrupts funktionieren mit jedem Pin, reagieren aber auf jede Zustandsänderung und das kann auch nicht weiter spezifiziert werden.
Die External-Interrupts gehen nur an den Pins INT0 und INT1, aber hier kann über das EIMSK(External Interrupt Mask Register) eingestellt werden, ob sie auf steigende oder fallende Flanke, oder Dauerhaft Low (bei manchen Atmegas auch dauerhaft HIGH) reagieren sollen.
Soweit korrekt ?

Eine weitere Frage hätte ich noch:

Um den Taster provisorisch zu entprellen, habe ich einen Widerstand + Kondensator (100nF) parallel zum Pin Eingang und am anderen Ende nach Masse verbunden. Den Widerstand habe ich entsprechend der Aufladeformel für den Kondensator (umgestellt) und dem Wert des internen Pullups (laut Datenblatt 60kOhm) dimensioniert.

In dieser Konfiguration funktioniert die Entprellung jedoch nicht.
Auch deutlich höhere oder niedrigere Widerstände brachten keine Änderung.

Verwende ich aber nur den 100nF-Kondensator ohne den Widerstand, geht die Entprellung plötzlich.

Ich verstehe grade nicht so ganz woran das liegt, hat da jemand eine Erklärung ?

Gruß

Soweit korrekt ?

Sieht so aus!

Um den Taster provisorisch zu entprellen, habe ich einen Widerstand + Kondensator (100nF) parallel zum Pin Eingang und am anderen Ende nach Masse verbunden. Den Widerstand habe ich entsprechend der Aufladeformel für den Kondensator (umgestellt) und dem Wert des internen Pullups (laut Datenblatt 60kOhm) dimensioniert.

In dieser Konfiguration funktioniert die Entprellung jedoch nicht.
Auch deutlich höhere oder niedrigere Widerstände brachten keine Änderung.

Verwende ich aber nur den 100nF-Kondensator ohne den Widerstand, geht die Entprellung plötzlich.

Ich verstehe grade nicht so ganz woran das liegt, hat da jemand eine Erklärung ?

Nein!
Ich kann deiner Prosa nicht entnehmen, was du jetzt wirklich getan hast.

Ein Schaltplan wäre deutlich aussagekräftiger.

Tipp:
Papier Bleistift Händikamera

Ok also ich habe mal eine kleine Skizze beigefügt.

Kurze Erklärung was das ganze soll:

Der Sinn des Kondensators ist, dass die "kurzzeitigen HIGH-Zustände" beim Prellen es nicht schaffen, den max. Pegel des HIGH-Zustands anzunehmen, da das durch die Kondensator-Aufladung verzögert wird.

Der Sinn des Widerstandes ist es die Aufladezeit des Kondensators (Zeitkonstante(Tau) = R * C) und damit die Entprellzeit in etwa auf 10ms einzustellen.

Eine detailliertere Erklärung und Vorgehensweise zur Dimensionierung des Widerstandes findet sich hier.

Das Problem ist nun, egal ob ich den Widerstand genau dimensioniere, oder einen etwas größeren/kleineren oder einen deutlich größeren/kleineren Widerstand nehme, der Taster prellt trotzdem.
Nehme ich aber nur den Kondensator und lasse den Widerstand einfach weg, funktioniert die Entprellung.

Im Prinzip liegt, selbst ohne zusätzliches R, der interne Pull-Up (der laut Datenblatt des Atmega 328P zwischen 20 und 50 kOhm beträgt (bin etwas verwirrt, dass hier eine so große Spannweite angegeben wird und kein eindeutiger Wert)) ja auch noch in Reihe mit dem Kondensator und erzeugt damit ein bestimmtes Tau, aber wenn das Tau etwas größer/kleiner wird (durch den zusätzlichen Widerstand), dürfte das an der Grund-Funktionsweise eigentlich nichts ändern, nur eine deutliche Vergrößerung/Verkleinerung müsste etwas bewirken.

Jedoch funktioniert es nur komplett ohne zusätzlichen Widerstand und das verstehe ich nicht ganz.

Tasterentprellung.png

Hi,

wenn Dein R = 100 Ohm ist, entlädt sich der C (100nF) in ca. 9µs auf 2V >> LOW Pegel, also quasi sofort, die 100 Ohm sind nur Strombegrenzung des Entladestromes auf max. 50mA Spitze. Das aufladen von 0 auf 3V >> HIGH Pegel dauert aber 1.8ms - 4.5ms, tut also was es soll. Nimmst Du aber höhere Werte für R, dann geht das nach hinten los.

Gruß André