Taster prellen und Interrupt

L I V E Stammtisch ab 20:30 Uhr im Chat
  • Hallo zusammen,
    ich habe folgendes Problem. Ich möchte eigentlich nur
    einen Taster abfragen und dazu eine LED ansteuern.

    Also Taster drücken -> LED an
    Taster noch einmal drücken -> LED aus.
    Taster drücken -> LED an
    usw.

    Als Programmiersprache benutze ich C++
    Ich möchte diese über einen Interrupt steuern.
    Muss man Software mäßig auch dafür sorgen, dass der Taster nicht prellt ?
    Oder macht das der Interrupt selber ?

    Sorry aber ich bin noch Laie auf dem Gebiet.

    Hier mal mein Code:

    #include <stdio.h>
    #include <wiringPi.h>

    #define LEUCHTDIODE 5 // wiringPi Pin 5
    #define TASTER 0 // wiringPi Pin 0
    #define PAUSE 500

    using namespace std;
    bool status=false;

    void interrupt_0(void)
    {
    printf("Interrupt ausgelöst");

    if (status==false)
    {
    digitalWrite(LEUCHTDIODE, LOW);
    cout << "LED aus" << endl;
    status=true;
    }
    else
    {
    digitalWrite(LEUCHTDIODE, HIGH);
    cout << "LED an" << endl;
    status=false;
    }
    }

    main()
    {
    wiringPiSetup();
    pinMode(TASTER, INPUT);
    pullUpDnControl(TASTER,PUD_DOWN);

    wiringPiISR (0, INT_EDGE_RISING, interrupt_0) ;

    for ( ; ; )
    {
    int waitForInterrupt(TASTER, -1);
    }
    }

    Der Aufbau ist:
    3,3V -> Taster
    Taster -> 1k
    1k -> Pin0

    Pin6 -> LED
    LED -> 560Ohm
    560 Ohm -> Masse

    Wenn ich auf den Taster drücke passiert nix.
    Drücke ich aber mehrmals kurz hintereinander drauf, so sehe ich auf der Konsole irgendwann:
    "Interrupt ausgelöst" viele male hintereinander stehen. Also irgendwie unkontrolliert.

    Hätte jamand mal etwas Code für mich ?
    Oder kann mir jemand dazu ein Tipp dazu geben ?
    Denn ich hänge hier schon Stunden vor dem Rechner und finde im Netz einfach nichts.
    Auch der Code unter:
    Sensor-Signal per Interrupt abfragen
    klappt bei mir nicht.

    Vielen Dank für Eure Hilfe

    Gruß
    Juppy

  • Nach allem was ich bis jetzt so gelesen habe ist hardwaremäßiges entprellen rein prinzipiell wesentlich besser, wenn man „einen Interrupt an den Schalter hängen“ will.
    (Das ist insofern klar, als man ja nicht will dass die Routine zB 5 Mal aufgerufen wird, wenn man den Taster nur einmal betätigt)


    Damit man anders als bei einem simplen Kondensator parallel zum Schalter immer einen eindeutig definierten Zustand am GPIO-Pin hat, kann man bei Umschalttastern ein R/S-FlipFlop oder bei einfachen Tastern den Kondensator, 2 Widerstände und einen Schmitt-Trigger verwenden. Es ist zwar etwas aufwändiger zu bauen, funktioniert aber zuverlässiger und die Kosten halten sich in sehr engen Grenzen.
    Die Schaltungen findest du hier:
    http://www.mikrocontroller.net/articles/Entprellung


    Mir ist die richtige hardwaremäßige Entprellung bis jetzt übrigens auch zu aufwändig gewesen und der simple parallel geschaltete Kondensator zu windig, weshalb ich beim Abfragen einfach auf Interrupts verzichte und in Software entprelle. Auf meinem Cubietruck schaffe ich in python selbst über das sysfs trotzdem genügend Abfragen pro Sekunde für einen Taster und das bei einer CPU-Last von wenigen % (ca. 6% für 8 Taster), macht man das statt über das sysfs mit rpi.GPIO oder WiringPi kann man die Rechenlast fast schon komplett vernachlässigen.

    • Offizieller Beitrag

    [code=php]
    //Software debouncing in Interrupt, by Delphiño K.M.

    long debouncing_time = 15; //Debouncing Time in Milliseconds
    volatile unsigned long last_micros;

    void setup() {
    Serial.begin(9600);
    attachInterrupt(0, debounceInterrupt1, CHANGE);
    attachInterrupt(1, debounceInterrupt2, CHANGE);
    }

    void loop() {
    }

    void debounceInterrupt1() {
    if((long)(micros() - last_micros) >= debouncing_time * 1000) {
    Interrupt1();
    last_micros = micros();
    }
    }
    void debounceInterrupt2() {
    if((long)(micros() - last_micros) >= debouncing_time * 1000) {
    Interrupt2();
    last_micros = micros();
    }
    }

    void Interrupt1() {
    Serial.print("btn_right");
    Serial.println();
    }
    void Interrupt2() {
    Serial.print("btn_left");
    Serial.println();
    }
    [/php]

    Hier mal der code der auf meinem Uno läuft, viielleicht hilft das schon.

  • Hmm
    sorry aber so fit bin ich da noch nicht drin.

    Ich benutze den PI und die wiringPI Lib.

    Könntest Du mir das bitte irgendwie ein wenig erklären was
    die einzelnen Befehle so treiben ?

    Sorry :daumendreh2:

    Gruß
    Juppy

    • Offizieller Beitrag

    Du scheinst dich ja an diesem langgehangelt zu haben.
    Am Ende machst du nichts weiter als zu prüfen wie viel Zeit zwischen den Tastendrücken vergangen ist. Mal ein verfachtes Beispiel in sekunden
    Das hier
    [code=php]
    void debounceInterrupt1() {
    if((long)(micros() - last_micros) >= debouncing_time * 1000) {
    Interrupt1();
    last_micros = micros();[/php]
    wird zu
    [code=php]
    #Beispielwerte
    micros = 5 #Das script läuft seit 5sek.
    last_micros = 0 #Da der Interuppt noch nicht ausgelöst wurde steht hier nix drin
    debouncing_time = 1 #1sek soll zwischen den Tastdrücken liegen
    void debounceInterrupt1() {
    if((5 - 0) >= 1) {
    Interrupt1(); #hier wird die Funktion aufgerufen die dann dann macht was du willst, gpios schalten usw.
    last_micros = aktuelle Laufzeit des Script in sek (z.B. 5,5);[/php]

    nehmen wir an du drückst den schalter nochmal (ganz schnell)
    [code=php]
    #Beispielwerte
    micros = 6 #Das script läuft seit 5sek.
    last_micros = 5,5 #diese zahl wurde nach dem ersten druck gespeichert
    debouncing_time = 1 #1sek soll zwischen den Tastdrücken liegen
    void debounceInterrupt1() {
    if((6 - 5,5) >= 1) {
    Interrupt1(); #hier wird die Funktion aufgerufen die dann dann macht was du willst, gpios schalten usw.
    last_micros = aktuelle Laufzeit des Script in sek;[/php]

    In diesem Fall passiert einfach nichts, weil 0,5 nicht grösser gleich 1 ist. Ob es nun micros() direkt in c/c++ weiß ich nicht, aber irgend eine ähnliche Funktion wird es geben.

  • Hi,
    hmm irgendwie verstehe ich Deinen Code nicht so ganz.

    Was mir nicht so ganz einleuchten will ist, wenn ich doch per Interrupt
    einen Port abfragen möchte, dann müßte ich doch im Vorfeld dafür sorgen, dass
    der Taster nicht prellt. Denn ansonsten würde der Interrupt doch zigmal
    ausgelöst werden. Oder liege ich da falsch ? :s

    Wenn dem so ist, dann nützt doch das entprellen Softwaremäßig gar nichts.
    Denn bei jedem Tastendruck wird doch der Interrupt ausgelöst ....:s

    Sorry aber ich bin wie gesagt auf dem Gebiet noch Laie.

    Gruß
    Juppy

  • Servus Juppy,

    ich hoffe, ich bin mit meiner Antwort noch nicht zu spät dran, aber es sind ja noch Fragen von Dir offen:

    Das Entprellen per Software hilft sehr wohl! Tatsächlich tritt - wie Du richtig erkennst - der Interrupt bei jeder Pegeländerung am GPIO des Tasters auf, somit relativ häufig während der Prellphase des Tasters! Die Interrupt-Routine bzw. -Routinen werden auch tatsächlich jedes Mal aufgerufen, aber sie vollziehen die Aktion des Tastendrucks erst nach Ablauf der Prellzeit!

    Wie schon dbv in seinem Codebeispiel zeigt, besteht der Witz bei der softwaremäßigen Entprellung darin, am Anfang der Interruptroutine in einer durch den Prozessor schnell abgearbeiteten if-Abfrage zu überprüfen, ob eine definierte Prellzeit bereits verstrichen ist oder noch nicht: Wenn die Prellzeit noch nicht um ist, wird die Interruptroutine sofort wieder verlassen.
    Nur bei verstrichener Prellzeit wird die eigentliche Behandlungsroutine für den Tastendruck dann einmalig ausgeführt. So werden ungewollte Doppelaufrufe durch das Prellen vermieden.

    Die anfangs häufigen Aufrufe der Interruptroutine während des Prellens fallen auch von der CPU-Leistung her fast nicht ins Gewicht, da eben die Routine durch den if-Vergleich sofort verlassen wird.

    Die notwendige Dauer für die Prellzeit hängt vom verwendeten Taster ab, 100ms dürften fast immer reichen. Bedenke, in C/C++ reden wir von Befehlszeiten im Millisekundenbreich.
    Größere Zeiten im Sekundenbereich für die Prelldauer gäben in diesem Zuge so nebenbei eine Art Mindestwartezeit zwischen zwei Tastenbetätigungen vor. Bis zu einem gewissen Grad kann man das je nach Anwendung durchaus verwenden, je nachdem wie lange die Reaktion auf den Tastendruck zu ihrer Ausführung braucht.

    ich hoffe, Dir damit die Funktionsweise des softwaremäßigen Entprellens einigermaßen verständlich erklärt zu haben.

    Viele Grüße,
    Peter


  • Servus Juppy,

    ich hoffe, ich bin mit meiner Antwort noch nicht zu spät dran, aber es sind ja noch Fragen von Dir offen:

    Das Entprellen per Software hilft sehr wohl! Tatsächlich tritt - wie Du richtig erkennst - der Interrupt bei jeder Pegeländerung am GPIO des Tasters auf, somit relativ häufig während der Prellphase des Tasters! Die Interrupt-Routine bzw. -Routinen werden auch tatsächlich jedes Mal aufgerufen, aber sie vollziehen die Aktion des Tastendrucks erst nach Ablauf der Prellzeit!

    Hallo,
    also vom Prinzip her denke ich habe ich das verstanden. Nur was ich mich jetzt wiederum
    Frage, Du schreibst "Die Interrupt-Routine bzw. -Routinen werden auch tatsächlich jedes Mal aufgerufen, aber sie vollziehen die Aktion des Tastendrucks erst nach Ablauf der Prellzeit!"
    Woher weiss die Interrupt Routine, wann der Prellvorgang vorbei ist ?
    Das hieße doch, das ich im Interruptprogramm immer folgendes abfragen muss:
    Ist der Pegel innerhalb einer gewissen Zeit konstant ?
    Wenn ja, dann führe den Int Code aus.
    Aber soll in dem eigentlichen Intteruptprogramm nicht so wenig wie möglich Code
    stehen ?

    Hättest Du evt. mal ein Beispiel für mich, wie so etwas aussehen könnte ?

    Ich habe es erstmal so gelöst. Bin aber für Verbesserungen offen:

    Einmal editiert, zuletzt von dbv (10. Februar 2015 um 20:17)


  • na ich hoffe doch, dass wir hier noch weit unterhalb des Mikrosekundenbereichs bleiben ...

    aber sonst: ganz gut beschrieben ... :thumbs1:
    cu,
    -ds-

    Hallo dreamshader,

    da hast Du natürlich völlig recht! Solche Fehler passieren halt, wenn man Text so dahinschreibt :lol:

    Es ist jedenfalls schön, wenn andere Leute die Einträge kritisch gegenlesen und konstruktiv kommentieren. Danke!

    Peter

    • Offizieller Beitrag

    Juppy
    jetzt hast du dir nen Interrupt gebaut der nichts weiter macht als ne globale Variable setzen. Der Rest des Scripts rattert weiter endlos über die Variable. Verleg das schalten der LED in den Interrupt und speicher in ner varibale den Status selbiger.

  • Servus Juppy,

    der von Dir zuletzt gepostete Code funktioniert wohl wie erwartet, wenn die Abfrage des Reed-Kontaktes "Weiche1_Reed1" und das Schalten von "LEUCHTDIODE_Rot" mehr oder weniger die einzige Aufgabe des Programms ist.

    Das Problem ist aber, dass
    1.
    in der Interrupt-Routine nur eine bool-Variable gesetzt wird, die den registrierten Tastendruck signalisiert und ansonsten dort keine Aktion stattfindet.
    2.
    in der while-Schleife wird nun diese Signalvariable wieder ständig abgefragt (gepollt), eine Sache, die man ja mit der Verwendung des Interruptes eigentlich vermeiden will.
    Erschwerend kommt hinzu, dass während des Befehls "delay(PAUSE)" für die Dauer der definierten PAUSE (2000ms?) die gesamte Abarbeitung in der Schleife steht. Wenigstens verbraten Befehle wie "delay" oder "sleep" keine Prozessorleistung und das Betriebssystem kann währenddessen die anderen Threads bedienen :)

    Zu meinem C-Code unten:
    Da ich das Paket mit meinen Raspberry Pi erst Anfang Februar erhielt, habe ich noch nicht viel damit angestellt, außer XBMC in Betrieb genommen ;) Mir fehlen leider noch ein Netzteil mit genügend Leistung und ein paar andere Sachen für einen vernünftigen Betrieb des Raspberry Pi...
    Beruflich programmiere ich unter Windows mit .NET (Visual Basic und C#). Meine C/C++- sowie Unix/Linux-Kenntnisse liegen seit ca. 20 Jahren brach, somit kann ich Dir nur ein ungeprüftes C-Codegerüst bieten, das im Übrigen dem Code von dbv ähnelt, aber sich vermutlich nach dem Rüberkopieren nicht sofort compilieren lässt:

    Ich hoffe, dieses Codegerüst ist Dir eine kleine Hilfe.

    Viele Grüße
    Schlizbäda

  • Hi schlizbäda,
    sorry das ich mich jetzt erst melde. Normalerweise bekomme ich eine Mail
    wenn ein neuer Beitrag eingestellt wurde. Hmm hab ich wohl vergessen
    den Haken zu setzen :daumendreh2:

    Erstmal danke für Deine Hinweise und den dazugehörigen Code.
    Ich werde mir den Code mal in Ruhe angucken und versuchen ihn zu verstehen
    und ihn dann umzusetzen. Ich werd mich auf jeden Fall dazu noch mal melden.

    Wollte Dir halt nur Bescheid geben. Nicht das Du meinst, toll jetzt habe ich hier
    was geschrieben und jetzt meldet er sich nicht mehr dazu.

    Also, nochmal vielen Dank.

    Gruß
    Juppy :shy:

  • Hallo zusammen
    ich hab da noch so eine Idee für das entprellen in C,
    bin nicht der C Profi scheint aber zu funktionieren

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!