2016-09-30 2 views
12

ich derzeit haben 2 Tasten meiner Raspberry Pi angeschlossen (das sind die, die mit dem Ring in ihnen LEDs) und ich versuche, diesen CodePython Tastenfunktionen tun seltsam nicht gleich

#!/usr/bin/env python 
import RPi.GPIO as GPIO 
import time 

GPIO.setmode(GPIO.BCM) 
GPIO.setwarnings(False) 
GPIO.setup(17, GPIO.OUT) #green LED 
GPIO.setup(18, GPIO.OUT) #red LED 
GPIO.setup(4, GPIO.IN, GPIO.PUD_UP) #green button 
GPIO.setup(27, GPIO.IN, GPIO.PUD_UP) #red button 

def remove_events(): 
     GPIO.remove_event_detect(4) 
     GPIO.remove_event_detect(27) 

def add_events(): 
     GPIO.add_event_detect(4, GPIO.FALLING, callback=green, bouncetime=800) 
     GPIO.add_event_detect(27, GPIO.FALLING, callback=red, bouncetime=800) 

def red(pin): 
     remove_events() 
     GPIO.output(17, GPIO.LOW) 
     print "red pushed" 
     time.sleep(2) 
     GPIO.output(17, GPIO.HIGH) 
     add_events() 

def green(pin): 
     remove_events() 
     GPIO.output(18, GPIO.LOW) 
     print "green pushed" 
     time.sleep(2) 
     GPIO.output(18, GPIO.HIGH) 
     add_events() 

def main(): 
    while True: 
     print "waiting" 
     time.sleep(0.5) 

GPIO.output(17, GPIO.HIGH) 
GPIO.output(18, GPIO.HIGH) 
GPIO.add_event_detect(4, GPIO.FALLING, callback=green, bouncetime=800) 
GPIO.add_event_detect(27, GPIO.FALLING, callback=red, bouncetime=800) 

if __name__ == "__main__": 
    main() 

On auszuführen die Oberfläche sieht wie ein ziemlich einfaches Skript aus. Wenn eine Taste drücken erkannt wird:

  1. die Ereignisse
  2. Mitteilung drucken
  3. 2 Sekunden warten, bevor die Ereignisse hinzufügen und drehen die LEDs wieder auf

, die normalerweise funktioniert groß entfernen, wenn Ich drücke den grünen Knopf. Ich habe es mehrmals hintereinander versucht und es funktioniert ohne Fehler. Mit dem Rot funktioniert es jedoch das erste Mal und das zweite Mal gut, aber nachdem es den zweiten roten (Stift-) Zyklus abgeschlossen hat, stoppt das Skript einfach.

In Anbetracht beider Ereignisse sind ziemlich ähnlich, kann ich nicht erklären, warum es am Ende des zweiten roten Knopfes fehlschlägt.

EDIT: Ich habe die Pins von rot und grün jeweils geändert (entweder zu verschiedenen Pins komplett oder tauschen Sie sie aus). Wie auch immer, es ist immer der rote Knopf Code (eigentlich jetzt grüne Taste) verursacht einen Fehler. Es scheint also, es ist nicht ein physikalisches rotes Knopf Problem, noch ein Pin-Problem, dies lässt nur den Code fehlerhaft sein ...

+0

Vielleicht eine der 'GPIO.output' Aufrufe eine Ausnahme ausgelöst und dann' add_events() 'wurde nie wieder aufgerufen? – zvone

+0

Vielen Dank für Ihre Meinung zu diesem Thema. Ich habe außer Klauseln hinzugefügt, aber sie wurden nicht ausgelöst. Sieht so aus als wäre es nicht so. – user5740843

+0

Es würde auch nicht erklären, warum es einmal gut funktioniert, aber immer am Ende des zweiten Zyklus versagt ... – user5740843

Antwort

9

Ich konnte Ihr Problem auf meinem Raspberry Pi 1, Modell B von reproduzieren Führen Sie Ihr Skript aus und verbinden Sie ein Jumperkabel zwischen Masse und GPIO27, um rote Tasten zu simulieren. (Dies sind die Pins 25 und 13 an meinem speziellen Pi-Modell.)

Der Python-Interpreter stürzt mit einem Segmentierungsfehler im Thread ab, der zum Abrufen von GPIO-Ereignissen dient, nachdem red von einem Tastendruck zurückkehrt. Nach dem Betrachten der Implementierung des Python GPIO Moduls ist es für mich klar, dass es unsicher ist, remove_event_detect innerhalb eines Ereignishandler Callbacks aufzurufen, und dies verursacht den Absturz. Insbesondere das Entfernen eines Ereignishandlers, während dieser Ereignishandler gerade ausgeführt wird, kann zu Speicherbeschädigung führen, was zu Abstürzen (wie Sie gesehen haben) oder anderen merkwürdigen Verhaltensweisen führen kann.

