[081] Układamy wiedzę - cz. 7

[081] Układamy wiedzę - cz. 7

Przypomnę: omawiałem dotąd takie instrukcje sterujące jak if w różnych formach rozbudowania (występująca także z else) oraz while. Pozostała nam niezbędna for, przydatna switch i pozostałe, już o wiele rzadziej używane, których w zasadzie mogłoby nie być. Ale nie dotyczy to for, bez której nie ma programowania, choć nie do końca, bo teoretycznie można by ją uczynić z funkcji if, acz byłoby to nieeleganckie.


Używając Płytki Edukacyjnej TME zróbmy sobie szkoleniowy symulator świateł dla pieszych. Jak wiadomo, przez jakiś czas świeci się światło czerwone – skrócimy go do trzech sekund, potem zielone, w końcu zielone zaczyna migać i proces zaczyna się od początku. Najpierw zrobimy symulację takiego urządzenia w sposób najbardziej prymitywny, ale pozbawiony jakichkolwiek warunków. Wstawiłem tu szereg instrukcji włączających bądź wyłączających świecenie diod i pętle opóźniające.

const byte diodaCzerwona = 9;  // Numer pinu, do którego podłączona jest dioda czerwona.
const byte diodaZielona = 10;  // Numer pinu, do którego podłączona jest dioda zielona.

void setup() {
  pinMode(diodaCzerwona, OUTPUT);  // Zadeklaruj porty diod jako wyjścia.
  pinMode(diodaZielona, OUTPUT);
}

void loop() {
                                      // Czerwone światło.
  digitalWrite(diodaZielona, LOW);    // Wyłącz diodę zieloną.
  digitalWrite(diodaCzerwona, HIGH);  // Włącz diodę czerwoną.
  delay(3000);                        // Zaczekaj trzy sekundy.

                                      // Zielone światło.
  digitalWrite(diodaCzerwona, LOW);   // Wyłącz diodę czerwoną.
  digitalWrite(diodaZielona, HIGH);   // Włącz diodę zieloną.
  delay(3000);                        // Zaczekaj trzy sekundy.

                                      // Migamy zielonym światłem.
  digitalWrite(diodaZielona, LOW);    // Wyłącz diodę zieloną.
  delay(250);                         // Zaczekaj ćwierć sekundy.
  digitalWrite(diodaZielona, HIGH);   // Włącz diodę zieloną.
  delay(250);                         // Zaczekaj ćwierć sekundy.
  digitalWrite(diodaZielona, LOW);
  delay(250);
  digitalWrite(diodaZielona, HIGH);
  delay(250);
  digitalWrite(diodaZielona, LOW);
  delay(250);
  digitalWrite(diodaZielona, HIGH);
  delay(250);
  digitalWrite(diodaZielona, LOW);
  delay(250);
  digitalWrite(diodaZielona, HIGH);
  delay(250);
  digitalWrite(diodaZielona, LOW);
  delay(250);
  digitalWrite(diodaZielona, HIGH);
  delay(250);
}

Ponieważ z daleka już wygląda to, delikatnie mówiąc, nieelegancko, wykorzystajmy funkcję while do uproszczenia programu.

                                       // Migamy zielonym światłem.
  liczbaPowtorzen = 5;                 // Liczba mrugnięć.
  while (liczbaPowtorzen > 0) {        // Powtarzaj dopóki liczba mrugnięć nie osiągnie zera.
    digitalWrite(diodaZielona, LOW);   // Wyłącz diodę zieloną.
    delay(250);                        // Zaczekaj ćwierć sekundy.
    digitalWrite(diodaZielona, HIGH);  // Włącz diodę zieloną.
    delay(250);                        // Zaczekaj ćwierć sekundy.
    liczbaPowtorzen--;                 // Zmniejsz liczbę powtórzeń o jeden.
  }

W tym celu musimy zadeklarować zmienną licznika mrugnięć, którą nazwałem liczbaPowtorzen. Przed wejściem w tę część programu, która odpowiada za miganie zielonym światłem, „nakręcamy” licznik na pięć cykli. Pierwsza linia bada, czy aby licznik się nie wyzerował. Jeśli nie, przeprowadzamy jeden cykl mignięcia, czyli ćwierć sekundy świecenia i ćwierć ciemności. Na końcu zmniejszamy licznik o jeden i wracamy na początek bloku – aż się licznik wyzeruje.

Właściwie można by to tak zostawić, lecz do takich celów mamy lepszą funkcję: for. Przemodelujmy nasz program.

                                                                       // Migamy zielonym światłem.
  for (liczbaPowtorzen = 5; liczbaPowtorzen > 0; liczbaPowtorzen--) {  // Powtarzaj dopóki liczba mrugnięć nie osiągnie zera.
    digitalWrite(diodaZielona, LOW);                                   // Wyłącz diodę zieloną.
    delay(250);                                                        // Zaczekaj ćwierć sekundy.
    digitalWrite(diodaZielona, HIGH);                                  // Włącz diodę zieloną.
    delay(250);                                                        // Zaczekaj ćwierć sekundy.
  }

