Funkce "ulož po vypnutí"

Spousta aplikací si potřebuje i po vypnutí něco zapamatovat. Jistě víte, že k tomuto účelu na Atmelu dobře slouží EEPROM. Mnoho aplikací si data potřebuje uložit ale až v okamžiku kdy je vypnete. Z mé praxe vyberou namátkou několik příkladů. Třeba regulátor hlasitosti, který má hned po startu nastavit hlasitost na poslední použitou úroveň. Nebo třeba stolní světlo s regulací jasu, po nastartování má jas naskočit na stejnou úroveň jakou měl při vypínání. A vy jistě najdete nepřeberné množství dalších aplikací. Není tedy pochyb o tom, že funkce "ulož při vypnutí" najde své uživatele.

Realizace takové funkce v jednočipu vyžaduje splnit dvě podmínky:

Jak poznat vypínání

Jednou z možností je sledovat pokles napájecího napětí. U aplikací s menším odběrem (v řádu jednotek až desítek mA) není potřeba nijak zasahovat do hardwaru. Stačí pomocí AD převodníku periodicky měřit stav napájecího napětí (třeba takto). Jakmile dojde ke znatelnému poklesu můžete usuzovat, že je zařízení vypnuté a bez napájení. Druhou a asi preferovanější možností je využít komparátor (více o něm zde). Sledované napětí (typicky napájecí) podělíte odporovým děličem a přivedete na záporný vstup komparátoru. Na kladný vstup komparátoru je možné softwarově připojit vnitřní 1.1V referenci. Komparátor pak porovnává zlomek napájecího napětí s referencí a může vás upozornit, že došlo k poklesu. Toto řešení nijak nezatěžuje váš software, protože komparátor umí sám detekovat vzestupnou nebo sestupnou hranu a vyvolat přerušení. Krom toho samozřejmě můžete využít celé řady dalších možností (externího komparátoru, různých vnějších "supervisorů" a pod.). My zůstaneme u komparátoru.

Jak získat čas

Těsně po vypnutí vypínače je vaše aplikace odpojená od zdroje energie. Dostatečně velký kondenzátor ji může po potřebnou dobu zásobovat energií. Ale co je to dostatečně velký kondenzátor ? A co je to potřebná doba ?

Odpověď na druhou otázku je jednodušší. Zápis jednoho bytu do EEPROM trvá přibližně 3.4ms. Jestliže potřebujete uložit například 32bit číslo tak vám to bude trvat v nejhorším případě přibližně 14ms. Nějákou dobu bude aplikaci trvat než přijde na to, že je bez šťávy a než samotné ukládání zahájí. Po celou dobu až do ukončení zápisu do EEPROM musí být napájecí napětí čipu nad minimálním provozním napětím (v případě Attiny nad 1.8V).

Jak dlouho udrží kodenzátor zařízení "při životě" záleží na jeho kapacitě a na spotřebě zařízení. Vybíjení kondenzátoru probíhá podle jednoduché diferenciální rovnice du/dt = I/C kde I je proud vytékající z kondenzátoru, du/dt je tempo jakým klesá jeho napětí a C je jeho kapacita. Typicky neuděláme chybu když budeme odběr zařízení považovat za konstantní (většinou bude odběr s vybíjením kondenzátoru ještě klesat). Jestliže bude odběr například 10mA a kapacita 100uF bude napětí klesat tempem 100V/s. Z 5V poklesne na 2V přibližně za 30ms.

U zařízení s větší spotřebou, které by vyžadovaly velký zálohovací kondenzátor, je možné "oddělit" napájení mikrokontroléru od ostatních obvodů. Zálohuje se pak pouze provoz miktrokontroléru, který má odběry typicky v řádu jednotek až desítek mA. Několik variant pro tuto situaci si můžete prohlédnout níže.


a) Tato konfigurace zajišťuje stabilní napětí pro Atmel. Hodit se může v aplikacích kde používáte třeba AD převodník.


