Skocz do zawartości

Użycie przycisku do przełączania się między pętlami


MrReus

Pomocna odpowiedź

Witam. Jestem w trakcie tworzenia projektu na bazie Arduino, lecz zatrzymał mnie problem, którego nie potrafię rozwiązać - mianowicie chciałbym za pomocą przycisku(dokładniej takiego z joystick'a) przełączać się między funkcjami, tak, żeby działały one w pętli. Mój problem polega na tym, że gdy nacisnę przycisk to pętla odpala się ale gdy wciskam kolejny raz chcąc przełączyć się na kolejną pętlę nic się nie dzieje. Kod przeznaczony do obsługi przycisku wygląda tak:

int buttonPin=2;
int buttonNew;
int buttonOld=1;
int buttonState = 0;
 
void setup() {
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
 
}
 
void loop() { 
buttonNew=digitalRead(buttonPin);
if(buttonOld==0 && buttonNew==1){
  if (buttonState==0){
    loop1();
    buttonState=1;
  }
  else{
    loop2();
    buttonState=0;
  }
}
buttonOld=buttonNew;
delay(100);
}

void loop1(){
  while(true){
    Serial.println("1");
  }
}

void loop2(){
  while(true){
    Serial.println("2");
  }
}

Gdy wciskam przycisk na monitorze dostaję serię jedynek ale po ponownym wciśnięciu nic się nie dzieje.

Prosiłbym o pomoc.

Link do komentarza
Share on other sites

Powodem jest to, że po pierwszym przyciśnięciu przycisku wpadasz w pętlę while, która jest wykonywana dotąd aż wypełniony jest warunek podany w nawiasie. W Twoim przypadku jest to true. Stąd też program nie jest w stanie opuścić pętli. Proponuję, żebyś poczytał sobie o instrukcji switch lub zastosował proste if. Poniżej przykład z zastosowaniem switcha.

switch(buttonState)
{
	case 0:	// jeśli buttonState = 0
	{
		Serial.println(“1”);
		break; // bez tego, jeśli buttonState = 0, wykonałby się też kod przypisany do wartości 1				
	}
	case 1: // jeśli buttonState = 1
	{
		Serial.println(“0”);
		break;
	}
}

Wówczas pozbywasz się w ogóle loop1 i loop2, a switch wstawiasz przed buttonOld = buttonNew i o ile nie popełniłem prostego błędu, działa. 

 

Link do komentarza
Share on other sites

Dnia 16.04.2021 o 16:36, _LM_ napisał:

Nic się nie dzieje ponieważ gdy już wejdziesz w jedną z pętli nie sprawdzasz stanu przycisku. W pętli 1 i 2 musisz mieć możliwość wyjścia z nich

Jak w prosty sposób sprawdzać stan przycisku w pętli żeby dało się z niej wyjść?

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

25 minut temu, MrReus napisał:

Jak w prosty sposób sprawdzać stan przycisku w pętli żeby dało się z niej wyjść?

Jak chcesz uzywac while w taki sposob ze chcesz w niej zostac do czasu ponownego wcisniecia guzika to moze cos takiego..

uint8_t stan = 0;

if (digitalRead(guzik) == LOW) {
  stan = 0;
  loop1();
  }

void loop1() {
  
while (stan == 0) {
  //kod
  if (digitalRead(guzik) == LOW) {
    stan = 1;
    }
  }
  }

Pozatym musisz zdawac sobie sprawe z tego jak juz wspomnial kolega _LM_  ze musisz miec mozliwosc wyjscia z while...a zapis..

while(true)

nigdy Ci nie da takiej mozliwosci...to jest nieskonczona petla! gdy raz do niej "wpadniesz" to juz zostajesz...

Edytowano przez farmaceuta
Link do komentarza
Share on other sites

Pytanie: po co while w loop1 i loop2? Przecież i tak wykonuje się pętla while w której siedzi loop()

Co się stanie, jeśli zrobisz po prostu:
 

void loop1()
{
  Serial.println("1");
}

void loop2()
{
  Serial.println("2");
}

 

Link do komentarza
Share on other sites

2 godziny temu, MrReus napisał:

Jak w prosty sposób sprawdzać stan przycisku w pętli żeby dało się z niej wyjść?

Już Ci @opp34 podpowiedział musisz użyć instrukcji break; wcześniej sprawdzić stan przycisku i jeśli wciśnięty to wyjście 

Link do komentarza
Share on other sites

Dnia 19.04.2021 o 13:09, ethanak napisał:

Pytanie: po co while w loop1 i loop2? Przecież i tak wykonuje się pętla while w której siedzi loop()

Co się stanie, jeśli zrobisz po prostu:
 


void loop1()
{
  Serial.println("1");
}

void loop2()
{
  Serial.println("2");
}

 

Mój główny program posiada funkcje, które mają zmieniać opcje sterowania silnikami gdy kliknę na przycisk. Jeśli nie dam tego w pętli to zmiana sterowania działa tylko gdy trzymam przycisk a gdy puszczę wraca do pierwotnego sterowania. Być może coś robię źle, ale nie udało mi się zrobić tego bez użycia pętli. 

Link do komentarza
Share on other sites

9 minut temu, MrReus napisał:

Być może coś robię źle, ale nie udało mi się zrobić tego bez użycia pętli. 

Mozesz uzyc petli tylko trzeba uzywac jej prawidlowo, a zapis while(true) nie jest prawidlowy..(w twoim przypadku oczywiscie)..to co Ci wyzej napisalem rozwiazuje twoj problem

Link do komentarza
Share on other sites

A nie prościej zapamiętać stan przycisku?

Pętla w pętli, sprawdzanie stanu przycisku w kilku miejscach, najlepszy sposób na niepotrzebne komplikacje i dziwne błędy.

uint8_t stan=0;

void loop()
{
  if (digitalRead(Button1) == LOW) stan = 1;
  if (digitalRead(Button2) == LOW) stan = 2;
  switch(stan) {
    case 1:
      loop1();
      break;
     
    case 2:
      loop2();
      break;
  }
}

Coś w tę stronę (oczywiście w loop1 i loop2 nie ma żadnych while)

Edytowano przez ethanak
Link do komentarza
Share on other sites

5 godzin temu, farmaceuta napisał:

Mozesz uzyc petli tylko trzeba uzywac jej prawidlowo, a zapis while(true) nie jest prawidlowy..(w twoim przypadku oczywiscie)..to co Ci wyzej napisalem rozwiazuje twoj problem

Przeanalizowałem twój kod, użyłem do obu funkcji i działa bardzo dobrze, ale natknąłem się na mały problem: mianowicie pętle zmieniają się tylko wtedy gdy naciśnięcie przycisku jest natychmiastowe. Gdy go przytrzymam, lub nie wcisnę wystarczająco szybko to nic się nie dzieje. Da się coś z tym zrobić? 

Link do komentarza
Share on other sites

 

9 minut temu, MrReus napisał:

ale natknąłem się na mały problem: mianowicie pętle zmieniają się tylko wtedy gdy naciśnięcie przycisku jest natychmiastowe. Gdy go przytrzymam, lub nie wcisnę wystarczająco szybko to nic się nie dzieje. Da się coś z tym zrobić? 

Da sie...jak wiadomo kod wykonuje sie strasznie szybko..wiec zanim zdazysz puscic guzik to przeskoczysz juz dalej...dodaj zwykly delay() w kazde miejsce gdzie uzywasz przycisku np.

if (digitalRead(guzik) == LOW) {
  delay(1000); ///tutaj
  stan = 0;
  loop1();
  }

void loop1() {
  
while (stan == 0) {
  //kod
  if (digitalRead(guzik) == LOW) {
    delay(1000); /// tutaj
    stan = 1;
    }

na poczatek delay() wystarczy...ale w bardziej zaawansowanych kodach to by nas za te delay'e powiesili..😉 teraz masz 1 sekunde na to zeby zdazyc puscic guzik...przeanalizuj rowniez przyklad kolegi @ethanak z case'ami bo to tez dobre i proste rozwiazanie, z tym ze po zakonczeniu case wychodzisz z niego i lecisz caly kod odnowa do ponownego wejscia w case...

Link do komentarza
Share on other sites

16 godzin temu, farmaceuta napisał:

na poczatek delay() wystarczy...ale w bardziej zaawansowanych kodach to by nas za te delay'e powiesili..😉 teraz masz 1 sekunde na to zeby zdazyc puscic guzik...przeanalizuj rowniez przyklad kolegi @ethanak z case'ami bo to tez dobre i proste rozwiazanie, z tym ze po zakonczeniu case wychodzisz z niego i lecisz caly kod odnowa do ponownego wejscia w case...

Rozumiem, że w tym przypadku mam zadeklarować Button1 i Button2 na ten sam pin? Po wstępnej próbuje opcja z case'ami wrzuca mnie do mojego "loop2" i nie wychodzi po ponownym kliknięciu. 

Link do komentarza
Share on other sites

27 minut temu, MrReus napisał:

Rozumiem, że w tym przypadku mam zadeklarować Button1 i Button2 na ten sam pin

Nie nie...z regoly tak sie nie robi...a robi sie tak ze jeden pin to jedna nazwa...

Reszty nie zgadne bo nie wiem co masz w tych loop1/2...ktore zreszta nie sa Ci do niczego potrzebne...umiesc dwie petle while w loop() i po krzyku...

uint8_t stan = 0;

void loop() {
  
  stan_guzik();

  while (stan == 1) {
  //kod pierwszy
  stan_guzik();
}
  while (stan == 2) {
  //kod drugi
  stan_guzik();
}

void stan_guzik() {
  if (digitalRead(guzik) == LOW) {
  delay(1000); ///tutaj
  stan++;
  if (stan > 2) {
    stan = 1;
  }
}
  }

Moze takie cos...tylko musisz pamietac ze nie ma tutaj zabezpieczenia przed zbyt dlugim wcisnieviem guzika...tak samo jesli nawalisz delay'ow do while to bedzie to kulawo chodzic...

A najlepiej poczytaj tu na forum...bo to chyba juz 3 temat w tym tygodniu ktory porusza ten sam problem...czyli przelaczanie sie miedzy podprogramami..

Edytowano przez farmaceuta
Link do komentarza
Share on other sites

13 minut temu, farmaceuta napisał:

Nie nie...z regoly tak sie nie robi...a robi sie tak ze jeden pin to jedna nazwa...

Reszty nie zgadne bo nie wiem co masz w tych loop1/2...ktore zreszta nie sa Ci do niczego potrzebne...umiesc dwie petle while w loop() i po krzyku...

Chciałem zrozumieć kod @ethanak z tym case'ami i dlaczego są dwa Buttony. Ale zostanę chyba przy Twojej wersji bo działa bardzo dobrze i w sumie rozwiązało to mój problem.

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.