Poprzedni, mniej czytelny układ zredukował się do jednej linii. Mamy w niej trzy elementy: pierwszy (liczbaPowtorzen=5) mówi o początkowym stanie zmiennej, drugi (liczbaPowtorzen>0)o warunku wykonania pętli, w którym bada się tą zmienną, a trzeci (liczbaPowtorzen--) – o tym, co się ma dziać ze zmienną po każdym przebiegu pętli. Możemy więc startować od dowolnej wartości, czekać także na dowolną i modyfikować ją w każdym przebiegu nie tylko z takim prostym krokiem „o jeden”, ale jak zechcemy, łącznie z przyrostem na przykład wykładniczym. Możemy ją także dynamicznie zmieniać, wręcz zawracając liczenie, jeśli potrzeba – wówczas dostaniemy nieskończone zapętlone „wahadełko”. W końcu zmienna ta może się przydać wewnątrz pętli, do jakichś działań, gdzie będzie wymagany konkretny skok co cykl. W porównaniu z poprzednim przykładem, tak jest dużo prościej i ładniej i właśnie do takich celów służy instrukcja for.

Jeszcze słówko o rzeczy. Instrukcje te są zwykle używane do odliczania czegoś skończonego, dokładnie jak w przykładzie. Najczęściej nie interesuje nas sama zmienna, gdyż służy tylko odliczaniu i niczemu więcej. Takie lokalne zmienne są… właśnie tym, o czym mówię – lokalnymi zmiennymi, które deklaruje się na miejscu, a nie na początku programu.

                                       // Migamy zielonym światłem.
  for (byte x = 5; x > 0; x--) {       // Powtarzaj dopóki liczba mrugnięć nie osiągnie zera.
    digitalWrite(diodaZielona, LOW);   // Wyłącz diodę zieloną.
    delay(250);                        // Zaczekaj ćwierć sekundy.
    digitalWrite(diodaZielona, HIGH);  // Włącz diodę zieloną.
    delay(250);                        // Zaczekaj ćwierć sekundy.
  }

Mają tę zaletę, że po wykorzystaniu są zapominane, że tak powiem i można ich używać w różnych miejscach, oczywiście jeśli tylko nie wchodzą ze sobą w konflikt. Ja osobiście nadaję im proste nazwy, w stylu X, Y, Z, w przeciwieństwie do tych, które nazwami zdradzają sens swojego istnienia. To jednak nie jest obowiązująca zasada, a tylko wygodny zwyczaj.

Czas poznać odmianę instrukcji while, która zawsze występuje razem z nią. Teraz jednak taka pętla zaczyna się od słowa do. Uczyńmy kolejny przykład, może nieco abstrakcyjny, ale i konstrukcja do… while bywa taka trochę i rzadko się jej używa.

const byte diodaCzerwona = 9;  // Numer pinu, do którego podłączona jest dioda czerwona.
const byte diodaZielona = 10;  // Numer pinu, do którego podłączona jest dioda zielona.
const byte kasujAlarm = 8;     // Numer pinu, do którego podłączony jest pstryczek kasujący alarm.
byte liczbaPowtorzen;          // Licznik powtórzeń migania.

void setup() {
  pinMode(diodaCzerwona, OUTPUT);     // Zadeklaruj porty diod jako wyjścia.
  pinMode(diodaZielona, OUTPUT);
  pinMode(kasujAlarm, INPUT_PULLUP);  // Zadeklaruj port pstryczka jako wejścia.
}
void loop() {
                                     // Stan niealarmowy - zielone światło.
  digitalWrite(diodaCzerwona, LOW);  // Wyłącz diodę czerwoną.
  digitalWrite(diodaZielona, HIGH);  // Włącz diodę zieloną.
                                     // Tutaj znajduje się reszta programu...
  delay(3000);                       // Zaczekaj trzy sekundy.

                                              // Stan alarmowy - czerwone światło.
  do {                                        // Początek pętli, którą będziemy powtarzać dopóki nie nastąpi warunek znajdujący się na końcu.
    digitalWrite(diodaZielona, LOW);          // Wyłącz diodę zieloną.
    digitalWrite(diodaCzerwona, HIGH);        // Włącz diodę czerwoną.
                                              // Tutaj znajduje się reszta programu obsługująca alarmy...
  } while (digitalRead(kasujAlarm) == LOW);   // Powtarzaj powyższy blok, dopóki nie zostanie wciśnięty pstryczek.
}

Dajmy na to, że mamy dwa bloki programu. W jednym, nazwijmy go – stanem niealarmowym, coś tam się dzieje, dla nas jest to teraz bez znaczenia i jest to stan domyślny, potwierdzany świeceniem zielonej diody. Jednak może się wydarzyć coś, co nakazuje wykonanie bloku obsługi alarmu. U nas wystarczy, że minie czas równy trzem sekundom – stąd ta wspomniana abstrakcja. I teraz różnica zasadnicza. W poprzednich przykładach pętla while wykonywała się zgodnie z pewnym warunkiem. Mogło się zdarzyć, że warunek od początku był niezgodny z oczekiwaniami, więc pętla w ogóle się nie wykonywała. Czasem tak nie można. Na przykład gdy wystąpi alarm, trzeba coś zrobić. U nas – zaświecić czerwoną diodę. Musi to być zrobione zawsze i dopiero gdy zostanie zrobione, można badać warunek opuszczenia pętli. W naszym abstrakcyjnym przykładzie musi być wciśnięty przycisk. Zauważmy jednak, że nawet jeśli będziemy trzymać ten przycisk bez przerwy, i tak dioda na chwilę mrugnie, bo raz ten fragment pętli musi się wykonać. W tym tkwi istota pętli do… whilezawsze przynajmniej raz muszą się wykonać instrukcje w niej zawarte.

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2025 arduino.pl