Probleme mit Interrupts

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
basti2s
User
Beiträge: 29
Registriert: Samstag 14. November 2020, 08:36

Hallo zusammen,

ich komm gerade nicht klar. Vielleicht könnt ihr mir helfen.

Ich verwende einen RaspberryPi. An den GPIOs ist ein Wahlschalter angeschlossen. Also im Endeffekt ein Drehschalter.
Ich möchte je nach Stellung des Schalters eine bestimmte Funktion ausführen. Der Schalter hat drei Stellungen.
In jeder Funktion befindet sich jedoch eine while Schleife, weshalb ich das ganze über Interrupts realisieren würde.
Das ganze sieht in etwa so aus:

Code: Alles auswählen

import RPi.GPIO as GPIO
GPIO.setmode (GPIO.BOARD)

GPIO.setup (12, GPIO.IN) #Wahlschalter
GPIO.setup (18, GPIO.IN) #Wahlschalter
GPIO.setup (16, GPIO.IN) #Wahlschalter

GPIO.add_event_detect (12, GPIO.RISING, bouncetime = 70) #Einstellen Interrupt
GPIO.add_event_detect (16, GPIO.RISING, bouncetime = 70)
GPIO.add_event_detect (18, GPIO.RISING, bouncetime = 70)

def mode1():
    while (1):
        print("1")

def mode2():
    while (1):
        print("8")

def mode3():
    while (1):
        print("3")



def callback_GPIO(n):
    if GPIO.input(12):
        mode1()

    if GPIO.input(16):
        print("wamp")
        mode2()

    if GPIO.input(18):
        mode3()



GPIO.add_event_callback(12, callback_GPIO) #Wenn Interrupt erkannt wurde wird funktion "myCallback" aufgerufen
GPIO.add_event_callback(16, callback_GPIO)
GPIO.add_event_callback(18, callback_GPIO)

mode1()
In der Konsole kommen dann nur komische Werte raus, wenn ich den Schalter drehe. Hier mal ein Ausschnitt:

Code: Alles auswählen

1
1
1
1
1
1
1
1
1
31

31

31

31

3
13
1

13

13

13
1

13

13

31

13

13

13
1

1
3
13

13

31

31
3

1
1
1
3
13

13

13

13

13

1
3
31
1
1
1
1

13

13
1

13

13
3
3
3
3
3
3
3
3
3

3
3
3
3
1
31

1
31
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3

31

3
3
3
3
3
3

Was genau mach ich falsch? Wo liegt mein Denkfehler?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Zu denken die callbacks würden immer in einem eigenen Thread gestartet. Es gibt nur EINEN Thread, der für alle IRQs da ist. Und den main Thread. Und was passiert ist ganz einfach: der main Thread printed 1. Und der eine IRQ thread wird von dir in eine Endlosschleife geschickt, die eine 3 printed. Und die kommen jetzt wild durcheinander auf der Konsole an.

Im IRQ darf man nur ganz kurz was machen. Denn sonst hält der nachfolgende auf.
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

(1) ist das selbe wie 1 und sollte eigentlich True sein. Was willst du mit den Endlosschleifen bezwecken?
Benutzeravatar
__blackjack__
User
Beiträge: 14027
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@basti2s: Ergänzend: Ich würde `RPi.GPIO` nicht mehr ohne Not verwenden. Es gibt mit dem `gpiozero`-Modul eine bessere, modernere API.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
basti2s
User
Beiträge: 29
Registriert: Samstag 14. November 2020, 08:36

Sirius3 hat geschrieben: Montag 15. Februar 2021, 17:28 (1) ist das selbe wie 1 und sollte eigentlich True sein. Was willst du mit den Endlosschleifen bezwecken?
Ich möchte mit dem Wahlschalter unterschiedliche Modi ausführen. Und in jedem Modi werden halt andere unterprogramme ausgeführt. Die sollen halt dann endlos durchlaufen, bis duch den wahlschalter ein anderer Modus ausgewählt wird.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das wird so nichts. Denn ohne periodisch zu prüfen, ob der Modus noch stimmt, kannst du etwas, das endlos läuft, per Definition nicht beenden.
basti2s
User
Beiträge: 29
Registriert: Samstag 14. November 2020, 08:36

Okay. Wäre es denn eine Möglichkeit mit mehreren Threads zu arbeiten?

