2016-09-30 629 views
12

Obecnie mam 2 przyciski podłączone do mojego Raspberry Pi (są to te z pierścieniem LED w nich) i próbuję wykonać ten kodPython funkcje przycisków dziwnie nie robi to samo

#!/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 powierzchnia wygląda na dość łatwy scenariusz. Kiedy naciśnij przycisk zostanie wykryte:

  1. usunąć Wydarzenia
  2. wydrukować wiadomość
  3. czekać 2 sekundy przed dodaniem wydarzenia i odwrotu diody na

który normalnie pracuje się wspaniale, gdy Naciskam zielony przycisk. Próbowałem go kilka razy z rzędu i działa bez problemów. Z czerwonym jednak działa dobrze za pierwszym razem, a po raz drugi, ale po zakończeniu drugiego cyklu (pin) skrypt po prostu się zatrzymuje.

Biorąc pod uwagę oba zdarzenia są dość podobne, nie mogę wyjaśnić, dlaczego nie zakończyło się ono na końcu drugiego czerwonego przycisku.

EDYCJA: Zmieniłem szpilki odpowiednio z czerwonego i zielonego (albo do różnych pinów całkowicie lub zamienię je). Tak czy inaczej, zawsze czerwony kod przycisku (obecnie zielony przycisk) powoduje błąd. Wygląda więc na to, że nie jest to problem z fizycznym czerwonym przyciskiem, ani problem z pinem, to po prostu pozostawia kod, by być w błędzie ...

+0

Być może jedno z wywołań "GPIO.output" podniosło wyjątek, a następnie 'add_events()' nigdy nie było wywoływane ponownie? – zvone

+0

Dziękuję za uwagę w tej sprawie. Dodałem klauzule z wyjątkiem, ale nie zostały uruchomione. Wygląda na to, że to nie to. – user5740843

+0

To również nie wyjaśnia, dlaczego działa dobrze, ale zawsze kończy się niepowodzeniem na końcu drugiego cyklu ... – user5740843

Odpowiedz

9

Udało mi się odtworzyć Twój problem na moim Raspberry Pi 1, Model B uruchomienie skryptu i podłączenie kabla łączącego między masą a GPIO27 w celu symulacji czerwonych naciśnięć przycisków. (To są piny 25 i 13 w moim konkretnym modelu Pi).

Interpreter pytonów ulega awarii z błędem segmentacji w wątku przeznaczonym do odpytywania zdarzeń GPIO po tym, jak red powraca z obsługi naciśnięcia przycisku. Po zapoznaniu się z implementacją modułu Python GPIO, jest dla mnie jasne, że nie jest bezpieczne wywoływanie remove_event_detect z poziomu wywołania zwrotnego funkcji obsługi zdarzenia, a to powoduje awarię. W szczególności usunięcie programu obsługującego zdarzenia podczas działania tego programu obsługi zdarzeń może prowadzić do uszkodzenia pamięci, co spowoduje awarie (jak widzieliśmy) lub inne dziwne zachowania.

Podejrzewam, że usuwasz i ponownie dodajesz programy do obsługi zdarzeń, ponieważ obawiasz się otrzymania wywołania zwrotnego w czasie, gdy przekazujesz naciśnięcie przycisku. Nie ma takiej potrzeby. Moduł GPIO obraca pojedynczy wątek odpytywania w celu monitorowania zdarzeń GPIO i czeka na powrót jednego wywołania zwrotnego przed wywołaniem innego, niezależnie od liczby obserwowanych zdarzeń GPIO.

Proponuję po prostu wykonać połączenia do add_event_detect podczas uruchamiania skryptu i nigdy nie usuwać wywołań zwrotnych. Po prostu usunięcie add_events i remove_events (i ich wywołań) ze skryptu rozwiąże problem.

Jeśli jesteś zainteresowany szczegółami problemu w module GPIO, możesz spojrzeć na C source code for that module. Spójrz na run_callbacks i remove_callbacks w pliku RPi.GPIO-0.6.2/source/event_gpio.c. Zauważ, że obie te funkcje używają globalnego łańcucha węzłów struct callback. run_callbacks przechodzi łańcuch wywołań zwrotnych, chwytając jeden węzeł, wywołując wywołanie zwrotne, a następnie podążając za odnośnikiem tego węzła do następnego wywołania zwrotnego w łańcuchu. remove_callbacks przejdzie ten sam łańcuch wywołań zwrotnych i zwolni pamięć skojarzoną z oddzwanianiem na określonym gnieździe GPIO.Jeśli zostanie wywołane remove_callbacks w środku run_callbacks, węzeł aktualnie przechowywany przez run_callbacks może zostać zwolniony (i może zostać ponownie wykorzystana i zastąpiona) przed przejściem do wskaźnika następnego węzła.

Powód, dla którego problem występuje tylko w przypadku czerwonego przycisku, prawdopodobnie wynika z kolejności wywołań pod numerami add_event_detect i remove_event_detect powoduje, że pamięć poprzednio używana przez węzeł wywołania zwrotnego dla czerwonego przycisku jest odzyskiwana w innym celu i nadpisywana wcześniej niż pamięć używana w zielonym węźle zwrotnym przycisku jest podobnie odzyskiwana. Zapewniamy jednak, że problem dotyczy obu przycisków - po prostu szczęście, że pamięć powiązana z zielonym wywoływaniem przycisku nie zostanie zmieniona przed śledzeniem wskaźnika do następnego węzła wywołania zwrotnego.

Ogólnie rzecz biorąc, istnieje brak synchronizacji wątków wokół ogólnie używanego łańcucha wywołania zwrotnego w module GPIO i podejrzewam, że podobne problemy mogą wystąpić, jeśli wywoływane są remove_event_detect lub add_event_detect podczas działania procedury obsługi zdarzeń, nawet jeśli zdarzenia są usuwane z innego wątku! Sugerowałbym, aby autor modułu RPi.GPIO użył pewnej synchronizacji, aby zapewnić, że łańcuch wywołań zwrotnych nie może być modyfikowany podczas wykonywania wywołań zwrotnych. (Może Oprócz sprawdzenia, czy sieć jest modyfikowany na gwint odpytywania siebie, pthread_mutex_lock i pthread_mutex_unlock może być stosowany w celu zapobiegania inne nici z modyfikując łańcuch zwrotnego, gdy jest on używany przez nitki sondowania).

Niestety , tak nie jest obecnie i dlatego sugeruję, aby unikać wywoływania remove_event_detect, jeśli możesz tego uniknąć.

+0

Dobre debugowanie. Możesz wspomnieć, jak udało ci się złapać tłumacza w segfaultie, ale poza tym jest to bardzo kompletna informacja. Częściowo dlatego, że GPIO obsługuje tylko jedno wydarzenie na raz, nie jest dobrym pomysłem spanie w programie obsługi zdarzeń. Rozwiązaniem jest ustawienie czasu zakończenia zdarzenia, a następnie kontynuowanie skanowania. Sposobem na uniknięcie wielu zdarzeń jest użycie poleceń testowania i zamiany, które na ARMv6 (Raspberry Pi 1) i późniejszych są STREX i LDREX: http://infocenter.arm.com/help/topic/com.arm. doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf Może dla Pi i Pythona znajduje się opakowanie. –