[041] Czym jest watchdog cz. 2

[041] Czym jest watchdog cz. 2

Skoro znamy już teoretyczne zagadnienia związane z watchdogiem, o których pisałem w tym artykule, czas na konkrety. W pierwszym przykładzie weźmiemy na warsztat nasz ostatni program, który przedstawiłem tutaj.


#include <avr/wdt.h>          // Biblioteka obsługująca watchdoga.
const byte opoznienie = 100;  // Opóźnienie po wciśnięciu pstryczków.

const byte pstryczek1 = 9;  // Port klawiatury.
const byte pstryczek2 = 10;
const byte pstryczek3 = 11;
const byte pstryczek4 = 12;

const byte pstryczekWDT = 8;  // Pstryczek, którym będziemy testować watchdoga.

const byte przekaznik1 = 4;  // Port przekaźników.
const byte przekaznik2 = 5;
const byte przekaznik3 = 6;
const byte przekaznik4 = 7;

bool przelacznik1 = false;  // Przełączniki.
bool przelacznik2 = false;
bool przelacznik3 = false;
bool przelacznik4 = false;

void setup() {
  wdt_enable(WDTO_2S);  // Włączamy watchdoga, będzie musiał być zerowany co dwie sekundy.

  pinMode(pstryczek1, INPUT_PULLUP);  // Deklaruj porty pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
  pinMode(pstryczek2, INPUT_PULLUP);
  pinMode(pstryczek3, INPUT_PULLUP);
  pinMode(pstryczek4, INPUT_PULLUP);
  pinMode(pstryczekWDT, INPUT_PULLUP);

  pinMode(przekaznik1, OUTPUT);  // Deklaruj porty przekaźników jako wyjścia.
  pinMode(przekaznik2, OUTPUT);
  pinMode(przekaznik3, OUTPUT);
  pinMode(przekaznik4, OUTPUT);
}

void loop() {
  while (digitalRead(pstryczekWDT) == HIGH) {}  // Testujemy watchdoga: po 2 sekundach trzymania pstryczka system powinien się zresetować.

  if (digitalRead(pstryczek1) == HIGH) {        // Jeśli wciśnięto pstryczek, to...
    przelacznik1 = !przelacznik1;               // Zaneguj stan przełącznika,
    digitalWrite(przekaznik1, przelacznik1);    // Wyślij na przekaźnik jego stan,
    delay(opoznienie);                          // Zaczekaj chwilę,
    while (digitalRead(pstryczek1) == HIGH) {}  // Czekaj na puszczenie pstryczka,
    delay(opoznienie);                          // Zaczekaj chwilę.
  }
  if (digitalRead(pstryczek2) == HIGH) {
    przelacznik2 = !przelacznik2;
    digitalWrite(przekaznik2, przelacznik2);
    delay(opoznienie);
    while (digitalRead(pstryczek2) == HIGH) {}
    delay(opoznienie);
  }
  if (digitalRead(pstryczek3) == HIGH) {
    przelacznik3 = !przelacznik3;
    digitalWrite(przekaznik3, przelacznik3);
    delay(opoznienie);
    while (digitalRead(pstryczek3) == HIGH) {}
    delay(opoznienie);
  }
  if (digitalRead(pstryczek4) == HIGH) {
    przelacznik4 = !przelacznik4;
    digitalWrite(przekaznik4, przelacznik4);
    delay(opoznienie);
    while (digitalRead(pstryczek4) == HIGH) {}
    delay(opoznienie);
  }
  wdt_reset();  // Zeruj watchdoga.
}

Przypomnę, że był to w zasadzie moduł czegoś większego, w którym stworzyliśmy prostą klawiaturę włączającą i wyłączającą cztery przekaźniki. Zrobimy sobie teraz pewną modyfikację, nazwijmy ją – szkoleniową, a dlaczego, to się okaże na końcu. Ponieważ na płytce edukacyjnej mam jeszcze piąty przycisk, do tego nie koliduje mi on z płytką przekaźników, wykorzystam go do psucia programu – dosłownie. Zatem dołączam go do listy definicji i określam typ portu.

const byte pstryczekWDT = 8;

Teraz najważniejsze: biblioteka obsługująca watchdoga. W zasadzie można się tam odwołać ręcznie, bo to tylko rejestr, ale arduinowa filozofia preferuje biblioteki.

#include <avr/wdt.h>

Sposób pracy z watchdogiem jest prosty i składa się z dwóch elementów: aktywowania go i zerowania w czasie krótszym niż czas jego zadziałania. Zwykle podczas aktywacji określamy ów czas: tutaj są to dwie sekundy. Dla tej biblioteki przewiduje się dziesięć różnych czasów do wyboru, od 15 mS do 8 sekund.

wdt_enable(WDTO_2S);

Istnieje jeszcze możliwość zawieszenia działania watchdoga, jeśli byłaby taka potrzebna, ale ja tego tutaj nie będę używać.

