[078] Układamy wiedzę - cz. 4
Po naszych przygodach ze wszystkimi odmianami funkcji if czas na kolejny krok. Przyjrzyjmy się teraz instrukcji while Kolejny szkic ukaże nam modelowe sterowanie klawiaturą typu: plus-minus. Do takich rzeczy właśnie ta instrukcja świetnie się nadaje.
byte pstryczekSciemnij = 4; // Numer pinu, do którego podłączony jest pstryczek ściemniający diodę.
byte pstryczekZjasnij = 7; // Numer pinu, do którego podłączony jest pstryczek rozjaśniający diodę.
byte diodaSwiecaca = 9; // Numer pinu, do którego podłączona jest dioda świecąca.
byte jasnosc = 128; // Poziom jasności diody.
void setup() {
pinMode(pstryczekSciemnij, INPUT_PULLUP); // Zadeklaruj porty pstryczków jako wejścia.
pinMode(pstryczekZjasnij, INPUT_PULLUP);
pinMode(diodaSwiecaca, OUTPUT); // Zadeklaruj port diody jako wyjście.
}
void loop() {
while (digitalRead(pstryczekSciemnij) == HIGH) { // Jeśli pstryczek ściemniający zostanie wciśnięty...
jasnosc--; // Zmniejsz poziom jasności o jeden.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij tę wartość na port diody.
delay(10); // Zaczekaj 10 ms
}
while (digitalRead(pstryczekZjasnij) == HIGH) { // Jeśli pstryczek rozjaśniający zostanie wciśnięty...
jasnosc++; // Zwiększ poziom jasności o jeden.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij tę wartość na port diody.
delay(10); // Zaczekaj 10 ms
}
}
Powołamy do życia parę przycisków do rozjaśniania pstryczekZjasnij i ściemniania pstryczekSciemnij diody, samą diodę diodaSwiecaca – podłączoną do pinu, który może wystawiać przebieg o zmiennym wypełnieniu i zmienną, którą nazwiemy: jasnosc. W głównej pętli będą dwa bliźniacze bloki warunków. Pierwszy będzie analizował stan przycisku ściemniającego. Dopóki będzie on wciśnięty, blok nie zostanie opuszczony, tylko będzie wykonywał się bez końca, za każdym razem zmniejszając zmienną jasność o jeden i wyrzucając jej wartość na port diody, pracujący obecnie jako źródło zmieniającego się napięcia, proporcjonalnego do wartości tam wyrzucanej. Innymi słowy – trzymamy przycisk i dioda gaśnie. Ale tak po prostu działoby się to zbyt szybko, więc dołożyłem funkcję delay zwalniającą proces gaszenia.
Drugi blok robi dokładnie to samo, tylko rozjaśnia diodę. Gdy żaden z przycisków nie jest wciśnięty, oba warunki będą pomijane. Program jest bardzo prosty i nie ma tu zabezpieczenia przez tak zwanym zawinięciem się zmiennej jasność. Innymi słowy, jak będziemy przycisk rozjaśniający trzymać zbyt długo, dioda zgaśnie nagle i zacznie się rozjaśniać od początku. Dlaczego? Po przekroczeniu 255 zmienna jasnosc stała się zerem, a wtedy dioda się nie świeci. W drugą stronę jest analogicznie: zero minus jeden dla zmiennej typy byte równa się 255, więc po ściemnieniu dioda nagle zapali się pełnym światłem. Poprawmy to!
void loop() {
while (digitalRead(pstryczekSciemnij) == HIGH) { // Jeśli pstryczek ściemniający zostanie wciśnięty...
jasnosc--; // Zmniejsz poziom jasności o jeden.
if (jasnosc == 255) { jasnosc = 0; } // Zastaw pułapkę na zawinięcie zmiennej.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij tę wartość na port diody.
delay(10); // Zaczekaj 10 ms
}
while (digitalRead(pstryczekZjasnij) == HIGH) { // Jeśli pstryczek rozjaśniający zostanie wciśnięty...
jasnosc++; // Zwiększ poziom jasności o jeden.
if (jasnosc == 0) { jasnosc = 255; } // Zastaw pułapkę na zawinięcie zmiennej.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij tę wartość na port diody.
delay(10); // Zaczekaj 10 ms
}
}
Dodamy do każdego bloku po jednej linii. Jeśli zmienna jasność będzie wynosiła 255, co oznacza, że się właśnie przewinęła, wyzerujemy ją z powrotem, czyli jak gdyby odkręcimy to, co się stało wiersz wyżej. W drugim bloku zrobimy to samo, tylko na odwrót. Teraz już trzymanie pstryczka w nieskończoność nie zaświeci nam nagle diody po całkowitym zgaszeniu.
void loop() {
while (digitalRead(pstryczekSciemnij) == HIGH && (jasnosc != 0)) { // Jeśli pstryczek ściemniający zostanie wciśnięty i jasność nie wynosi zero...
jasnosc--; // Zmniejsz poziom jasności o jeden.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij tę wartość na port diody.
delay(10); // Zaczekaj 10 ms
}
while (digitalRead(pstryczekZjasnij) == HIGH && (jasnosc != 255)) { // Jeśli pstryczek rozjaśniający zostanie wciśnięty i jasność nie wynosi 255...
jasnosc++; // Zwiększ poziom jasności o jeden.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij tę wartość na port diody.
delay(10); // Zaczekaj 10 ms
}
}
Można to także zrobić inaczej. Niech while bada dwa warunki jednocześnie: wciśnięcie przycisku i przyjęcie skrajnej wartości jasności. Jeśli zostanie ona osiągnięta – zero albo 255 odpowiednio dla każdego z bloków, instrukcje w blokach w ogóle nie będą wykonywane. Przypominając sobie poprzednie informacje, stwórzmy podprogram o nazwie podprogram z tego, co nam się dubluje. Od razu widać tu dwie lnie.
void setup() {
pinMode(pstryczekSciemnij, INPUT_PULLUP); // Zadeklaruj porty pstryczków jako wejścia.
pinMode(pstryczekZjasnij, INPUT_PULLUP);
pinMode(diodaSwiecaca, OUTPUT); // Zadeklaruj port diody jako wyjście.
podprogram(); // Skocz do podprogramu.
}
void loop() {
while (digitalRead(pstryczekSciemnij) == HIGH && (jasnosc != 0)) { // Jeśli pstryczek ściemniający zostanie wciśnięty i jasność nie wynosi zero...
jasnosc--; // Zmniejsz poziom jasności o jeden.
podprogram(); // Skocz do podprogramu.
}
while (digitalRead(pstryczekZjasnij) == HIGH && (jasnosc != 255)) { // Jeśli pstryczek rozjaśniający zostanie wciśnięty i jasność nie wynosi 255...
jasnosc++; // Zwiększ poziom jasności o jeden.
podprogram(); // Skocz do podprogramu.
}
}
void podprogram() { // Tu jest podprogram.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij wartość jasności na port diody.
delay(10); // Zaczekaj 10 ms
}
Wiele nie zaoszczędzimy, ale postąpimy zgodnie ze sztuką. A przy okazji, wymusiłem wysłanie wartości początkowej jasności po resecie, bo dotąd, dopóki nic nie wcisnęliśmy, dioda nie świeciła się w ogóle, a jak widać – początkowo ustawiłem jej wartość w połowie, czyli na 128
To nie koniec instrukcji warunkowych, ale dziś przerwijmy ten temat, bo pojawiło się coś istotnego. Rzućmy jeszcze okiem w pierwszy przykład. Mamy tam dwa znaki równa się.
while (digitalRead(pstryczekSciemnij) == HIGH) {...
Po co dwa? Bo tak. Gdybyśmy wstawili tylko jeden, dostaniemy błąd. Takie są po prostu zasady języka C przy porównywaniach. Jeśli elementy są sobie równe logicznie, wyrażenie jest prawdziwe – w tym wypadku wykonają się instrukcje w nawiasie wąsatym. Możemy jednak zażyczyć sobie innych warunków. Zaprzeczenie prawdzie sprowadza się do wymiany jednego znaku równości na wykrzyknik.
void loop() {
if (digitalRead(pstryczek) != HIGH) { // Jeśli pstryczek nie jest wciśnięty...
digitalWrite(diodaSwiecaca, HIGH); // Włącz diodę.
}
if (digitalRead(pstryczek) != LOW) { // Jeśli pstryczek nie jest puszczony...
digitalWrite(diodaSwiecaca, LOW); // Wyłącz diodę.
}
}
Proszę, użyjemy zaprzeczenia i… urządzenie działa na odwrót. Dioda gaśnie, gdy przycisk jest wciśnięty. W przykładzie z potencjometrem z poprzedniego artykułu mieliśmy znaki większości.
if (analogRead(potencjometr) > 767) {...
Można je zastąpić znakami większe bądź równe:
if (analogRead(potencjometr) >= 768) {...
Dla programu nie ma to żadnego znaczenia, ale dla naszych oczu ma. Nie trzeba pamiętać o odejmowaniu jedynki od tych wszystkich połówek i trzech czwartych zakresów i tak jest bardziej elegancko.
I jeszcze słówko o łączeniach warunków. Taki przykład był wówczas, gdy musiał naraz być wciśnięty pstryczek i do tego jasność musiała być niezerowa.
while (digitalRead(pstryczekSciemnij) == HIGH && (jasnosc != 0)) {...
Naraz, koniunkcja, iloczyn logiczny, czyli każdy z warunków musi zaistnieć. Warunki wymagane jednocześnie łączymy dwoma znakami and (&&) Mamy jeszcze dwa przypadki: jeśli wystarczy jeden z wielu warunków, użylibyśmy dwóch pionowych kresek (||) To jest tak zwana alternatywa, czyli suma logiczna. Można by go wykorzystać do obsługi jednej diody dwoma przyciskami, z których każdy mógłby ją włączać. W końcu możemy użyć jeszcze zaprzeczenia (!) które już poznaliśmy, ale tym razem nie będziemy żądać wartości różnej od zera, a zaprzeczenia wartości równej zero.
void loop() {
while (digitalRead(pstryczekSciemnij) == HIGH && !(jasnosc == 0)) { // Jeśli pstryczek ściemniający zostanie wciśnięty i nie będzie jasność wynosić zero...
jasnosc--; // Zmniejsz poziom jasności o jeden.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij tę wartość na port diody.
delay(10); // Zaczekaj 10 ms
}
while (digitalRead(pstryczekZjasnij) == HIGH && (jasnosc != 255)) { // Jeśli pstryczek rozjaśniający zostanie wciśnięty i jasność nie wynosi 255...
jasnosc++; // Zwiększ poziom jasności o jeden.
analogWrite(diodaSwiecaca, jasnosc); // Wyślij tę wartość na port diody.
delay(10); // Zaczekaj 10 ms
}
}
Zmieniłem to wyrażenie tylko w pierwszym bloku, opisując zmianę w komentarzu. Ot, taka subtelna różnica, ale czasem może mieć znaczenie, zwłaszcza podczas tworzenia bardziej skomplikowanych wyrażeń.