Tartalom:
- Mi a program lefagyás, és hogyan fordul elő
- Mit lehet kezdeni, ha lefagy a program? Watchdog!
- Watchdog beépítése a programjainkba
- Arduino nano watchdog problémák és megoldásuk (bootloader csere)
—————————————————————————-
Azt hiszed, erre soha nem lesz szükséged? Csak idő kérdése!
A wotchdog egy remek eszköz a nem túlságosan profi programozóknak. Igaz évek teltek el, mire szükségem lett rá, de elérkezett a pillanat. Sikerült olyan nagy programot írnom, hogy szinte teljesen megtöltötte a rendelkezésre álló flash memóriát, és a rendelkezésre álló RAM területet is. A fordító már figyelmeztető üzenetet küldött, hogy stabilitási problémák adódhatnak. Így is lett. Készítettem egy programot, ami időjárási adatokat mért és gyűjtött. Négy ponton mért hőmérsékletet, légnyomást, páratartalmat, az eredményeket óránként SD kártyára írta, és 7 segmens-es kijelző jelenítette meg az aktuális adatokat, használtam led matrix kijelzőt, LCD kijelzőt és RTC órát is. Nem volt túl bonyolult, csak összetett. Minden modult külön külön is kipróbáltam, nem tapasztaltam velük semmilyen stabilitási problémát, a legelső egyszerűsített verzió (SD kártyára írás nélkül) két évig működött probléma nélkül. Az SD kártya könyvtára alapból több mint 10 Kbyte memóriát foglalt el, így itt lehet valahol a probléma gyökere és oka. Mióta SD kártyára rögzítem az adatokat, néhány naponta lefagy a kütyü. Néha egy nap, néha két hét is eltelik a lefagyások között. A led kijelzőn 2 másodpercenként váltogatom a mért értékeket (páratartalom, légnyomás, pince hőmérséklet stb.), így könnyű volt kiszúrni, amikor lefagyott a rendszer. A kijelzőre “ráfagyott” az utolsó mért adat, nem váltogatta a program az értékeket. Szerintem ekkor már csak a MAX7219 és a 74HC595 IC-k működtek, világítottak a led kijelzők az utoljára regisztereikbe léptetett adattal.
Nézegettem egy darabig a programot, de nem láttam benne hibát (persze van valahol, csak nem láttam). A fiam profi C# programozó, és arról győzködött, hogy “memória szivárgás” lehet, mert az a leggyakoribb probléma. Hogy mi ez, azt csak homályosan értettem meg, és a megoldásra nem láttam lehetőséget. Debuggolni kellet volna a programot, közben figyelni a memória felhasználást és állapotokat. A jelenséget az okozhatja, hogy a program futása közben memória változókat hoznak létre a különböző függvények, majd amikor végeztek, törlik azokat. Néha azonban valamiért nem sikerül a törlés és a foglalt terület felszabadítása, így hosszú idő után elfogy a rendelkezésre álló RAM terület. Ekkor fagy le a rendszer. Lehet, hogy így volt, lehet, hogy nem!
Régi mondás, hogy vannak problémák, amiket könnyebb megkerülni, mint megoldani. Jó kerülő megoldás az ATmega vezérlőkbe beépített watchdog. Ennek lényege, hogy beépítettek egy RC oszcillátort a chip-be. Ez mindentől függetlenül működik, nem kell hozzá külső alkatrész. Ha tápfeszt kap a chip, akkor megy. Ha bekapcsolod a watchdog-ot (házőrző kutya), akkor ez az oszcillátor meghajt egy számlálót, ami ha elér egy értéket, akkor képes resetelni (alaphelyzetbe állítani) a chip-et. Ha ezt a számlálót számlálás közben időnként 0-ra állítod, akkor újrakezdi a számlálást, és esetleg soha nem éri el azt az értéket, ami reseteli az ATmega chip-et. Hát ez szuper! A programban ciklikusan törölni kell a watchdog számlálót, és ha lefagy a program, akkor a törlés elmarad, és a chip egy idő után újraindul. Persze minden adat elveszik, de legalább újra működik. Ha vannak adatok, melyeket jó lenne nem elveszíteni, akkor azokról folyamatosan gondoskodni kell. Pl a programnak az ATmega vezérlőben beépített nem felejtő ram-ba (eeprom) kell beírni, külső SD kártyán kell tárolni, vagy lehet egy külső RAM-ot is használni. Az ATmega eeprom használatának nagy hátránya, hogy kb. 100.000 újraírást enged meg, ezért a programod teljes élettartama alatt nem írhatsz bele többször 100 ezernél többször, különben a chip nem lefagy, hanem tönkremegy. Az SD kártya használat elég macerás, mert fájlokat kell kezelni, de azért nem megoldhatatlan. A külső ram lehet a legjobb megoldás. Van olyan, amit I2C kommunikációval lehet használni, és csak arra kell vigyázni, hogy mindig legyen tápfesze. Pl. szünetmentesen kell táplálni egy elemről. Két ceruzaelemről évekig tudnak működni. Nekem nem kellett ilyen megoldás, mert nem voltak kritikus adataim. Egyszerűen elég volt az újraindítás.
Az említett lefagyós program egy Arduino nano-ban futott. Elővettem hát a legegyszerűbb watchdog mintaprogramot, és rátöltöttem. Ennek a példa programnak a lényege, hogy az Arduino nano beépített led-jét villogtatja végtelen ciklusban. A villogási (világítási) időt viszont minden ciklusban növeli. Egyszer csak hosszabb ideig áll várakozásban a program, mint amennyi idő alatt a watchdog számláló végigszámol, és a chip újraindul. Az újraindulást a program setup részében elhelyezett három gyors felvillanás jelzi. Előre jelzem, a program nem volt működőképes.
#include<avr/wdt.h> //WatchDog header betöltése int i; void setup() { wdt_enable(WDTO_4S); //engedélyezük a watcdog-ot 4 sekundum várakozással //4sec után a chip ujaindítja magát, ha nincs közben // wdt_reset() függvényhívás, ami ujraindítja a timert pinMode(13, OUTPUT); //beépített ledet fogjuk használni for (i=0;i<3;i++){ //három gyors villanás digitalWrite(13,HIGH); delay(100); digitalWrite(13,LOW); delay(100); } delay(100); } void loop() { for(int i = 0; i<10000; i++) { wdt_reset(); //alaphelyzetbe állítjuk a a watcdog timer-t digitalWrite(13, HIGH); delay(250*i); //minden ciklussal növekvő ideíg vílágit a led //4sec-nél hosszabb idő után nem kap resetet //a watchdog, és ujraindítja a chip-et digitalWrite(13, LOW); delay(250); } }
Szóval az Arduino nano-ban ez a program nem működött. Feltöltés után gyorsan felvillant a led háromszor, tehát a setup rész rendben lefutott. Aztán a led egyre hosszabb ideig világított rövid elalvások között, vagyis a végtelen ciklus is működött. Egyszer csak elérkezett az a pillanat, hogy négy másodpercnél hosszabb ideig világított a led, vagyis nem került sor a watchdog számláló resetelésére. Ekkor bekövetkezett az újraindulás. Viszont nem három gyors felvillanás jött, hanem a beépített led gyors villogásba kezdett végtelenül. Ebből az állapotból csak akkor került ki, ha kihúztam az USB portból, vagyis elvettem tőle a tápfeszültséget. Reset gombra sem reagált. Kipróbáltam mezei telefontöltő táppal is, hátha az a baj, hogy a PC az USB porton keresztül kommunikálni akar. Ez sem segített. Csak a teljes ki és bekapcsolás, de ez is csak egy watchdog “reset”-ig, mindig megjelent a led gyors villogása. Szerencsére programot lehetett rátölteni, az működött, nem kellett kidobni, de csak a ki be kapcsolást követő működési szakaszban, amíg a watchdog egyszer sem indította újra a chip-et!
Találtam a youtube-on oktató videót a watchdogról. Azon gyakorlatilag ugyanezt a programot mutatták be. Egyetlen különbség az volt, hogy a bemutatón Arduino UNO R3-at használtak. Az ugye kb. ugyanaz, de azért kipróbáltam. És igen, az UNO-n működött. Mi lehet a különbség? Hát a bootloader verziója. Az UNO-n már a kisebb helyfoglalású 512 byte-os bootloader (optiboot) található, míg az általam beszerzett Arduino nano példányok mindegyikén egy 2 kbyte-os bootloader. Ezt honnan tudom? Két gyanús dolgot is felfedeztem. Az egyik nyilvánvaló volt, csak nem tulajdonítottam neki jelentőséget. Az Arduino IDE használatakor, a nano-ra történő program feltöltéskor az alaplap típusa “Arduino nano”, a processzor ATmega328P (old bootloader):
A másik gyanús dologért tenni kelett valamit. Az aruino.cc weboldalon találtam egy “showinfo” nevű programocskát. Ez futás közben kiír jó néhány adatot a chip-ben található dolgokról. Többek között az úgynevezett “fuse” változókról is. Ezek tartalma most nem lényeges, csak annyit kell tudni, hogy a “HIGH FUSE” egyes bitjeivel be lehet állítani a bootloader által lefoglalt memória nagyságát. Íme a high fuse értéke:
Arduion UNO R3 showinfo:
HIGH FUSE = 0xDE
Arduino nano old bootloader showinfo:
HIGH FUSE = 0xDA
Igen, itt is látszik, hogy valami más. Persze azt nem tudtam, hogy mi okozhatja a jelenséget, semmilyen más különbséget nem találtam, tekintve, hogy az UNO-ban és a nano-ban is ATmega328 chip található. Így ezen a nyomon indultam el. Nosza töltsünk a nano-ra új bootloadert. Bamba módon csináltam. Feltételeztem, hogy az Arduino IDE által felajánlott bootloader feltöltési lehetőség valami új bootloadert fog a chip-re tölteni. Ehhez persze programozó kell, bootloadert nem lehet USB porton keresztül közvetlenül a nano-ra tölteni. Az UNO-ból csináltam programozót, és azzal töltöttem fel az új bootloadert. Ennek részleteiről itt olvashatsz.
Az akció sikeres volt. Az új bootloaderrel ellátott nano már képes a fenti programot az elvárások szerint futtatni. Az új bootloader rátöltése után újra kiolvastam a “showinfo”-val a high fuse értékét:
HIGH FUSE = 0xDA
Tehát az új bootloader nem az UNO optiboot verziója, vagy csak a bootloader feltöltsée nem állította be helyesen a high fuse megfelelő bitjeit, és az új bootloader 512 byte-on is elférne, de a fuse bitek állapota 2 kbyte-ot tart fenn számára. Lehet, hogy soha nem fog kiderülni, de legalább a watchdog működik!
Még egy említésre méltó dolog tartozik ide. Mióta a nano-ra feltöltöttem az új bootloadert, már az Arduino IDE alaplap beállításainál nem az “old bootloader”-t kell beállítani. Ezzel a paraméterrel már nem megy a program feltöltés. Sajna a hibaüzenet nem azonnal érkezik, hosszú timeout után a “programozó nem válaszol” hibaüzenetet küld.