Richtige Verwendung eines Pinwechsel-Interrupts


10

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:

  1. Legen Sie im PCMSK-Register fest, welche PINs Sie steuern möchten
  2. Aktivieren Sie das PIN-Register für die Pinwechsel-Interrupt-Steuerung (PCICR).
  3. Interrupts aktivieren
  4. 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)

Sie benötigen das PCIE1-Flag (nicht PCIE2) und PCINT1_vect (nicht PCINT2)
microtherion

Warum PCIE1? Ich verwende das C-Register. Wenn ich also zähle, wäre es A (PCIE0), B (PCIE1), C (PCIE2)? Wie auch immer, ich habe es mit PCIE1 und PCINT1_vect versucht und es gibt keine Reaktion, wenn ich die Tasten drücke.
Echox

1
Es kann ein bisschen riskant sein, bei solchen Aufgaben eine Orthogonalität anzunehmen. In diesem speziellen Fall wären Sie fast korrekt, außer dass der ATmega168 keinen Port A hat. Auf jeden Fall habe ich das Datenblatt und die Pinbelegung durchgesehen. Ein weiterer Hinweis war, dass Sie PCIE2 verwendeten, aber in PCMSK1 Bits setzten. das kann unmöglich richtig sein (Leider weiß ich nicht, warum Ihre überarbeitete Skizze immer noch nicht funktioniert).
Microtherion

Danke, ich verstehe auch, dass die Kombination von Debugging-Software, die von selbst erstellter Hardware abhängt, nicht so einfach ist ;-)
echox

Antworten:


14

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.


1
50 ms können für einige Fälle zu lang sein (normalerweise sind 10 bis 20 ms die Grenze der menschlichen Wahrnehmung, und das sollte zum Entprellen ausreichen), aber die hier beschriebene Methode ist der richtige Weg.
Laszlo Valko

1
@ Laszlo: Nein, 50 ms sind nicht zu lang für den normalen Fall. Siehe Ergänzung zu meiner Antwort.
Olin Lathrop

Ich habe 50ms ausprobiert, was für mich gut funktioniert :-) Ich bin immer noch neugierig, warum der Pinwechsel-Interrupt nicht funktioniert (neben dem springenden Zeug), aber das funktioniert :-) Danke.
Echox

1

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.


-1

"Pinwechsel-Interrupts sind normalerweise keine gute Möglichkeit, Tastenaktionen zu erkennen."

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.

"Das liegt daran, dass mechanische Tasten abprallen und Sie viele bedeutungslose Interrupts erhalten, und dann müssen Sie trotzdem noch entprellen."

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.


2
Das Entprellen von Software ist ein gültiger Ansatz, und meistens ist der geringe zusätzliche CPU-Overhead irrelevant. Zu sagen, dass Sie normalerweise in Hardware entprellen sollten, ist bestenfalls fraglich. Zu sagen, dass Sie in jedem Fall Hardware-Debouncing verwenden müssen, ist einfach falsch.
Olin Lathrop

In den meisten Anwendungen läuft der Controller ohnehin die meiste Zeit im Leerlauf und führt die Hauptschleife aus. Außerdem ist die CPU-Zeit, die erforderlich ist, um eine E / A-Statusprüfung durchzuführen und möglicherweise eine Variable zu erhöhen, minimal. Die Implementierung eines einfachen Entprellens in Software ist nahezu "kostenlos", Hardware kostet Geld. Und lachen Sie nicht über ein paar Cent, die Montage kostet auch Geld und wenn Sie mittlere bis hohe Mengen eines Produkts verwenden, ist dies nicht zu vernachlässigen. Es ist wahr, dass die ISR-Zeit kurz gehalten werden sollte, aber das ist in diesem Fall kaum ein Argument. Es ist wahrscheinlich kritischer, wenn der PC INT ISR aufgrund von Bouncen 50 Mal hintereinander ausgelöst wird.
Rev1.0

@ Nelson, "Verschwendung von CPU-Zeit" ist in einigen Anwendungen wichtig und in vielen anderen nicht. Sie sollten Ihre Antwort für Situationen qualifizieren, in denen die CPU-Zeit kritisch ist.
Benutzer1139880
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.