[010] Pierwsze optymalizacje oraz podejmowanie decyzji

[010] Pierwsze optymalizacje oraz podejmowanie decyzji

Skoro już opanowaliśmy podstawy, jeśli chodzi o komunikację z wyświetlaczem, czas na coś bardziej zaawansowanego. Ale po kolei. W dawnych czasach, gdy na świecie pojawiły się komputery ośmiobitowe, nastał ogólnoświatowy szał nauki programowania w języku BASIC. Albowiem właściwie wszystkie te komputery po włączeniu odzywały się do ludzi w tym języku, utożsamianym z naturalnym środowiskiem łączącym komputer z człowiekiem. O ile miał on swoje zalety, a jego echa możemy znaleźć nawet tutaj, miał też swoje wady, z których główną było przyzwyczajanie do bałaganiarstwa. I właśnie nasz program z artykułu o wyświetlaczu alfanumerycznym jest ideologicznie typowym programem pisanym w BASIC-u z lat osiemdziesiątych. Bierzmy zatem miotłę i posprzątajmy nieco.


Największym problemem początkujących programistów jest myślenie „tu i teraz”. Rozentuzjazmowani podłączają różne elementy, by potem nimi sterować, odwołując się do konkretnych portów. Aż nagle trzeba element przesunąć, bo albo się nie mieści, albo trzeba podłączyć jeszcze coś, albo urządzenie trafiło na miesiąc do szuflady i kabelki porozłączały się. W każdym z tych wypadków należałoby prześledzić cały kod od początku do końca i zaktualizować co gdzie zostało przypięte. W przypadku małych programów nie będzie to wielki problem. Co innego gdy linii będzie kilka tysięcy.

Zasadą numer jeden jest stworzenie odrębnego bloku deklaracji. Tam, między innymi wpisuje się konkretne adresy przyłączonych elementów i nazywa się je jakimiś przyjaznymi nazwami. Jak się to robi? Oto nasze nowe linie.

int pstryczekPierwszy = 8;
int pstryczekDrugi = 9;
int pstryczekTrzeci = 10;

Deklaracje mają tę zaletę, że można używać ludzkich nazw, jakie komu pasują. Zatem pstryczek siedzący na porcie ósmym, którym będziemy wywoływać komunikat „Zajęte”, nazwę sobie pstryczekPierwszy. Trzeba tylko pamiętać, że w nazwach nie można używać spacji, a ilość znaków poza literami i cyframi jest ograniczona do kilku. Znajduje się tam ósemka – bo pstryczek jest podłączony do ósmego portu. A co oznacza tajemnicze „int” na początku? Przy deklaracjach należy określić co deklarujemy. W tym przypadku jest to numer portu, wynoszący konkretnie 8. Int oznacza liczbę całkowitą, jakich to używa się do numerowania portów na przykład. Jeszcze do tego wrócę, na razie poprzestańmy na tym, że tym zmiennej musi być podany i bez int wyskoczy nam błąd.

No to teraz można ulepszyć nasz program. Wszystkie odwołania do portu ósmego zamieniamy odwołaniami przez zmienną o miłej dla nas nazwie, czyli pstryczekPierwszy. Tak samo zrobimy z pstryczkami podłączonymi do portów 9 i 10 No i oczywiście w treści programu zastępujemy bezpośrednie odwołania do tych portów nazwami naszych nowych zmiennych.

#include <LiquidCrystal.h>
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);
int pstryczekPierwszy = 8;
int pstryczekDrugi = 9;
int pstryczekTrzeci = 10;
void setup() {
  lcd.begin(16, 2);
  pinMode(pstryczekPierwszy, INPUT_PULLUP);
  pinMode(pstryczekDrugi, INPUT_PULLUP);
  pinMode(pstryczekTrzeci, INPUT_PULLUP);
}
void loop() {
  if (digitalRead(pstryczekPierwszy) == LOW) {
    lcd.home();
    lcd.print("    Zajete!     ");
  }
  if (digitalRead(pstryczekDrugi) == LOW) {
    lcd.home();
    lcd.print(" Jeszcze moment ");
  }
  if (digitalRead(pstryczekTrzeci) == LOW) {
    lcd.home();
    lcd.print("Ciezka sprawa...");
  }
  if (digitalRead(pstryczekPierwszy) == HIGH && digitalRead(pstryczekDrugi) == HIGH && digitalRead(pstryczekTrzeci) == HIGH) {
    lcd.home();
    lcd.print("    Wolne :)    ");
  }
}

