Pułapki watchdoga AVR

Wiele czasu ostatnio spędziłem na walce z watchdogiem. Ten niepozorny i dość prosty mechanizm może przysporzyć sporo problemów, zwłaszcza gdy nie bierze się pod uwagę paru dość ważnych następstw jego działania. Jako, że źródła internetowe piszą o problematyce różnie i przeważnie tylko o jej fragmencie, postanowiłem zebrać w całość wszystkie znane mi pułapki i je szerzej omówić.

Watchdog po zadziałaniu się sam nie wyłączy

Watchdog jest różnie implementowany na AVRach. Te mniejsze, o ile internet nie kłamie, mają WD, który po swoim zadziałaniu wraca do domyślnego stanu, czyli wyłączenia, o ile fuse’y nie stanowią inaczej. Na tę informację dość często można natrafić. Z kolei bardziej rozbudowane scalaki, w tym ATmega328p mają tak zwanego Enhanced Watchdoga, który po swoim zadziałaniu dalej jest aktywny pomimo restartu programu. Na domiar złego po restarcie będzie on działał z najmniejszym prescalerem, dając restarty po 15ms! Z tego też powodu, jeśli przy starcie aplikacji ma on nie działać, to należy go w pierwszych liniach kodu wyłączyć, mimo że czysto teoretycznie nie został on wcześniej aktywowany.

Przykładowo, na mikrokontrolerze ATmega328p z domyślnie zaprogramowanymi fuse’ami, poniższy kod powinien objawiać się pierwszym dłuższym zaświeceniem diody, a następnie jej krótkimi mignięciami:

Wyłączenie Watchdoga musi nastąpić dość szybko

Datasheet mikrokontrolerów AVR definiuje procedurę zmiany konfiguracji WD jako:

  • Wyzerowanie bitu restartu WD w MCUSR,
  • Wstawienie w jednej operacji jedynki do pól WDE oraz WDCE,
  • W przeciągu kolejnych czterech cyklów procesora przestawienie bitów WDE lub WDP z wyzerowanym bitem WDCE.

Aby ułatwić programistom życie, istnieją funkcje wdt_enable() , wdt_disable()  oraz wdt_reset() . Problem w tym, że o ile funkcja wdt_reset()  jest aliasem jednej instrukcji asemblerowej, to pozostałe dwie są już bardziej złożone i lubią nie działać w zależności od użytych flag optymalizacyjnych, a także od wersji nagłówków AVR – niektóre bowiem są zbugowane. Problem ten można łatwo odwołując się do rejestrów na piechotę:

Dla pewności na czas wyłączenia WD powinny zostać wyłączone przerwania, o ile są używane. Z funkcją wdt_enable()  póki co problemu nie miałem, choć pewnie to tylko kwestia czasu. Można by zastąpić czymś ją analogicznym – różnica będzie tylko w drugiej operacji na WDTCSR. W przypadku użycia wdt_disable()  trzeba pamiętać o resecie MCUSR – z nieznanych mi przyczyn gotowiec tego nie robi.

This entry was posted in AVR and tagged , , , . Bookmark the permalink.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

This site uses Akismet to reduce spam. Learn how your comment data is processed.