Włączyłem zatem mechanizm i teraz muszę koniecznie zadbać o wysyłanie sygnału zerującego, bo inaczej po dwóch sekundach czeka mnie reset. Dopisałem więc na końcu pętli tę instrukcję:

wdt_reset();

I teraz periodycznie watchdog jest zerowany, a program działa sobie jak należy. No dobrze, wierzymy na słowo, ale może by tak sprawdzić czy to w ogóle działa? Wbrew pozorom zawiesić Arduino jakimś sygnałem z zewnątrz wcale nie jest tak prosto, a czekanie na wielką burzą z piorunami może być nużące. Ale od czego są cwane sposoby?

Przyda mi się teraz piąty przycisk, który… nie będzie niczego robił, tylko trwał w pętli bez końca, dopóki nie zostanie puszczony.

while (digitalRead(pstryczekWDT) == HIGH) {}

Gdy teraz uruchomimy program, będzie działać normalnie dopóki nie przytrzymamy tego przycisku co najmniej przez dwie sekundy. Wtedy nagle wszystkie przekaźniki się rozłączą. Dlaczego? Zadziała watchdog! Zresetuje nam mikrokontroler, a po resecie wszystkie przekaźniki mają się wyłączyć. Czyli mechanizm działa.

Ale zaraz, zaraz, przecież to samo stanie się, gdy będziemy trzymać dłużej jakikolwiek inny przycisk. Trochę to nie działa dobrze, bo w ten sposób, zbyt leniwie operując panelem sterującym możemy wywoływać resety. Trzeba program zmodyfikować. A konkretnie należy w każdym miejscu, w którym następują niekreślone zatrzymywania się programu na skutek na przykład zbyt długiego trzymania wciśniętego przycisku, wprowadzić procedurę zerującą watchdoga. Zatem zamiast czekać na puszczenie pstryczka, teraz jeszcze czekając będziemy zerować nasz układ czuwający.

#include <avr/wdt.h>          // Biblioteka obsługująca watchdoga.
const byte opoznienie = 100;  // Opóźnienie po wciśnięciu pstryczków.

const byte pstryczek1 = 9;  // Port klawiatury.
const byte pstryczek2 = 10;
const byte pstryczek3 = 11;
const byte pstryczek4 = 12;

const byte pstryczekWDT = 8;  // Pstryczek, którym będziemy testować watchdoga.

const byte przekaznik1 = 4;  // Port przekaźników.
const byte przekaznik2 = 5;
const byte przekaznik3 = 6;
const byte przekaznik4 = 7;

bool przelacznik1 = false;  // Przełączniki.
bool przelacznik2 = false;
bool przelacznik3 = false;
bool przelacznik4 = false;

void setup() {
  wdt_enable(WDTO_2S);  // Włączamy watchdoga, będzie musiał być zerowany co dwie sekundy.

  pinMode(pstryczek1, INPUT_PULLUP);  // Deklaruj porty pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
  pinMode(pstryczek2, INPUT_PULLUP);
  pinMode(pstryczek3, INPUT_PULLUP);
  pinMode(pstryczek4, INPUT_PULLUP);
  pinMode(pstryczekWDT, INPUT_PULLUP);

  pinMode(przekaznik1, OUTPUT);  // Deklaruj porty przekaźników jako wyjścia.
  pinMode(przekaznik2, OUTPUT);
  pinMode(przekaznik3, OUTPUT);
  pinMode(przekaznik4, OUTPUT);
}

void loop() {
  while (digitalRead(pstryczekWDT) == HIGH) {}  // Testujemy watchdoga: po 2 sekundach trzymania pstryczka system powinien się zresetować.

  if (digitalRead(pstryczek1) == HIGH) {       // Jeśli wciśnięto pstryczek, to...
    przelacznik1 = !przelacznik1;              // Zaneguj stan przełącznika,
    digitalWrite(przekaznik1, przelacznik1);   // Wyślij na przekaźnik jego stan,
    delay(opoznienie);                         // Zaczekaj chwilę,
    while (digitalRead(pstryczek1) == HIGH) {  // Czekaj na puszczenie pstryczka.
      zerujWDT();                              // Oczekując na jego puszczenie, zeruj watchdoga.
    }
  }
  if (digitalRead(pstryczek2) == HIGH) {
    przelacznik2 = !przelacznik2;
    digitalWrite(przekaznik2, przelacznik2);
    delay(opoznienie);
    while (digitalRead(pstryczek2) == HIGH) {
      zerujWDT();
    }
  }
  if (digitalRead(pstryczek3) == HIGH) {
    przelacznik3 = !przelacznik3;
    digitalWrite(przekaznik3, przelacznik3);
    delay(opoznienie);
    while (digitalRead(pstryczek3) == HIGH) {
      zerujWDT();
    }
  }
  if (digitalRead(pstryczek4) == HIGH) {
    przelacznik4 = !przelacznik4;
    digitalWrite(przekaznik4, przelacznik4);
    delay(opoznienie);
    while (digitalRead(pstryczek4) == HIGH) {
      zerujWDT();
    }
  }
  wdt_reset();  // Po przelocie całej pętli zeruj watchdoga.
}

