[011] Odmierzanie czasu

[011] Odmierzanie czasu

O bibliotekach już pisałem, gdy potrzebowaliśmy nawiązać współpracę z wyświetlaczem. Użyliśmy wtedy biblioteki LiquidCrystal, którą twórcy Arduino IDE ściągnęli domyślnie. Tutaj przyjrzymy się kolejnej bibliotece, która zajmuje się sprawami związanymi z funkcjami zegarowymi. Tym razem może się zdarzyć, że biblioteka, o której zaraz opowiem, nie została jeszcze ściągnięta i podczas kompilacji dowiemy się o tym. W takich przypadkach biblioteki należy ściągać ręcznie. Robi się to prosto, klikając w trzecią ikonkę z lewej strony, symbolizującą poukładane książki albo wprost z menu, wybierając Narzędzia/Zarządzaj bibliotekami. W okienku należy wpisać jej nazwę, czyli TimeLib.h i po znalezieniu zainstalować ją. W tym wypadku widzimy, że biblioteka była już zainstalowana. Tak na marginesie, bibliotek zegarowych znajdziemy kilka, ale ja tutaj wybrałem właśnie tą.


Teraz już do szkicu z poprzedniego artykułu będziemy mogli dopisać kolejny wiersz, który dołączy co dopiero ściągniętą bibliotekę.

#include <TimeLib.h>  // To jest biblioteka obsługi zegara.

Zróbmy sobie teraz użytek z pustych przebiegów, gdy to nasze urządzenie nic nie robiło, tylko czekało na wciśnięcie przycisków. Na początek napiszemy sobie kilka instrukcji, ale jak gdyby osobno, poza głównymi blokami oznaczonymi słowami void setup oraz void loop. Bloki te, jak już pisałem, są obowiązkowe i trochę nie miałyby sensu, gdyby nie fakt, że bloków zaczynających się od słowa void może być więcej. Po co tak? Żeby rozbić duże programy na szereg mniejszych, niezależnych elementów. Celów to ma wiele, dla nas obecnie ważne będą dwa: w ten sposób program staje się czytelniejszy, bo każdy taki blok możemy traktować jak jedną superinstrukcję. Drugim celem jest oszczędność: co się powtarza, można ująć w taki blok i w miejscach, gdzie oryginalnie program korzystał z jego treści, zrobić odwołanie do owego bloku.

Zaczniemy od stworzenia takiego bloku. W tym celu musimy iść na sam koniec programu, poza ostatnią klamrę i wstawić tam następującą treść:

void sekundnik()  // To jest podprogram rysujący sekundnik.
{
  lcd.setCursor(0, 1);      // Ustawiamy kursor na samym początku drugiego wiersza wyświetlacza.
  lcd.print(" Juz ");       // Napiszemy "Juz "
  lcd.print(now());         // Wyciągniemy ilość sekund od chwili zresetowania timera
  lcd.print(" sekund   ");  // I dopiszemy " sekund".
}

Struktura bardzo przypomina bloki void setup albo void loop, także mamy nawiasy okrągłe, wąsate, słowo void, ale teraz mamy także swojsko brzmiącą nazwę bloku, która u mnie brzmi „sekundnik”. Nazwa ta może być dowolna i najlepiej, żeby kojarzona była z celem istnienia bloku.

lcd.setCursor już znamy, ustawia kursor w konkretnym miejscu, tutaj: w dolnym wierszu, na jego początku. lcd.print także już znamy: rysuje teksty na wyświetlaczu. Pojawiła się nowa funkcja o nazwie now() Jest ona związana z naszą nową biblioteką, a z dokumentacji tejże możemy się dowiedzieć, że zwraca ona ilość sekund od zainicjowania zegara, czyli w praktyce od resetu. Tak więc wiersz:

lcd.print(now());

narysuje nam na wyświetlaczu ilość sekund, które upłynęły od włączenia urządzenia. Ale jak sprawić, by program zaglądał do tego naszego nowego bloku? Bardzo prosto: w każdej pętli, która czeka na wciśnięcie bądź puszczenie przycisku, należy wstawić instrukcję:

sekundnik();

Jak więc widać, odwołanie się do podprogramu wymaga napisania wyłącznie naszej wybranej nazwy i dwóch nawiasów. Te nawiasy nie są tutaj tak bez sensu, bowiem w nich umieszcza się czasem przekazywane do podprogramów dane. Ale to innym razem.