Można śmiało skompilować program i wysłać do Arduino. Zaręczam, że będzie działać dokładnie tak samo jak ten z poprzedniego artykułu. Teoretycznie w naszym programie przybyło linii, więc może się wydawać bardziej skomplikowany, ale przybyła też jedna wspaniała właściwość: jeśli sobie zmienimy porty, do których podłączymy nasze przełączniki, wystarczy wprowadzić zmianę tylko w jednym miejscu, zostawiając całą resztę programu w spokoju. I uwierzcie, że w przypadku dużych aplikacji bez tej możliwości wszystko by się niesamowicie skomplikowało.

No to teraz trzeba zrobić to samo z adresami wyprowadzeń wyświetlacza. Co prawda odwołujemy się do niego tylko raz, na początku programu, ale jednak zasada obowiązuje niezależnie od tego ile razy z niej potem skorzystamy. I co najważniejsze, tak będzie czytelniej.

int lcdKolumny = 16;
int lcdWiersze = 2;

  lcd.begin(lcdKolumny, lcdWiersze);

Zauważmy, że wymyśliłem sobie tu pewien klucz: nazwy składają się z części związanej z obsługiwanym przedmiotem, czyli „lcd” oraz funkcjami wyprowadzeń albo cechami wyświetlacza. Ale to jest mój klucz, każdy może sobie wymyślić swój, byle tylko był konsekwentny.

Drugą naczelną zasadą jest nanoszenie komentarzy czy też opisów. I tak naprawdę to jest robota dla nas samych – z przyszłości, gdy będziemy wracali do własnych dzieł, nie rozumiejąc czasem zupełnie co my – z przeszłości – mieliśmy na myśli. Komentarze rozpoczynają się od dwóch ukośników i zwykło je się stawiać za instrukcjami. A co tam należy napisać? A tego to ja nie wiem. Niech każdy sobie opisuje wszystko tak, jak mu potrzeba. Tutaj wykazałem się tak zwanym przesadyzmem, komentując także rzeczy oczywiste, ale na potrzeby tego tylko przykładu. Przy okazji, warto także funkcjonalne bloki podzielić pustym wierszem.

#include <LiquidCrystal.h>  // To jest biblioteka obsługi wyświetlacza, którą należy zaimportować poleceniem #include.

int pstryczekPierwszy = 8;  // Używamy trzech pstryczków do wywoływania komunikatów.
int pstryczekDrugi = 9;
int pstryczekTrzeci = 10;

int lcdRS = 2;  // "lcd" to oczywiście wyświetlacz, którego wyprowadzenia ponazywaliśmy tutaj.
int lcdEN = 3;
int lcdD4 = 4;
int lcdD5 = 5;
int lcdD6 = 6;
int lcdD7 = 7;

int lcdKolumny = 16;  // "Kolumny" to ilość kolumn na naszym wyświetlaczu,
int lcdWiersze = 2;   // a "Wiersze" to ilość wierszy.

LiquidCrystal lcd(lcdRS, lcdEN, lcdD4, lcdD5, lcdD6, lcdD7);  // Tutaj dostarcza się bibliotece wiedzy, gdzie co jest podłączone.

