Ich versuche, Pinwechsel-Interrupts zu verwenden, um gedrückte Tasten zu erkennen. Bis jetzt habe ich noch nie mit solchen Interrupts gearbeitet und es gibt einige Probleme. Deshalb möchte ich sicherstellen, dass dies die richtige Verwendung ist.
Wenn ich das Datenblatt richtig verstanden habe, müssen die folgenden Schritte ausgeführt werden, um einen Pin-Wechsel-Interrupt zu verwenden:
- Legen Sie im PCMSK-Register fest, welche PINs Sie steuern möchten
- Aktivieren Sie das PIN-Register für die Pinwechsel-Interrupt-Steuerung (PCICR).
- Interrupts aktivieren
- Verwenden Sie den entsprechenden Interrupt-Vektor
Projekt: Einfache Stimmungslampe, Farben über 4 Tasten gesteuert.
Installieren:
- Atmega168A-PU
- 4 Mini-Druckschalter
- MOSFETS zur Steuerung meiner 3 Watt RGB LED
Hier ist der Code, den ich verwende und der nicht wie erwartet funktioniert:
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BUTTON1 (1<<PC5)
#define BUTTON2 (1<<PC4)
#define BUTTON3 (1<<PC3)
#define BUTTON4 (1<<PC2)
#define GREEN (1<<PB1)
#define BLUE (1<<PB2)
#define RED (1<<PB3)
void init() {
// enable LED
DDRB |= GREEN;
DDRB |= BLUE;
DDRB |= RED;
// button pullups
PORTC |= BUTTON1;
PORTC |= BUTTON2;
PORTC |= BUTTON3;
PORTC |= BUTTON4;
// pin change interrupts for buttons
PCMSK1 |= PCINT13;
PCMSK1 |= PCINT12;
PCMSK1 |= PCINT11;
PCMSK1 |= PCINT10;
// enable pin change for buttons
PCICR |= PCIE2;
sei();
}
ISR(PCINT2_vect) {
PORTB = BLUE;
}
void ledTest() {
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
}
int main() {
init();
ledTest();
_delay_ms(500);
PORTB |= GREEN;
while(1) {
_delay_ms(100);
}
}
Hinweis: Die Tasten sollten entprellt werden. Da ich dies Schritt für Schritt versuche und es für das Einschalten der LED keine Rolle spielen sollte, habe ich es hier ignoriert.
Frage: Ist die Art und Weise, wie ich versuche, die Interrupts zu verwenden, korrekt?
Probleme mit meinem Setup:
- Die Schaltflächen 1-3 werden vollständig ignoriert.
- Button4 löst einen Reset der Atmosphäre aus
Dinge, die ich überprüft habe:
- Tasten sind in keiner Weise mit der Reset-PIN verbunden
- Die Tasten sind ordnungsgemäß mit GND verbunden, wenn sie gedrückt werden
- Tasten sind nicht mit GND verbunden, wenn sie nicht gedrückt werden
Schaltflächen funktionieren gut, wenn ich sie ohne Unterbrechung verwende, z.
if (! (PINC & BUTTON4)) {PORTB ^ = BLUE; }}
- 16 MHz externer Kristall / interner Kristall
- Fehler im Routing
- Ich benutze einen 100nF Kondensator zwischen PWR und GND auf der Atmosphäre
- VCC (7), GND (8), GND (22), AVCC (20) sind verbunden (da ich AREF nicht benötige, ist es nicht verbunden)
quelle
Antworten:
Pinwechsel-Interrupts sind normalerweise keine gute Möglichkeit, Tastenaktionen zu erkennen. Dies liegt daran, dass mechanische Tasten abprallen und Sie viele bedeutungslose Interrupts erhalten, und dann müssen Sie trotzdem noch entprellen.
Ein besserer Weg ist ein periodischer Interrupt, wie alle 1 ms (1 kHz Rate). Das ist auf den meisten Prozessoren eine lange Zeit, daher ist der Anteil der Zeit, die für den Interrupt aufgewendet wird, gering. Probieren Sie einfach den Schaltflächenstatus bei jedem Interrupt aus. Deklarieren Sie einen neuen Schaltflächenstatus, wenn Sie den neuen Status 50 ms hintereinander gesehen haben. 50 ms sind länger als die meisten Tasten, aber immer noch kurz genug, damit Menschen die Verzögerung nicht bemerken oder sich nicht darum kümmern.
Beachten Sie, dass Sie auf diese Weise auch mehrere Tasten in demselben periodischen 1-ms-Interrupt behandeln können. Sie benötigen lediglich einen Zähler für jede Schaltfläche.
Mehr zur Entprellzeit:
Gelegentlich, wie in diesem Fall, sagt jemand, dass 50 ms eine zu lange Entprellzeit sind. Dies gilt nicht für gewöhnliche Tasten, die von Menschen gedrückt werden. Es mag vielleicht ein Problem in sehr zeitkritischen Anwendungen wie einer Stoppuhr sein, aber bisher bin ich noch nicht auf eine gestoßen. Ich habe dies in den frühen 1980ern getestet, und viele andere Leute haben es auch.
Es ist wahr, dass die typische Druckknopf-Sprungzeit etwa 10 ms beträgt, wobei sich fast alle um 25 ms einstellen. Der begrenzende Faktor für die Entprellzeit ist die menschliche Wahrnehmung. 50 ms sind etwas kürzer als dort, wo Leute eine Verzögerung bemerken, wenn sie nicht danach suchen. Selbst dann dauert es viel länger, bis es nervt. In einigen Fällen kann es für einen Menschen möglich sein, einen Unterschied zwischen 50 ms und 0 ms Verzögerung zu erkennen, wenn er speziell danach sucht. Dies unterscheidet sich jedoch erheblich davon, einen Knopf zu drücken und zu sehen, dass etwas passiert, und nicht an die Verzögerung zu denken.
50 ms ist daher eine gute Entprellzeit, da die Verzögerung bei normalen Anwendungen unter der Wahrnehmungsgrenze, weit unter der Störgrenze und weit über der Absprungzeit der meisten Schalter liegt. Ich habe Schalter gefunden, die fast so lange abprallten, also können Sie genauso gut an die Wahrnehmungsgrenze gehen, da es nichts zu verlieren gibt.
Ich habe viele Produkte mit von der Firmware entprellten Tasten mit einer Entprellzeit von 50 ms hergestellt. Nicht ein einziges Mal erwähnte ein Kunde eine Verzögerung. Sie alle akzeptierten, dass die Tasten ohne Probleme einwandfrei funktionierten.
quelle
Pinwechsel-Interrupts sind eine bessere Möglichkeit zum Entprellen als das Abrufen. Der Interrupt durchläuft normalerweise eine Logik wie ein D-Flip-Flop oder einen D-Latch. Obwohl dies zutrifft, ist es schwieriger, diese Entprellroutine mit Compilern höherer Ebene zu implementieren. Sobald der Interrupt auftritt, wird das Interrupt-Flag nicht gelöscht und die Interrupt-Freigabe wird gelöscht, bis eine Verzögerung aufgetreten ist. Sobald die Verzögerung aufgetreten ist, wird der Zustand des Pins überprüft. Wenn er sich noch in dem Zustand befindet, der den Interrupt ausgelöst hat, wird der Zustand der Taste geändert und das Interrupt-Flag gelöscht und die Interrupt-Freigabe gesetzt. Wenn nicht in dem Zustand, der den Initiator verursacht hat, wird die Interrupt-Freigabe gesetzt und der Zustand bleibt gleich. Dies macht den Prozessor für andere Aufgaben frei. Regelmäßige Unterbrechungen verschwenden Zeit im Programm.
quelle
Falsch. PC INT ist die beste Option. Wenn Sie den Status einer Schaltfläche mithilfe der Abfrage überprüfen, wird die meiste Zeit nichts ausgeführt. Sie verschwenden viel wertvolle CPU-Zeit. Mit PC INT können Aktionen nur auf Anfrage ausgeführt werden.
Richtig beim Hüpfen. Sie sollten jedoch NIEMALS eine Taste / einen Schalter innerhalb einer Interruptroutine entprellen (gleicher Grund: Verschwendung von CPU-Zeit). ISRs sollen in Bezug auf den Code sehr kurz und effizient sein. Verwenden Sie einfach das Hardware-Debouncing. Halten Sie Ihre Software sauber!
Hardware-Debouncing ist bequemer, siehe hier / RC-Debouncing + Schmitt-Trigger als Referenz. Ich habe es unzählige Male mit PC INT verwendet, es ist nie fehlgeschlagen.
Ja, Sie können (und sollten) PC INT verwenden, um einen Schaltflächenstatus zu erhalten. Sie müssen aber auch die richtige Hardware-Entprellung verwenden.
quelle