Jak mówiłem, instrukcję tą powielimy w każdym miejscu, gdzie wystąpiła funkcja while, żeby sekundnik wyświetlał się nam w każdej pozycji przełączników, także gdy trzymamy je wciśnięte. Ale żeby ta nowa właściwość naszego komputera toaletowego miała sens, dodamy jeszcze jedną funkcję. Ją także umieścimy w drugim już podprogramie, tym razem o nazwie „wyzeruj”.

void wyzeruj()  // A to jest podprogram, który ustawia kursor i zeruje zegar.
{
  lcd.home();  // Ustawiamy kursor na początku wyświetlacza.
  setTime(0);  // Zerujemy czas.
}

Nowa instrukcja, związana z nową biblioteką, brzmi: setTime (0) i zeruje wartość zegara, jak po resecie. Jeśli dodamy ją we wszystkich miejscach po wykryciu wciśnięcia bądź puszczenia któregoś z przycisków, dostaniemy interesujący stoper, który będzie odmierzał poszczególne etapy... przesiadywania w miejscu odosobnienia ;) Czas, gdy w środku nie będzie nikogo (oznaczony wyświetleniem napisu „Wolne”) także będzie rejestrowany.

Dlaczego znalazła się tu jeszcze instrukcja lcd.home? Ponieważ skorzystamy tutaj z nowo powstałej możliwości optymalizacji. Otóż odwołanie się do tego podprogramu występuje czterokrotnie, w miejscach, w których pierwotnie znajdowała się właśnie instrukcja lcd.home. O ile tworzenie podprogramów dla jednej tylko instrukcji nie ma sensu, gdy będzie ich tam więcej, warto wydzielić takie bloki, nawet jeśli nie wpłynie to na czytelność programu (a czasem wręcz się ona zmniejszy). Ale zaoszczędzimy na bajtach i długości kodu. Pamiętajmy: Wszystko, co się powiela, winno być wydzielone do osobnego bloku, żeby istniało tylko w jednej kopii. A w tych wszystkich dziurach po usunięciu powielonych fragmentów należy wstawić instrukcję, która będzie się do owego bloku odwoływać. To jednak nie koniec naszych czynności optymalizacyjnych. Spójrzmy na ten blok:

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

Po naszych przeróbkach coś tutaj jakby nie ma sensu. Dwukrotnie bowiem sprawdzamy warunek pozostawienia wszystkich przycisków wolnymi (tj. niewciśniętymi). Możemy spokojnie pozbyć się pierwszego warunku, bo przecież tutaj i tak program wpada dopiero, gdy żaden przycisk nie jest wciśnięty. Ale drugi warunek musi pozostać, by rysowanie napisu „Wolne” nie trwało bez końca. W tej pętli za to bez końca trwa rysowanie sekundnika. Zatem optymalizacja nie jest idealna, acz przynajmniej pozbyliśmy się dwóch wierszy i teraz blok ten wygląda tak:

wyzeruj();
lcd.print("    Wolne :)    ");
while (digitalRead(pstryczekPierwszy) == HIGH && digitalRead(pstryczekDrugi) == HIGH && digitalRead(pstryczekTrzeci) == HIGH) {
  sekundnik();
}

I w ten sposób dorobiliśmy się wersji dwa zero „ustępowego komputera.” We wszelkich sporach terytorialno – czasowych wyświetlany stoper z pewnością pozwoli rozstrzygnąć je i ustalić toaletowy konsensus. Na koniec dzisiejszej przygody zamieszczę cały listing szkicu, który można sobie skopiować i przetestować u siebie.

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

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.
    wyzeruj();                                  // Ustawiamy kursor i zerujemuy zegar...
    lcd.print("    Zajete!     ");              // i wysyłamy do niego tekst "Zajęte".
  }
  while (digitalRead(pstryczekPierwszy) == LOW) {  // Czekamy, dopóki przycisk nie zostanie puszczony.
    sekundnik();                                   // Rysujemy sekundnik.
  }

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

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

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

void sekundnik()  // To jest podprogram rysujący sekundnik.
{
  lcd.setCursor(0, 1);      // Ustawiamy kursor na samym początku drugiego wiersza wyświetlacza.
  lcd.print(" Juz ");       // Napiszemy "Już "
  lcd.print(now());         // Wyciągniemy ilość sekund od chwili zresetowania timera
  lcd.print(" sekund   ");  // I dopiszemy " sekund".
}

void wyzeruj()  // A to jest podprogram, który ustawia kursor i zeruje zegar.
{
  lcd.home();  // Ustawiamy kursor na początku wyświetlacza.
  setTime(0);  // Zerujemy czas.
}

Oczywiście ustępowy komputer jest formą żartu, ale już zupełnie praktyczny zegar pojawi się niebawem.

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