b) V tomto zapojení je stabilizátor použit pro výkonovou část obvodu. Atmel a zálohovaná doména je napájen přes diodu D1. Napájecí napětí Atmelu není stabilní, kolísá s odběrem a má hodnotu o 0.4V až 0.7V nižší než napětí stabilizátoru. V řadě aplikací to ale není žádná komplikace.


c) Nejjednodušší zapojení bez stabilizátoru najde uplatnění třeba v aplikacích napájených baterií.

Příklad

Je čas vrhnout se na praktickou ukázku. K testům jsem si vybral Attiny84 a zapojení podle schématu a) (kompletní schema je níže). Možná vás mate přítomnost C2, takže jeho roli raději vysvětlím. Bez něj by po vypnutí spínače pokleslo napětí nezálohované části obvodu defakto skokově a často se bude vaše zařízení přesně tak chovat. Ale vyjmečně v situacích kdy bude odběr nezálohované části malý, může její napětí klesat pozvolna. To zavádí drobné komplikace v podobě zákmitů komparátoru a proto jsem volik v ukázce raději "složitější" situaci. Dělič R1-R2, sloužící ke snímání podpětí zde hraje zároveň i roli spotřebiče a vybíjí C2. Proto jsou jeho hodnoty pouze 470R. V praxi můžete volit odpor o několik řádů vyšší. Stejně tak můžete změnit i dělící poměr a určit si tak jaké podpětí už lze považovat za "vypnutí".


d) Zapojení ukázky

Jak správně tušíte kondenzátor C1 zálohuje provoz mikrokontroléru jehož odběr je přibližně 7mA. Teoreticky by mělo jeho vybíjení probíhat tempem přibližně 70V/s. Z 5V na 1.8V (minimální napětí pro Atmel) by mělo poklesnou za 45ms. Aby Atmel nepracoval při nižším než minimálním napětí je vhodné zapnout Brow-Out detekci (BOD). V naší ukázce nastavím BOD na 1.8V (pomocí fuses). Abychom měli možnost v rámci ukázky vidět kdy ukládání do EEPROM začíná a jak dlouho trvá, využijeme PB2 jako pomocného výstupu (AUX) a budeme na něm průběh činnosti signalizovat.

V programu budeme udržovat celkový provozní čas zařízení. Něco jako "motohodiny" nebo v našem případě spíš "motosekundy". Po zapnutí načteme "motohodiny" a každou vteřinu pak obsah proměnné inkrementujeme. Po detekci vypnutí "motohodiny" uložíme do EEPROM. Díky tomu bychom měli mít kompletní přehled o době činnosti zařízení. Proměnnou necháme typu uint32 což nám umožní skladovat motovteřiny s rozsahem až do 50 tisíc let :D

// Zápis do EEPROM při vypínání aplikace - schema A
#include <avr/io.h>
#define F_CPU 8000000
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>

// pomocný výstup abychom viděli jak dlouho zápis trvá
#define AUX_H PORTB |= (1<<PORTB2) 
#define AUX_L PORTB &=~(1<<PORTB2)

uint32_t EEMEM ee_motosekund=0; // proměná v EEPROM
uint32_t motosekund; // proměnná v RAM

int main(void){
DDRB |= (1<<DDB2); // inicializace AUX výstupu (PB2)
motosekund = eeprom_read_dword(&ee_motosekund); // načíst obsah EEPROM
DIDR0 = (1<<ADC2D); // vypnout digitání vstup na PA2 (AIN1)
// komparátor - pozitivní vstup 1.1V referenci, smazat vlajku, povolit přerušení na sestupnou hranu
ACSR = (1<<ACBG) | (1<<ACI) | (1<<ACIE) | (1<<ACIS1) | (1<<ACIS0);
_delay_us(70); // čas pro ustavení vnitřní reference
sei(); // globálně povolit přerušení

    while (1){
    _delay_ms(1000); // hloupý způsob jak počítat vteřiny
    motosekund++; // počítat "motovteřiny"
    }
}