void setup() {                               // Tu zaczyna się program. Ta część wykona się jednorazowo.
  lcd.begin(lcdKolumny, lcdWiersze);         // Inicjujemy wyświetlacz, powiadamiając go o ilości kolumn i wierszy.
  pinMode(pstryczekPierwszy, INPUT_PULLUP);  // Deklarujemy linie pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
  pinMode(pstryczekDrugi, INPUT_PULLUP);
  pinMode(pstryczekTrzeci, INPUT_PULLUP);
}
void loop() {  // Tutaj zaczyna się część programu wykonująca się bez końca.

  if (digitalRead(pstryczekPierwszy) == LOW) {  // Sprawdzamy czy wciśnięto pstryczek pierwszy.
    lcd.home();                                 // Jeśli tak, to ustawiamy kursor na początku wyświetlacza...
    lcd.print("    Zajete!     ");              // i wysyłamy do niego tekst "Zajęte".
  }

  if (digitalRead(pstryczekDrugi) == LOW) {  // To samo robimy z pstryczkiem drugim...
    lcd.home();
    lcd.print(" Jeszcze moment ");
  }

  if (digitalRead(pstryczekTrzeci) == LOW) {  // Oraz z trzecim.
    lcd.home();
    lcd.print("Ciezka sprawa...");
  }

  if (digitalRead(pstryczekPierwszy) == HIGH && digitalRead(pstryczekDrugi) == HIGH && digitalRead(pstryczekTrzeci) == HIGH) {  // A tutaj lądujemy, gdy żaden z pstryczków nie jest wciśnięty.
    lcd.home();
    lcd.print("    Wolne :)    ");
  }
}

Na chwilę zapomnijmy o optymalizacji zapisu i prześledźmy jak działa nasz program w głównej pętli. Dopóki wciskamy tylko jeden przycisk, wszystko jest w porządku. Program będzie w nieskończoność ustawiał kursor na początku i wypisywał komunikat „Zajęte”. Działa to poprawnie, ale niezgodnie ze sztuką, ponieważ wypisywanie w kółko tego samego miliony razy nie ma sensu. Wystarczyłoby to zrobić raz i zaczekać, aż przycisk zostanie puszczony albo wciśnięty zostanie inny. A co będzie, gdy wciśniemy dwa albo trzy przyciski naraz? Katastrofa, bowiem najpierw wyświetli się napis „Zajęte” i prawie natychmiast „Jeszcze moment”. W praktyce będzie to wyglądać tak.

Oczywiście można ostrzec użytkowników, by nie wciskali dwóch przycisków jednocześnie, lecz kolejna generalna zasada informatyków mówi, że jak coś się da zrobić, to ktoś to zrobi na pewno i trzeba się na taką sposobność zabezpieczyć. Zaradźmy temu jakoś. Poznamy teraz kolejną funkcję, o nazwie while. Jest to funkcja warunkowa, jak if. Jeśli tę znaną nam już można przetłumaczyć na ludzkie „jeśli warunek z nawiasu okrągłego jest spełniony, zrób to co w nawiasie wąsatym albo idź sobie dalej”, tutaj tłumaczenie wygląda mniej więcej tak: „dopóki warunek z nawiasu okrągłego zachodzi, wykonuj rozkazy z nawiasu wąsatego”.

  while (digitalRead(pstryczekPierwszy) == LOW) {  // Czekamy, dopóki przycisk nie zostanie puszczony.
  }

No dobrze, tylko nasz wąsaty nawias nie zawiera niczego! Ale to nie jest błąd: jeśli nie trzeba nic robić, a jedynie czekać, rozkaz nadal działa, tyle że czeka i nic więcej nie robi. Tak długo, aż warunek przestanie zachodzić, czyli dopóki pstryczek pierwszy nie zostanie puszczony.

Pozostaje nam tylko dopisać takie same rozkazy w pozostałych dwóch blokach, zmieniając oczywiście numery pstryczków. Gdy teraz wciśniemy przycisk pierwszy, program będzie trwał bez końca w bloku związanym z jego obsługą. Wcześniej oczywiście wypisze tekst „Zajęte”, ale jednorazowo. Potem będzie gonił bez końca w tym jednym miejscu pętli while, sprawdzając wyłącznie czy przycisk wciąż jest wciśnięty. Żadne dane nie będą już do wyświetlacza płynąć, a całość – w jakimś tam minimalnym stopniu, ale jednak – nie będzie konsumować tyle energii.