Also in meinem Main Thread führe ich quasi die Modi1-3 die ganze Zeit aus (unabhängig von der Schalterstellung) und schreibe die Ausgaben in Queues.
Wenn der Schalter verändert wird erfolgt ein Interrupt. In der Callback Funktion wird in eine separate Queue geschrieben, dass der Modus x jetzt aktiv ist.
Der zweite Thread pullt diese Liste und gibt dann die Werte des aktiven Modus aus.

Wäre das eine gute Lösung?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das klingt falsch. Wenn du alles drei laufen lässt, aber die Ergebnisse von zweien verwerfen willst - wieso lässt du die laufen? Warum kannst du nicht abhängig von dem gewünschten Zustand einfach EIN Ergebnis berechnen, das ausgeben, prüfen, ob der Zustand sich geändert hat, und entsprechend reagieren, und von vorne beginnen?
basti2s
User
Beiträge: 29
Registriert: Samstag 14. November 2020, 08:36

Genau das hat mich daran auch gestört. Ich habe jetzt folgendes gemacht:

Code: Alles auswählen

import RPi.GPIO as GPIO
GPIO.setmode (GPIO.BOARD)

GPIO.setup (12, GPIO.IN) #Wahlschalter
GPIO.setup (18, GPIO.IN) #Wahlschalter
GPIO.setup (16, GPIO.IN) #Wahlschalter

GPIO.add_event_detect (12, GPIO.RISING, bouncetime = 70) #Einstellen Interrupt
GPIO.add_event_detect (16, GPIO.RISING, bouncetime = 70)
GPIO.add_event_detect (18, GPIO.RISING, bouncetime = 70)

def mode1():
    print("1")

def mode2():
    print("8")

def mode3():
    print("3")



def wahlschalter_callback():
    if GPIO.input(12):
        mode1()

    if GPIO.input(16):
        print("wamp")
        mode2()

    if GPIO.input(18):
        mode3()
        
 while (1):
    wahlschalter_callback()



GPIO.add_event_callback(12, wahlschalter_callback) #Wenn Interrupt erkannt wurde wird funktion "myCallback" aufgerufen
GPIO.add_event_callback(16, wahlschalter_callback)
GPIO.add_event_callback(18, wahlschalter_callback)


Müsste so doch ertragbar sein?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nicht. Im. Callback. Dinge. Machen. NUR signalisieren. Wie schon Millionen mal geschrieben, und mehrfach in diesem Thema.
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Callbacks werden jetzt ja gar nicht mehr gebraucht und auch nie gesetzt, da die Endlosschleife nie endet.
Benutzeravatar
__blackjack__
User
Beiträge: 14027
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Also mal ordentlich ohne Zeug auf Modulebene und mit aufräumen der GPIOs am Programmende, und auf das wesentliche gekürzt der Stand:

Code: Alles auswählen

#!/usr/bin/env python3
from RPi import GPIO  # TODO `gpiozero` verwenden.

PIN_TO_TEXT = {12: "1", 16: "wamp\n8", 18: "3"}


def main():
    try:
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(list(PIN_TO_TEXT), GPIO.IN)
        while True:
            for pin, text in PIN_TO_TEXT.items():
                if GPIO.input(pin):
                    print(text)
    finally:
        GPIO.cleanup()


if __name__ == "__main__":
    main()
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
basti2s
User
Beiträge: 29
Registriert: Samstag 14. November 2020, 08:36

Sirius3 hat geschrieben: Dienstag 16. Februar 2021, 13:24 Die Callbacks werden jetzt ja gar nicht mehr gebraucht und auch nie gesetzt, da die Endlosschleife nie endet.
Ist es nicht der Sinn eines Interrupts, dass er mich aus einer Endlosschleife holt?
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

Nein, ein Interrupt unterbricht nur kurz eine Endlosschleife, die danach aber ungerührt weiterläuft, wenn man nicht explizit in der Schleife was macht.
Meine Satz bezieht sich aber auf Dein damals aktuelles Programm, das keine Callbacks mehr benutzt.
basti2s
User
Beiträge: 29
Registriert: Samstag 14. November 2020, 08:36

und wie verhält sich das ganzte mit time.sleep? Ich habe das Gefühl, dass er nach dem Interrupt wieder zurück in das time.sleep springt und dort weiter schläft.
Gibt es denn die Möglichkeit zu definieren, dass nach einem Interrupt nicht wieder in das time.sleep gesprungen wird?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dazu benutzt man eine Queue. Die schläft, entweder für immer, oder mit timeout, bis aus einem IRQ darin ein Element abgelegt wurde. Oder eben der timeout zuschlägt, was früher dran ist.
Antworten