Použitie EEPROM a prerušenie od watchdogu na Arduine

V predchádzajúcej časti sme ukázali princíp mechanizmu nazývaného watchdog, ktorý umožní, aby sa riadiaci mikrokontrolér sám zotavil po prípadnej chybe, napríklad zacyklení, delení nulou a podobných problémoch.

Watchdog funguje na princípe čítača, ktorý sa automaticky a nezávisle od zvyšku CPU inkrementuje a v okamihu keď počítadlo dosiahne nastavenú hodnotu, mikrokontrolér resetuje. Aby sa tak nestalo, je potrebné periodicky nulovať hodnotu tohto počítadla. Preto je potrebné do vnútra cyklov, prípadne iných sekvencií kódu, ktoré trvajú dlhšie vkladať príkaz, ktorý toto počítadlo pravidelne nuluje, takže nastavená hodnota sa nikdy nedosiahne. Ak v dôsledku chyby hardvéru alebo programu nedokáže programový kód počítadlo resetovať, počítadlo dosiahne nastavenú hodnotu, pri ktorej je potrebné vykonať akciu na nápravu situácie. Najčastejšie sa mikrokontrolér v takom prípade resetuje, alebo sa vygeneruje prerušenie.

Po resete, ktorý spôsobí watchog začne program mikrokontroléra bežať znovu. A v prípade ak chybu, pre ktorú sa program zasekne, alebo zacyklí nenájdete, sa watchdog znovu aktivuje. Preto je dôležité stav, kedy sa mikrokontrolér resetoval z dôvodu watchdogu nejakým spôsobom indikovať, napríklad rozsvietením LED diódy signalizujúcej poruchový stav. To je zadanie pre príklad, avšak na jeho splnenie bude treba vyriešiť jeden problém. V predchádzajúcej časti sme po prekročení nastaveného času mikrokontrolér resetovali. Ako tento stav odlíšiť od resetu po zavedení aplikačného kódu, alebo pripojení napájania? Kam umiestniť kód, ktorý by do nejakej premennej uložil informáciu, že aspoň raz zasiahol watchdog a kam umiestniť túto premennú, tak, aby sa po resete od watchdogu jej stav nevynuloval? Po resete sa totiž všetky premenné nastavia buď na hodnotu uvedenú v deklarácii premennej, alebo na nulu ak pri deklarácii hodnota premennej nie je uvedená.

Trvalá pamäť EEPROM

Mikrokontroler Arduina má aj pamäť typu EEPROM, čiže elektricky preprogramovateľnú pamäť, ktorá je primárne určená na ukladanie konfiguračných hodnôt. Práve túto pamäť využijeme na uloženie informácie o tom, že nastal problém a musel zasiahnuť watchdog. Mikrokontrolér ATmega168 má tri druhy pamäti. Na uloženie programového kódu je určených 16 kB pamäti typu flash. Operačná pamäť RAM má kapacitu 1 KB a k dispozícii je aj 512 bajtov pamäti EEPROM. Mikrokontrolér ATmega328P má 1 kilobajt EEPROM a ATmega2560 na prototypovej doske Arduino Mega má až 4 kilobajty EEPROM.

Pri využívaní pamäti EEPROM je potrebné brať do úvahy, že na rozdiel od RAM, životnosť tejto pamäti nie je neobmedzená, výrobca mikrokontroléra garantuje 100 000 cyklov prepisu, takže ak by ste do pamäti zapisovali 10 x za sekundu, po 10 000 sekundách, čiže po necelých troch hodinách túto životnosť prekročíte. Počet čítaní je neobmedzený. Preto sa do tohoto typu pamäti ukladajú predovšetkým konfiguračné informácie, protokol o chybách a podobne.  Zápis do EEPROM má aj určitú časovú réžiu, približne 3,3 ms.

Pre zaujímavosť, aj pamäť Flash pre program má garantovaných 100 000 cyklov zápisu. Ak by ste do Arduina uploadovali program 10 x denne, limit by ste prekročili za 27 rokov.
V knižnici EEPROM.h sú na zápis, čítanie a prepisovanie údajov v EEPROM k dispozícii funkcie:

  • EEPROM.write(adresa, hodnota);
  • EEPROM.read(adresa, hodnota);
  • EEPROM.update(adresa, hodnota);

Knižnica obsahuje aj ďalšie funkcie, pomocou ktorých je možné pracovať s konfiguračnými údajmi v EEPROM, to bude námetom niektorej ďalšej časti. Teraz sa zamerajme na uschovanie informácie, či nastala, alebo nenastala určitá udalosť, napríklad že zaúradoval watchdog. V prvom, najjednoduchšom príklade ukážeme zapamätanie stavu dvojice tlačidiel. Sú pripojené na Pin 2 a Pin 3 Stlačením prvého tlačidla sa LED rozsvieti, druhým tlačidlom sa LED vypne a tento stav sa uchová aj počas prerušenia napájania. Po zapnutí napájania bude LED v takom stave ako pred jeho vypnutím. V príklade pre jednoduchosť nebudeme ošetrovať zákmity mechanických kontaktov tlačidiel. Zjednodušíme aj zapojenie, k tlačidlám nebudeme pripájať odpory, ale využijeme špeciálny vstupný mód digitálnych pinov  INPUT_PULLUP.