Gdybyśmy w tym czasie równocześnie wcisnęli przycisk drugi, nic się nie stanie, bo program jest zainteresowany wyłącznie puszczeniem przycisku pierwszego. Natomiast gdy to nastąpi, od razu przejdzie do obsługi przycisku drugiego i dopóki tego nie puścimy, nie będzie reagował na przycisk pierwszy ani trzeci. I tak dalej.

Teraz program jest napisany dużo ładniej, choć nadal do ideału brakuje mu sporo. Ale kolejnym etapem naprawiania świata zajmiemy się kiedy indziej.

#include <LiquidCrystal.h>  // To jest biblioteka obsługi wyświetlacza, którą należy zaimportować poleceniem #include.

int pstryczekPierwszy = 8;  // Używamy trzech pstryczków do wywoływania komunikatów.
int pstryczekDrugi = 9;
int pstryczekTrzeci = 10;

int lcdRS = 2;  // "lcd" to oczywiście wyświetlacz, którego wyprowadzenia ponazywaliśmy tutaj.
int lcdEN = 3;
int lcdD4 = 4;
int lcdD5 = 5;
int lcdD6 = 6;
int lcdD7 = 7;

int lcdKolumny = 16;  // "Kolumny" to ilość kolumn na naszym wyświetlaczu,
int lcdWiersze = 2;   // a "Wiersze" to ilość wierszy.

LiquidCrystal lcd(lcdRS, lcdEN, lcdD4, lcdD5, lcdD6, lcdD7);  // Tutaj dostarcza się bibliotece wiedzy, gdzie co jest podłączone.

void setup() {                               // Tu zaczyna się program. Ta część wykona się jednorazowo.
  lcd.begin(lcdKolumny, lcdWiersze);         // Inicjujemy wyświetlacz, powiadamiając go o ilości kolumn i wierszy.
  pinMode(pstryczekPierwszy, INPUT_PULLUP);  // Deklarujemy linie pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
  pinMode(pstryczekDrugi, INPUT_PULLUP);
  pinMode(pstryczekTrzeci, INPUT_PULLUP);
}
void loop() {  // Tutaj zaczyna się część programu wykonująca się bez końca.

  if (digitalRead(pstryczekPierwszy) == LOW) {  // Sprawdzamy czy wciśnięto pstryczek pierwszy.
    lcd.home();                                 // Jeśli tak, to ustawiamy kursor na początku wyświetlacza...
    lcd.print("    Zajete!     ");              // i wysyłamy do niego tekst "Zajęte".
  }
  while (digitalRead(pstryczekPierwszy) == LOW) {  // Czekamy, dopóki przycisk nie zostanie puszczony.
  }

  if (digitalRead(pstryczekDrugi) == LOW) {  // To samo robimy z pstryczkiem drugim...
    lcd.home();
    lcd.print(" Jeszcze moment ");
  }
  while (digitalRead(pstryczekDrugi) == LOW) {
  }

  if (digitalRead(pstryczekTrzeci) == LOW) {  // Oraz z trzecim.
    lcd.home();
    lcd.print("Ciezka sprawa...");
  }
  while (digitalRead(pstryczekTrzeci) == LOW) {
  }

  if (digitalRead(pstryczekPierwszy) == HIGH && digitalRead(pstryczekDrugi) == HIGH && digitalRead(pstryczekTrzeci) == HIGH) {  // A tutaj lądujemy, gdy żaden z pstryczków nie jest wciśnięty.
    lcd.home();
    lcd.print("    Wolne :)    ");
    while (digitalRead(pstryczekPierwszy) == HIGH && digitalRead(pstryczekDrugi) == HIGH && digitalRead(pstryczekTrzeci) == HIGH) {
    }
  }
}

Powiązane tematy

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

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2024 arduino.pl