Skocz do zawartości

Obsługa przycisku Python Raspberry Pi przerwanie


Jan3k

Pomocna odpowiedź

Szanowni Państwo,

Zrealizowałem działanie świateł zgodnie z Państwa instrukcją i światła działają jak należy. Chciałbym dodać funkcjonalność działania świateł awaryjnych (żółta miga z częstotliwością 1 Hz) załączaną za pomocą przycisku. Zrealizowałem to poniższa metodą i działa. Mimo, że nie do końca tak jak bym chciał (trzeba czekać do końca pętli). Niestety nie jest to metoda przerwań ( a jak to nazwać? "polling"?). Da się jakoś inaczej prosto to zrealizować? Czy trzeba by dodać osobną zewnętrzną funkcję która będzie wywoływana i przerywana na zbocze opadające przycisku? 
 

import RPi.GPIO as GPIO
from time import *

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(21, GPIO.OUT)			 #czerwona dioda
GPIO.setup(18, GPIO.OUT)			 #zolta dioda
GPIO.setup(12, GPIO.OUT)			 #zielona dioda
GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_UP) #przycisk

while True:
	while not GPIO.input(4):
		GPIO.output(18, 0)
		sleep(1)
		GPIO.output(18, 1)
		sleep(1)
	GPIO.output(18, 0)
	GPIO.output(21, 1)
	sleep(1)
	GPIO.output(18, 1)
	sleep(1)
	GPIO.output(21, 0)
	GPIO.output(18, 0)
	GPIO.output(12, 1)
	sleep(1)
	GPIO.output(12,0)
	GPIO.output(18,1)
	sleep(1)

EDIT: Nie chcę od razu tutaj jakiejś burzy wywoływać. Pytam z ciekawości. 

Edytowano przez Jan3k
błąd składni zdania
Link do komentarza
Share on other sites

@Jan3k wydzieliłem temat, bo jest on do rozwiązania ale może być mylący dla przyszłych kursantów.

Zadanie to możesz zrealizować bez odpytywania (zgadza się polling) tylko przy pomocy funkcji (callback).

import RPi.GPIO as GPIO

def button_callback(channel):
    print("nacisnieto przycisk")

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_UP)

GPIO.add_event_detect(4,GPIO.FALLING,callback=button_callback)

message = input("Press enter to quit\n\n")

GPIO.cleanup()

Po wykryciu sygnału opadającego, nastąpi wywołanie funkcji, której nazwa (w C wskaźnik na funkcję) został przekazany jako argument, tu jest to button_callback. Zmienna channel która jest tu w argumencie a nie jest użyty jest wymogiem narzuconym przez twórców pakietu i stanowi "szablon" w który trzeba się wpasować. Nazwa funkcji jest za to dowolna w obrębie możliwych nazw 🙂 

Edytowano przez Gieneq
Link do komentarza
Share on other sites

import RPi.GPIO as GPIO
from time import *

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(21, GPIO.OUT)			 #czerwona dioda
GPIO.setup(18, GPIO.OUT)			 #zolta dioda
GPIO.setup(12, GPIO.OUT)			 #zielona dioda
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP) #przycisk

def button_callback(channel):
	print("Tryb Awaryjny")
	while True:
		GPIO.output(18,1)
		sleep(1)
		GPIO.output(18,0)
		sleep(1)

GPIO.add_event_detect(17,GPIO.FALLING,callback=button_callback)

while True:
	GPIO.output(18, 0)
	GPIO.output(21, 1)
	sleep(1)
	GPIO.output(18, 1)
	sleep(1)
	GPIO.output(21, 0)
	GPIO.output(18, 0)
	GPIO.output(12, 1)
	sleep(1)
	GPIO.output(12,0)
	GPIO.output(18,1)
	sleep(1)

Zmodyfikowałem kod dodając tą funkcjonalność. Przeniosłem także przycisk z wejścia IO 4 na IO 17. Pytanie, czemu używasz trybu "GPIO.BOARD" zamiast "GPIO.BCM"? Nie łatwiej się trzymać tylko i wyłącznie jednego, żeby się nie mieszały? 🙂

Co do mojego programu. To teraz dzieje się coś dziwnego. Puki nie kliknę przycisku to główna pętla działa tak jak wcześniej, czyli dobrze. Po wciśnięciu przycisku uruchamia się kolejny "while" i działają dwa równolegle? Dioda Zielona i czerwona migają tak jak w głównej sekwencji. Natomiast Żółta miga z częstotliwością 1Hz tak jak w funkcji button_callback. 
Powinienem dodać do funkcji "button_callback" instrukcję warunkową która na ponowne wciśnięcie przycisku wywołuje "breake"  i wraca do głównej pętli? albo jak można zablokować główną pętle tak by po wywołaniu przerwania się nie wykonywała? 

Link do komentarza
Share on other sites

20 godzin temu, Jan3k napisał:

Nie łatwiej się trzymać tylko i wyłącznie jednego, żeby się nie mieszały