Ich vermute, dass Sie die Ereignisbehandlungsroutinen entfernen und erneut hinzufügen, weil Sie Bedenken haben, während der Zeit, in der Sie einen Tastendruck ausführen, einen Rückruf zu erhalten. Es besteht keine Notwendigkeit, dies zu tun. Das GPIO-Modul dreht einen einzelnen Abfrage-Thread, um GPIO-Ereignisse zu überwachen, und wartet auf einen Rückruf, bevor er einen anderen aufruft, unabhängig von der Anzahl der GPIO-Ereignisse, die Sie gerade sehen.

Ich schlage vor, dass Sie einfach Ihre Anrufe an add_event_detect als Skript starten, und entfernen Sie nie die Rückrufe. Durch das einfache Entfernen von add_events und remove_events (und ihrer Aufrufe) aus Ihrem Skript wird das Problem behoben. Wenn Sie sich für die Details des Problems im Modul GPIO interessieren, können Sie sich die C source code for that module ansehen. Werfen Sie einen Blick auf run_callbacks und remove_callbacks in der Datei RPi.GPIO-0.6.2/source/event_gpio.c. Beachten Sie, dass diese beiden Funktionen eine globale Kette von struct callback Knoten verwenden. run_callbacks führt die Rückrufkette durch, indem ein Knoten abgerufen, der Rückruf aufgerufen und dann die Verknüpfung dieses Knotens mit dem nächsten Rückruf in der Kette verfolgt wird. remove_callbacks wird dieselbe Callback-Kette durchlaufen und den mit den Callbacks auf einem bestimmten GPIO-Pin verbundenen Speicher freigeben.Wenn remove_callbacks in der Mitte von run_callbacks aufgerufen wird, kann der Knoten, der derzeit von run_callbacks gehalten wird, freigegeben werden (und sein Speicher möglicherweise wiederverwendet und überschrieben werden), bevor der Zeiger auf den nächsten Knoten folgt.

Der Grund, warum Sie dieses Problem für den roten Knopf nur sehen aufgrund der Reihenfolge der Anrufe an add_event_detect und remove_event_detect wahrscheinlich ist, bewirkt, dass der Speicher durch die Callback-Knoten für den roten Knopf verwendet zuvor für einen anderen Zweck werden zurückgewonnen und überschrieben früher als der Speicher, der von dem Rückrufbutton für die grüne Taste verwendet wird, wird ähnlich zurückgewonnen. Seien Sie jedoch versichert, dass das Problem für beide Tasten besteht - es ist nur Glück, dass der Speicher, der mit dem grünen Knopfrückruf verbunden ist, nicht geändert wird, bevor der Zeiger auf den nächsten Rückfrageknoten folgt.

Generell gibt es einen über Mangel an Thread-Synchronisation um die Callback-Kette der Verwendung in dem GPIO-Modul im Allgemeinen, und ich vermute, ähnliche Probleme auftreten könnten, wenn remove_event_detect oder add_event_detect genannt werden, während ein Event-Handler ausgeführt wird, auch wenn Ereignisse werden aus einem anderen Thread entfernt! Ich würde vorschlagen, dass der Autor des Moduls RPi.GPIO eine Synchronisierung verwenden sollte, um sicherzustellen, dass die Callback-Kette nicht geändert werden kann, während Rückrufe durchgeführt werden. (Vielleicht, zusätzlich zu überprüfen, ob die Kette auf dem Faden Polling modifiziert wird selbst, pthread_mutex_lock und pthread_mutex_unlock könnten verwendet werden, um andere Threads zu verhindern, dass die Callback-Kette zu modifizieren, während es in Gebrauch durch den Polling-Thread.)

Leider , das ist derzeit nicht der Fall, und aus diesem Grund schlage ich vor, Sie vermeiden, remove_event_detect ganz zu rufen, wenn Sie es vermeiden können.

+0

Gutes Debugging. Sie könnten erwähnen, wie Sie den Interpreter in einem segfault fangen konnten, aber ansonsten sind es sehr vollständige Informationen. Da GPIO nur jeweils ein Ereignis bedient, ist es nicht sinnvoll, in einem Event-Handler zu schlafen. Die Lösung besteht darin, die Endzeit eines Ereignisses festzulegen und dann den Scanvorgang fortzusetzen. Um mehrere Ereignisse zu vermeiden, verwenden Sie die test-and-swap-Befehle, die auf ARMv6 (Raspberry Pi 1) und höher STREX und LDREX sind: http://infocenter.arm.com/help/topic/com.arm. doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf Vielleicht gibt es für den Pi und Python einen Wrapper. –