// přerušení od komparátoru
ISR(ANA_COMP_vect){
// zde není od věci zredukovat spotřebu zálohované domény
AUX_H; // teď začíná zápis do EEPROM
eeprom_update_dword(&ee_motosekund,motosekund); // zapiš do EEPROM
while(EECR & (1<<EEPE)){} // počkej na dokončení zápisu - ať vidíme jak dlouho trvá
AUX_L; // zápis dokončen
// tady lze přidat další potřebné kroky při vypínání aplikace
_delay_ms(2); // na chvíli zablokovat - zákmity na výstupu komparátoru
ACSR |= (1<<ACI); // zákmity mohly znovu nastavit vlajku, smaž ji pro jistotu
}

Konfigurace komparátoru je vcelku přímočará. Vybereme jako zdroj signálu pro kladný vstup vnitřní referenci, povolíme přerušení, smažeme vlajku a zvolíme k detekci sestupnou hranu. Jakmile napětí na sledovaném vstupu (AIN1) klesne pod referenční (1.1V) zavolá se rutina přerušení. Její obsah si rozebereme podrobněji.

Pokud Atmel budí například LEDky nebo obecně napájíte ze zálohované domény nějaké spotřebiče, jejichž provoz při ukončování není důležitý, vyplatí se je povypínat. Snížíte tím odběr, zpomalíte vybíjení zálohovacího kondenzátoru a získáte více času pro zápis do EEPROM. Samotný zápis je vhodné provádět pomocí funkcí eeprom_update_xxx(). Šetří se tím životnost EEPROM a krom toho je zápis většinou i rychlejší neboť se nepřepisují ty buňky, které obsahují stejnou hodnotu jakou do paměti chcete zapsat. Na konci rutiny je čekací smyčka. To je za normálních okolností krajně "neslušné", ale v této vyjmečné situaci (aplikace se vypíná a už nic smysluplného dělat nebude) je to přijatelné. Čekání je tam jako ošetření zákmitů komparátoru. Pokud totiž snímaný signál prochází hladinou 1.1V "pomalu" může vlivem šumu komparátor detekovat průchod hned několikrát (více o tom v jiném tutoriálu). Ze stejného důvodu je na konci i příkaz ke smazání vlajky. Nakonec bych jen poznamenal, že čekací smyčku můžete v reálné aplikaci vynechat, má význam jen pro náš příklad. A nyní se pojďme podívat jak se aplikace chová v provozu.


e) průběh vypínání. Žlutá stopa indikuje zápis do EEPROM, červená stopa vstup komparátoru, modrá stopa napětí zálohovacího kondenzátoru (C1)

Průběh je vcelku zajímavý. V prvé řadě se můžete přesvědčit, že zápis do EEPROM se zahájí opravdu v okamžiku kdy napětí na AIN1 (PA2) klesne pod 1.1V. Zápis v této konkrétní situaci trval přibližně 3.6ms. A to díky tomu, že tři ze čtyř bytů naší proměnné motosekund nebylo nutné přepisovat (zápis 1B trvá ~3.4ms). Zajímavý je ještě průběh napájecího napětí Atmelu (modrá). Těsně po vypnutí klesá tempem 57V/s. Pomaleji než jsme odhadovali (70V/s). To může být způsobeno větší kapacitou kondenzátoru (tolerance +-20%). Všimněte si že tempo se zpomaluje, neboť odběr Atmelu se snižujícím se napájecím napětím klesá. Během zápisu do EEPROM je vidět citelně strmější tempo (91V/s). Přepis EEPROM totiž zvyšuje odběr. Aplikace pracuje ještě přibližně 60ms po zápisu, což se projevuje vybíjením s tempem přibližně 32V/s. Jakmile napětí klesne na 1.8V Atmel se vlivem BOD vypne. Tím klesne jeho odběr a tempo vybíjení se zpomalí na ~4V/s a tím celý proces končí.



Home
Vx.xx 23.7.2017
By Michal Dudka (m.dudka@seznam.cz)