Zgodzę się, że łatwiej 🙂 

20 godzin temu, Jan3k napisał:

Powinienem dodać do funkcji "button_callback" instrukcję warunkową która na ponowne wciśnięcie przycisku wywołuje "breake"  i wraca do głównej pętli?

Jak rozumiem klikamy przycisk i puszczamy włącza się żółte i sobie miga. Znowu klikamy i puszczamy i już nie miga? Takie zachowanie nazywa się przełącznik T (toggle), albo zatrzask. To można zmienić działanie - po naciśnięciu przycisku ma nastąpić wystartowanie lub zatrzymanie migania LED. W jaki sposób? Albo jakąś zmienną i pewną logiką warunków albo zmienną i wątkiem.

Np wewnątrz zdarzenia od przycisku napisz coś takiego:

emergency_state = 0

def but_callback(channel):
	if emergency_state == 0:
		emergency_state = 1
	else:
      emergency_state = 1

albo nieco prościej, tylko inne wartości trzeba rozróżniać (-1 i 1):

emergency_state = -1

...

emergency_state = -emergency_state
}

Teraz mamy zmienną której możemy użyć do rozpoznawania czy jest włączone czy nie, to teraz jak jej użyć. Jakoś tego nie kojarzę, ale pewnie jak stan wyjątkowy to nie ma tego poprzedniego migania:

while True:
	if emergency_state = -1:
      GPIO.output(18, 0)
      GPIO.output(21, 1)
      sleep(1)
      GPIO.output(18, 1)
      sleep(1)
      GPIO.output(21, 0)
      GPIO.output(18, 0)
      GPIO.output(12, 1)
      sleep(1)
      GPIO.output(12,0)
      GPIO.output(18,1)
      sleep(1)
    else:
		GPIO.output(18,1)
		sleep(1)
		GPIO.output(18,0)
		sleep(1)

Trzeba wyrównać wcięcia, dla interpretera Pythona to bardzo ważne.

Jeszcze w funkcji obsługi przycisku możesz kasować stany, bo pewnie coś zostanie z animacji dla GPIO 18 które jest ustawiane na końcu obiegu pętli.

Działanie te nie zostanie wykonane od razu, bo w animacji są opóźnienia. W najgorszym wypadku zostanie wykonane po 4 lub 2 sekundach, zależnie z jakiego stanu wychodzimy. Aby to zmienić trzeba by inaczej podejść do odmierzania czasu i animacji.

Edytowano przez Gieneq
  • Lubię! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

(edytowany)

Zrobiłem coś podobnego do twojego:
 

import RPi.GPIO as GPIO
from time import *

tryb = 1					#tryb pracy swiatel
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(21, GPIO.OUT)			 #czerwona dioda
GPIO.setup(18, GPIO.OUT)			 #zolta dioda
GPIO.setup(12, GPIO.OUT)			 #zielona dioda
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP) #przycisk

def button_callback(channel):
	print("Tryb Awaryjny")
	tryb = -tryb

GPIO.add_event_detect(17,GPIO.FALLING,callback=button_callback)

while True:
	if tryb == 1:
		GPIO.output(18, 0)
		GPIO.output(21, 1)
		sleep(1)
		GPIO.output(18, 1)
		sleep(1)
		GPIO.output(21, 0)
		GPIO.output(18, 0)
		GPIO.output(12, 1)
		sleep(1)
		GPIO.output(12,0)
		GPIO.output(18,1)
		sleep(1)
	else:
		GPIO.output(18,0)
		sleep(1)
		GPIO.output(18,1)
		sleep(1)

Niestety wyrzuca mi taki błąd. Po mimo, że program "chodzi". To tylko żółta dioda świeci światłem ciągłym bez migania.

image.thumb.png.5ae424f75171fcdbd6e555291f2ab051.png

EDIT: Rozumiem, ze nie podoba mu się zmienna "tryb". Czemu traktuje ją jako lokalną skoro zdefiniowałem ją jako globalną?

EDIT2: Dobra, nie zdefiniowałem w funkcji "tryb" jako "global tryb. Co do żółtej diody to zapomniałem, że mam ją podłączoną do IO 19 a nie IO 18 😅
Dzięki za pomoc. Temat można zamknąć.

Edytowano przez Jan3k
  • Lubię! 1
Link do komentarza
Share on other sites

Ciekawa sprawa, fajnie że doszedłeś co było nie tak z tą zmienną.

Nie jestem pewny czy gdyby dodać funkcję main i w niej zadeklarować wartość czy by nie zadziałało, możesz sprawdzić a main przyda Ci się w przyszłości np. tworząc dodatkowe klasy.

if __name__ == "__main__":
  # wejscie do programu

Właśnie skoro o klasach, pewnie jakby wydzielić to do osobnej klasy to też by zadziałało, ale to już inna sprawa, ale zwracam uwagę, bo może będziesz chciał coś jeszcze pokombinować. 😉 

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.