void zerujWDT() {
  wdt_reset();        // Zeruj watchdoga.
  delay(opoznienie);  // Zaczekaj chwilę.
}

Część procedury zerującej watchdoga przeniosłem do podprogramu, ponieważ jego treść jest wspólna dla wszystkich czterech przekaźników. Pozostawiłem jednak obsługę piątego pstryczka, by po kompilacji sprawdzić jak to wszystko działa. Okazuje się, że teraz już trzymanie przycisków związanych z przekaźnikami nie wywołuje resetu. Jeśli już pobawimy, można będzie usunąć obsługę piątego przycisku – ona służy tylko testom.

Tak na marginesie, wcześniejsze rozwiązanie wcale nie jest skazane na porażkę. Przecież tak możemy ukryć sposób na wymuszenie resetu – długim trzymaniem przycisków sterujących. Acz dwie sekundy to trochę mało. Możemy jednak ten czas zmienić na 4 bądź 8 sekund.

Biblioteka pracuje z mikrokontrolerami ośmiobitowymi. A co, gdybyśmy się chcieli przesiąść na nowe Arduino Uno R4? Ależ proszę, wystarczy wybrać inną bibliotekę i lekko zmienić składnię. W tym wypadku najdłuższy czas przekracza 5 sekund, ale za to najkrótszy schodzi do milisekundy. Każda rodzina kontrolerów pracuje z własnymi bibliotekami i należy je dobierać na bieżąco.

#include <WDT.h>  // Biblioteka obsługująca watchdoga.
const byte opoznienie = 100;  // Opóźnienie po wciśnięciu pstryczków.

const byte pstryczek1 = 9;  // Port klawiatury.
const byte pstryczek2 = 10;
const byte pstryczek3 = 11;
const byte pstryczek4 = 12;

const byte pstryczekWDT = 8;  // Pstryczek, którym będziemy testować watchdoga.

const byte przekaznik1 = 4;  // Port przekaźników.
const byte przekaznik2 = 5;
const byte przekaznik3 = 6;
const byte przekaznik4 = 7;

bool przelacznik1 = false;  // Przełączniki.
bool przelacznik2 = false;
bool przelacznik3 = false;
bool przelacznik4 = false;

void setup() {
  WDT.begin(2000);  // Włączamy watchdoga, będzie musiał być zerowany co dwie sekundy.

  pinMode(pstryczek1, INPUT_PULLUP);  // Deklaruj porty pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
  pinMode(pstryczek2, INPUT_PULLUP);
  pinMode(pstryczek3, INPUT_PULLUP);
  pinMode(pstryczek4, INPUT_PULLUP);
  pinMode(pstryczekWDT, INPUT_PULLUP);

  pinMode(przekaznik1, OUTPUT);  // Deklaruj porty przekaźników jako wyjścia.
  pinMode(przekaznik2, OUTPUT);
  pinMode(przekaznik3, OUTPUT);
  pinMode(przekaznik4, OUTPUT);
}

void loop() {
  while (digitalRead(pstryczekWDT) == HIGH) {}  // Testujemy watchdoga: po 2 sekundach trzymania pstryczka system powinien się zresetować.

  if (digitalRead(pstryczek1) == HIGH) {       // Jeśli wciśnięto pstryczek, to...
    przelacznik1 = !przelacznik1;              // Zaneguj stan przełącznika,
    digitalWrite(przekaznik1, przelacznik1);   // Wyślij na przekaźnik jego stan,
    delay(opoznienie);                         // Zaczekaj chwilę,
    while (digitalRead(pstryczek1) == HIGH) {  // Czekaj na puszczenie pstryczka.
      zerujWDT();                              // Oczekując na jego puszczenie, zeruj watchdoga.
    }
  }
  if (digitalRead(pstryczek2) == HIGH) {
    przelacznik2 = !przelacznik2;
    digitalWrite(przekaznik2, przelacznik2);
    delay(opoznienie);
    while (digitalRead(pstryczek2) == HIGH) {
      zerujWDT();
    }
  }
  if (digitalRead(pstryczek3) == HIGH) {
    przelacznik3 = !przelacznik3;
    digitalWrite(przekaznik3, przelacznik3);
    delay(opoznienie);
    while (digitalRead(pstryczek3) == HIGH) {
      zerujWDT();
    }
  }
  if (digitalRead(pstryczek4) == HIGH) {
    przelacznik4 = !przelacznik4;
    digitalWrite(przekaznik4, przelacznik4);
    delay(opoznienie);
    while (digitalRead(pstryczek4) == HIGH) {
      zerujWDT();
    }
  }
  WDT.refresh();  // Po przelocie całej pętli zeruj watchdoga.
}

void zerujWDT() {
  WDT.refresh();      // Zeruj watchdoga.
  delay(opoznienie);  // Zaczekaj chwilę.
}

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