Interrupts funktionieren zum Teil nicht

  • Hallo,

    erstmal danke für die Mühe die sich einige mit Hilfestellungen machen. Normalerweise bringt mich die Suche hier immer an das gewünschte Ziel. Ich habe allerdings gerade ein Problem aus dem ich nicht ganz schlau werde. Ich habe das Programm schon auf das notwendigste reduziert.

    So funktionieren es und der Merker wird wenn ich den Taster drücke auf 1 gesetzt und wenn ich den Taster los lasse auf 0

    Ausgabe

    Jetzt füge ich zwei Zeilen Code in den Interrupt ein.
    Wie man sieht, ist die Ausgabe des Merkers jetzt immer 0. Sporadisch (ca. jedes 10-20 mal ist er 1)



    Woran kann das liegen? Ein ähnliches Verhalten bekomme ich auch ohne die Print-Befehle, wenn ich z.B. mehr Interrups hinzufüge.

    Edit: Pullup-widerstände sind extern verbaut und die Eingänge sind negiert.

    Einmal editiert, zuletzt von Dawa (17. April 2017 um 12:31)

  • Hallo Dawa,

    in einer Interrupt-Service-Routine (ISR) haben Ausgaben auf den Bildschirm oder Speichern in Dateien oder ähnliche langwierige Sachen nichts zu suchen.

    An Deinem zweiten Code kannst Du erkennen, dass die Abarbeitung der beiden Print-Befehle offensichtlich so viel Zeit benötigt, dass die Bedingung (Abfage des GPIO-Status) - fast - immer gleich ist. Hier besteht auch die Gefahr, dass während der Bearbeitung eines Interrupts bereits der nächste ausgelöst wird - eine der vier Voraussetzungen für Programmabstürze, die sich nur sehr schwer identifizieren lassen. In dem Fall läuf ein Stapelspeicher über und dann knallt es.

    Wie sieht es aus, wenn Du den Taster längere Zeit gedrückt hältst?
    Und wie sieht es aus, wenn Du die Delay-Zeit verringerst?

    Wenn Du nur einen Status setzt (und das auch effizienter als in Deinem ersten Code), dann funktioniert das auch 100%ig zuverlässig.

    Beste Grüße

    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

    Einmal editiert, zuletzt von Andreas (17. April 2017 um 12:54)

  • Wird der Schalter nur sehr kurz gedrück? Vielleicht ist der GPIO schon wieder low, bis die beiden Print-Befehle abgearbeitet sind.
    Stell mal die print-Befehle hinter die If-Abfrage.

    Oh, man kann hier unliebsame Nutzer blockieren. Wie praktisch!

  • Die Print Ausgabe blockiert die ISR derart das Flankenwechsel in der Zeit nicht registriert werden bzw die spätere Abfrage GPIO.input(26) bereits veraltet ist.
    Jegliche Ausgaben auf die Konsole bringen eine nicht zu unterschätzende Verzögerung mit sich.
    Google Suche nach: python print benchmark ... zB http://stackoverflow.com/questions/3857…n-it-be-sped-up


    Würde man direkt am Anfang GPIO.input(26) in eine Variable schreiben und erst dann die print's usw setzen, wäre es besser - unter Vorbehalt, da man in der ISR Funktion wie schon erwähnt keine wilden Sachen behandeln sollte, sondern lieber mit Queue arbeiten, das kann man dann genüsslich verarbeiten während die ISR blitzschnell und ohne Behinderung Flankenwechsel ins Queue einfügen kann.

  • Danke. Der Link mit #9 hatte die Lösung.

    Wenn der Taster jetzt gedrückt wird, wird hoch gezählt.
    Wenn der andere Taster gedrückt wird wird runter gezählt.

    Geht bestimmt auch eleganter, aber vielleicht hilft es ja irgendwem weiter.

  • Das sieht aber noch nicht richtig aus, da du das Queue nicht wirklich verwendest.
    interrupt_Event() trägt zwar was ins Queue ein, du verwendest die Einträge aber gar nicht... Klar, du hast zwar ein queue.get() aber nutzt es nur für'n print.
    Die späteren Abfragen außerhalb der ISR von wegen "if GPIO.input(br) == 0:" usw haben nichts mit der ISR Funktion oder dem Queue zu tun. Dass es bei dir auf den ersten Blick zu funktionieren scheint ist also nur Glück, weil dein "sleep" sehr kurz ist - aber unnötig wenn du den Code richtig anwenden würdest.
    Du solltest dir also unbedingt noch mal den Text in #9 durchlesen!

    Auch "dimmer()" könnte man anders schreiben sodass kein "global" benötigt wird.


    Richtiger wäre:
    [code=php]
    from __future__ import print_function
    from time import sleep, time
    from RPi import GPIO
    from Queue import Queue
    from functools import partial


    def interrupt_Event(q, channel):
    q.put( (channel, GPIO.input(channel)) )


    def dimmen(dimmer, value):
    if value == 1:
    dimmer += 1
    elif value == 0:
    dimmer -= 1
    return dimmer


    def main():
    br = 26 #button right
    bmr = 19 #button middle right
    bml = 13 #button middle left
    bl = 6 #button left
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(br, GPIO.IN)
    GPIO.setup(bmr, GPIO.IN)
    GPIO.setup(bml, GPIO.IN)
    GPIO.setup(bl, GPIO.IN)
    queue=Queue()
    GPIO.add_event_detect(br, GPIO.BOTH, callback=partial(interrupt_Event, queue), bouncetime=150)
    GPIO.add_event_detect(bmr, GPIO.BOTH, callback=partial(interrupt_Event, queue), bouncetime=150)
    GPIO.add_event_detect(bml, GPIO.BOTH, callback=partial(interrupt_Event, queue), bouncetime=150)
    GPIO.add_event_detect(bl, GPIO.BOTH, callback=partial(interrupt_Event, queue), bouncetime=150)
    dimmer_value=0

    try:
    while True:
    job = queue.get()
    pin = job[0]
    state = job[1]
    print(pin, state)

    if pin == br: #taster 1 gedrueckt
    if state == 0:
    dimmer_value = dimmer(dimmer_value, 1)
    elif pin == bmr: #taster 2 gedrueckt
    if state == 0:
    dimmer_value = dimmer(dimmer_value, 0)

    print(dimmer_value)

    except (KeyboardInterrupt, SystemExit):
    GPIO.cleanup()
    print("\nQuit\n")


    if __name__ == "__main__":
    main()
    [/php]

    Wenn du aber sowieso nur vor hast 'einen' Flankenwechsel zu verwenden (nicht BOTH sondern RISING oder FALLING) dann änder add_event_detect und passe ebenfalls die "state" Abfrage an.

  • Danke. Ich guck mir deinen Code Morgen nochmal genau an und überleg nochmal. Für heute ist die Luft raus.

    Ich glaube da fehlten Informationen. Warum ich das queue.get() überspringe um zu zählen, ist das er sonst den Wert nur um 1 pro Tastenanschlag verändert hat. Ich benutz den Wert aber um meine Beleuchtung zu dimmen. Die zulässigen Werte sind von 0 - 4095. So oft will ich den Schalter nicht drücken. :daumendreh2: Wenn ich den Taster los lasse hängt er wieder in dem queue.get() und die CPU wird nicht belasten. Es scheint so zu funktionieren. Bis jetzt ist noch kein Fehler aufgetreten. Wie der Code für einen Programmierer aussehen muss ist mir auch bewußt. :s

  • Bei dir funktioniert es so nur weil du extremes Glück hast. Du verwendest das Queue und somit die ISR aber nicht.
    Dein Code aus Beitrag#6 beachtet keine Einträge im Queue und handelt auch nicht entsprechend. So wie der Code in Beitrag#6 steht könntest du das ganze Queue Gedöns auch weg lassen. So wie Du es verwendest ist das ganze Queue Zeug useless.
    Keine Ahnung wie ich das sonst noch beschreiben soll - du hast es jedenfalls anscheint noch nicht verstanden wie das funktioniert, daher ist es eigentlich wichtig dass du dir das noch mal zu Gemüte führst.

    Code aus Beitrag#7:
    Der/Die Taster ist unabhängig von der while Schleife - das ist ja gerade das wichtige.
    Ein Taster beeinflusst nur das was ins Queue eingetragen wird - nichts weiter.
    Was du dann mit den Einträgen im Queue machst bzw um wie viel du dimmer_value veränderst, musst du selber festlegen und hat nichts mit der ISR Funktion zu tun. (ISR Funktion = interrupt_Event)

    queue.get() blockiert die while in der Tat solange bis ein Eintrag im Queue vorhanden ist - deshalb ist ein sleep auch überflüssig. Mit CPU Belastung hat das nichts zu tun, ebenso wenig mit deinem letzten Satz.

Jetzt mitmachen!

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