Vtedy sa aktivujú na príslušných pinoch v mikrokontroléri vstavané vnútorné rezistory 20 kiloOhm medzi pinom a kladným pólom napájacieho napätie. Takže ak je tlačidlo rozpojené je na vstupe úroveň HIGH, pretože je pripojené k + 5V cez vnútorný rezistor a spojenie na zem je rozpojené. Po stlačení tlačidla sa na pin dostane úroveň LOW.

#include <EEPROM.h>

int cervenaLED = 11;
int stavLED;

void setup()
{
 pinMode(cervenaLED, OUTPUT);
 pinMode(2,INPUT_PULLUP); // Piny 2 a 3
 pinMode(3,INPUT_PULLUP); // ako vstupné s vnútorným rezistorom
  stavLED = EEPROM.read(0);
 digitalWrite(cervenaLED, stavLED);
}

void loop()
{
 if(digitalRead(2) == LOW)
 {
   stavLED = LOW;
   EEPROM.update(0, stavLED);
 }

 if(digitalRead(3) == LOW)
 {
   stavLED = HIGH;
   EEPROM.update(0, stavLED);
 }
 digitalWrite(cervenaLED, stavLED);
 delay (200);
}

Watchdog generuje prerušenie

Ak modifikujeme predchádzajúci príklad, doplníme mechanizmus watchdogu a nakonfigurujeme ho tak, aby po pretečení počítadla došlo k prerušeniu a v jeho obsluhe sa zapíše vyskytnutie sa poruchového stavu, splníme zadanie. Informácia o tom, že bol watchdog aktivovaný sa zapíše do EEPROM, takže sa zachová aj po resete.

Aby pretečenie počítadla WDT vyvolalo prerušenie, je potrebné watchdog trochu prácnejšie nakonfigurovať na systémovej úrovni. Bude treba nastaviť hodnotu bitov osembitového registra WDTCSR. V predvolenom nastavení sú všetky bity nastavené na 0. Význam bitov registra je:

WDIF (7)  WDIE (6) WDP3 (5) WDCE (4) WDE (3) WDP2 (2) WDP1 (1) WDP0 (0)

WDIF – Nastavuje príznak prerušenia. Tento bit nemusíte riešiť systém ho nastavuje automaticky

WDIE – Povoľuje prerušenia. To umožňuje vykonať niekoľko málo riadkov kódu pred resetovaním dosky.

WDCE – Povoľuje konfiguračný režim, ktorý bude trvať 4 taktovacie cykly. Tento bit je potrebné nastaviť predtým, ako sa pokúsite vykonať akékoľvek zmeny vo watchdog registri.

WDE – Umožňuje reset systému pri prekročení časového limitu.

WDP0/WDP1/WDP2/WDP3 – štvorbitový register nastavujúci limit pretečenia počítadla.

Význam jednotlivých bitov je:

Kompletný kód príkladu. Počítadlo watchdogu sa resetuje prvým tlačidlom pripojeným na PIN 2 a červená LED, ktorá indikuje, že došlo k pretečeniu počítadla a následnému resetu môže používateľ zhasnúť druhým tlačidlom pripojeným na PIN 3.

#include <avr/wdt.h>
#include <EEPROM.h>

int cervenaLED = 11;
int zelenaLED  = 12;
int oranzovaLED = 13;
int stavWdLED;

int loop_count = 0;

void setup()
{
 pinMode(zelenaLED, OUTPUT);
 pinMode(cervenaLED, OUTPUT);
 pinMode(oranzovaLED, OUTPUT);
 pinMode(2,INPUT_PULLUP); // Piny 2 a 3
 pinMode(3,INPUT_PULLUP); // ako vstupné s vnútorným rezistorom

 digitalWrite(zelenaLED,HIGH);
 delay (200);
 digitalWrite(zelenaLED,LOW);
 // nastavenie watchdogu
 cli();  // zakaz prerusenia
 wdt_reset();  // reset the WDT
 WDTCSR |= (1<<<="" tt="">
 WDTCSR = (1<<<<<<<="" tt="">
 sei();

 stavWdLED = EEPROM.read(0);
 digitalWrite(cervenaLED,stavWdLED);
}

/*
WDTCSR konfigurácia:
WDIE = 1 : Povolené prerušenie
WDE = 1 :Povolený reset
WDP3 = 1 WDP2 = 0 WDP1 = 0  WDP0 = 0: 4000 ms = 4s
*/

ISR(WDT_vect) // obsluha prerušenia
{
  EEPROM.update(0, HIGH);
  digitalWrite(cervenaLED,HIGH);
}

void loop()
{

 if(digitalRead(2) == LOW)
 { //čítanie tlačidla resetujúceho WDT
  wdt_reset();  // reset WDT
 }

 if(digitalRead(3) == LOW)
 {  //čítanie tlačidla nulujúceho
  EEPROM.update(0, LOW);  // indikáciu WDT
  digitalWrite(cervenaLED,LOW); // zhasni cervenu LED
 }

 digitalWrite(oranzovaLED, HIGH); // blikaj oranzovou LED
 delay(100); //pauza 500 ms
 digitalWrite(oranzovaLED,LOW);
 delay(100);
}

Převzato z webu Nextech se souhlasem autora.