Reklama

ATtiny24 analogový komparátor – 2. Díl Praktické ukázky I

Úvod

Po minulém díle s teoretickým úvodem analogového komparátoru mikrokontroléru ATtiny24 si v tomto článku ukážeme první praktické ukázky použití. Nastíníme také některé problémy použití analogového komparátoru a jejich řešení.

Jednoduchý přístup

Začneme prvním jednoduchým programem. Na pozitivní vstup komparátoru připojíme interní referenci 1.1 V a negativní vstup připojíme na AIN1 (PA2). Vzhledem k tomu, že výstup komparátoru je pouze interní, budeme si jeho stav muset nějak signalizovat. PB0 si nakonfigurujeme jako výstup a procesor necháme sledovat bit ACO tedy výstup komparátoru. Jeho hodnotu “zkopírujeme” na výstup PB0. Pokud nemáte k dispozici osciloskop a chcete si příklad vyzkoušet, připojte si na PB0 LED diodu. Na AIN1 si přiveďte nějaké analogové napětí, třeba trimrem. Ideálně si na AIN1 připojte ještě voltmetr, ať můžete sledovat hodnotu napětí. Při překročení 1.1 V by se vám LED dioda měla zhasnout. My zkoušku provedeme za pomocí generátoru, který necháme vytvářet pilovitý průběh s frekvencí 1 kHz. Ta by měla stačit, aby program stíhal nastavovat výstup. Výsledek vidíte na obr. 1. Žlutý průběh je negativní vstup komparátoru (AIN1), modrý signál je pak PB0. Je patrné, že jakmile vstupní signál překročí hodnotu 1.1 V, přepne se bit ACO, což vyústí v nastavení PB0 (modrý průběh) do log. 0. Analogicky jakmile napětí klesne pod 1.1 V, program nastaví PB0 na log. 1.

 

001: // jednoduché použití komparátoru, pozitivní vstup 1.1V
002: // reference, negativní vstup AIN1
003: #include <avr/io.h>
004:
005: int main(void){
006: ACSR = (1<<ACBG);// na AIN0 zapojíme interní referenci,
007: ADCSRB &= ~(1<<ACME);// nechci zapínat multiplexer
008: // vypneme vstupní buffery
009: DIDR0 |= (1<<ADC2D) | (1<<ADC1D);
010: DDRB |= (1<<DDB0);// výstup abychom se měli na co dívat
011:
012: while(1){
013: if(ACSR & (1<<ACO)){PORTB=0b1;}// sledujeme výstup komparátoru
014: else{PORTB=0;}// a nastavujeme PB0 podle jeho hodnoty
015: }
016: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

Obr. 1: Žlutý průběh je srovnáván s vnitřní 1.1V referencí, modrý průběh je výstup komparátoru

Obr. 1: Žlutý průběh je srovnáván s vnitřní 1.1V referencí, modrý průběh je výstup komparátoru
Rychlost komparátoru

Teď si zkusíme rychlost reakce našeho programu, abyste měli představu, v jaké časové škále se bez optimalizace kódu pohybujete. Na vstup pustíme obdélníkový signál a budeme sledovat, za jak dlouho komparátor zareaguje. Výsledek je (jak mnozí z vás čekali) drobně znepokojivý (obr. 2.). Zpoždění je mezi 4 až 11 us (čip pracuje na 1 MHz). Kde se bere takový rozptyl hodnot (jinak též nazývaný “jitter”), když by podle datasheetu měl rozptyl dosahovat typicky 1 us? No jasně :) Je to programem. Celý cyklus kontroly hodnoty ACO trvá 4-10 strojových cyklů. Tedy pokud pulz přijde v tom nejvhodnějším okamžiku, tedy těsně před načtením hodnoty z ACO, zareaguje program rychle. Jestliže ale pulz přijde těsně po čtení ACO, musí program projít celou while smyčku než se dostane ke kontrole znovu. Ze zdrojového kódu to není vidět, ale když se podíváte na přeložený kód v assembleru, můžete spočítat instrukce a rozptyl zpoždění předpovídat. Nebo můžete assemblerovský kód editovat a pokusit se smyčku optimalizovat, což by se vám asi podařilo, ale tak do hloubky se problémem zabývat nebudeme. (Klidně to někdo zkuste a napište nám výsledky do komentářů). Přirozeně v drtivé většině aplikací vás tento “jitter” nebude vůbec zajímat. Navíc pokud byste potřebovali reagovat rychle, taktovali byste ATmel na vyšší frekvenci než 1 MHz. Pokud by vám to napájecí napětí dovolilo, mohli byste jít až na 20 MHz a celou reakci tak o řád zkrátit.

Obr. 2: Zpoždění komparátoru s jednoduchým programem

Obr. 2: Zpoždění komparátoru s jednoduchým programem
Problémy

Co vás ale určitě bude zajímat, je výsledek dalšího pokusu. Nechme program stejný a zkusme opět změnit vstupní signál (žlutý). Na vstup pustíme sinusový průběh o nízké frekvenci, dejme tomu 10 Hz. Ten přirozeně nebude ideální a bude obsahovat drobný šum. Jeho původem se nebudeme zabývat, ale u většiny vašich aplikací tam bude. Na obr. 3. vidíte v horní části hrubý pohled na průběh a v dolní části je pak “zoom” a z něj je patrné, že máme problém. Když vstupní signál (žlutý) klesne pod hranici 1.1 V, komparátor překlopí, ale pár mikrosekund poté se vrátí a tak několikrát “zakmitá” než se ustálí. Tento problém vás čeká u každého neošetřeného komparátoru, kterému přivede na vstupy stejné hodnoty. Stačí, aby jedna z hodnot byla větší jen o pár milivoltů než druhá a komparátor překlopí. Šum v řádu milivolt způsobí, že na krátký okamžik je vstupní hodnota větší a pak zase klesne zpět pod referenční hodnotu. A toto se děje nahodile. U analogových komparátorů se tohoto nežádoucího jevu můžete zbavit nastavením hystereze. Tou zařídíte, že hladina pro překlopení komparátoru do log. 1 je vyšší než hladina pro překlopení do log. 0. Jakmile měřený signál protne úroveň 1.1 V (i třeba za pomoci šumu), musí sestoupit pod 1.1 V mínus hystereze takže například pod hodnotu 1.08 V. Jemný šum, který dočasně zvedl úroveň vstupního signálu o pár milivolt, už nestačí na to, aby úroveň snížil o 20 mV. Zákmity se pak neobjeví. Přesněji řečeno drasticky se sníží šance, že se objeví. Náš komparátor ale vnitřně nic takového nemá! (na rozdíl od některých jiných mikrokontrolerů). Budeme si s tím tedy muset nějak poradit. Pokud by vám problematika stále nebyla jasná, zkuste googlit v obrázcích, ten to totiž vysvětlí ze všeho nejlíp!

Obr. 3: Problém ! Absence hystereze.

Obr. 3: Problém ! Absence hystereze.
Řešíme problémy

Předvedeme si dvě možnosti jak se zákmitů zbavit. Prvním z nich je zavést si do komparátoru hysterezi. Existuje několik variant obvodových řešení, my si předvedeme jednu z těch nejjednodušších. Referenci pro komparátor si vytvoříme děličem. Využijeme jeden z pinů procesoru, jehož přepínáním budeme drobně ovlivňovat referenční napětí. Kdykoli komparátor překlopí do log. 1, přepneme výstup tak, abychom referenční hladinu trošku snížili a dostali ji tak “z dosahu šumu”. Naopak když komparátor překlopí do log. 0, tak referenční hladinu drobně zvýšíme. Spotřebujeme sice jeden pin Atmelu, ale získáme řešení, které funguje “vždy”. Jak moc budeme ovlivňovat referenční hladinu, záleží na nás. Metoda není nijak závratně přesná, protože hodnoty hystereze kolísají s napájecím napětím, ale bude pro většinu aplikací postačovat. Přirozeně ji nemůžete použít v situacích, kdy vyžadujete přesnou a stabilní referenci. Zapojíme obvod podle Obr. 4. Referenční hladina by měla být 870 mV, pokud je PB3 v log. 0 a 910 mV, pokud je v log. 1. Hystereze je tedy 40 mV.
Hodnoty referenčního napětí můžete spočítat dle vztahu:
 
Uref = (Up * R1 * R2 + Ucc * R2 * R3)/(R1 * R2 + R2 * R3 + R1 * R3)
 
kde za Ucc dáte napájecí napětí a za Up dosadíte napětí na výstupu z čipu (v našem případě buď 5 V nebo 0 V). Vztah uvádím raději obecně, protože ho můžete použít i s jiným napájecím napětím. Na vstup obvodu pustíme sinus s 10 % šumu. Zdrojový kód příkladu je pod obrázky. Zapsáním samých nul do ACSR připojujeme pozitivní vstup komparátoru na AIN0 a negativní na AIN1. Záměrně na obr. 5. ukazuji výstup komparátoru, když je zpětná vazba (odpor R3) odpojena a hystereze tedy není zavedena. Na obr. 6. pak vidíte výsledek po zavedení hystereze (obvod přesně dle obr. 5.).

Obr. 4: Schéma zapojení

Obr. 4: Schéma zapojení

Obr. 5: Výsledek bez zpětné vazby (bez hystereze)

Obr. 5: Výsledek bez zpětné vazby (bez hystereze)

Obr. 6: Výsledek s hysterezí

Obr. 6: Výsledek s hysterezí
001: // Komparátor s napěťovou hysterezí přivedenou z PA3
002: #include <avr/io.h>
003:
004: int main(void){
005: DDRA |= (1<<DDA3);// výstup, bude upravovat referenci
006: ACSR = 0;// žádná přerušení, vstupy pro komparátor AIN0 a AIN1
007: // vypneme vstupní buffery
008: DIDR0 |= (1<<ADC2D) | (1<<ADC1D);
009:
010: while(1){
011: // drobně snížíme referenci
012: if(ACSR & (1<<ACO)){PORTA &=~(1<<PORTA3);}
013: else{PORTA |= (1<<PORTA3);}// drobně zvýšíme referenci
014: }
015: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

V některých situacích si můžete pomoct jinak. Předchozí metoda vyžadovala mít vnější referenci a spotřebovala vám jeden pin procesoru. Pokud ale máte “střídavý” nebo obecně nestejnosměrný signál, můžete hysterezi obejít. Jestliže je měřený signál natolik vychovaný, že se běžně příliš dlouho nezdržuje na hodnotě blízko referenční, můžete se zákmitů zbavit dočasným “zablokováním” komparátoru. Metodu lze použít pro detekci střídavých signálů, pulzů s pomalou (i rychlou) dobou náběhu a různých kolísavých a nestálých průběhů. Princip je jednoduchý. Jakmile komparátor překlopí, tak na to program zareaguje a na nějakou dobu výstup komparátoru ignoruje. Po určitém čase (kdy by se měl vstupní signál vzdálit dostatečně daleko od reference) začne program zase výstup komparátoru sledovat. Zjednodušeně řečeno, počká na první zákmit, zareaguje na něj a pak na nějaký čas zavře oči a bude dělat, že ostatní zákmity prostě nevidí.

Jednoduchá softwarová korekce je v následujícím zdrojovém kódu. Určitě bychom si stále vystačili se skenováním bitu ACO jako v předešlém příkladě, ale abychom se někam posunuli, zkusíme využít vlajky ACI. Vynulujeme bity ACIS0 a ACIS1 a zároveň zakážeme přerušení vynulováním ACIE. Žádné přerušení se tedy volat nebude, ale vlajka ACI bude signalizovat, zda nastala událost, jež by měla spustit přerušení, pokud by bylo povoleno. V našem případě je to jakákoli změna výstupu komparátoru. Ve zdrojovém kódu používám výrazy x &= ~(1 << y) jen pro názornost, aby bylo jasné, který bit nuluji a který nastavuji do log. 1. Celé nastavení by samozřejmě šlo provést jediným zápisem binárního čísla. Případně se spolehnout na to, že po restartu jsou v registru nuly, ale kód by nebyl tak čitelný. V hlavní smyčce pak sledujeme vlajku ACI. Pokud je nastavena a došlo ke změně výstupu komparátoru, tak chvíli počkáme (ignorujeme zákmity – “zavíráme oči”). Teprve pak se podíváme, jaká je (ustálená) hodnota výstupu komparátoru ACO a podle toho zareagujeme a nastavíme PB0. Nakonec vlajku zápisem log. 1 vynulujeme a čekáme na další událost.

 

001: // Jednoduchá časová hystereze
002: #define F_CPU 1000000
003: #include<avr/io.h>
004: #include<util/delay.h>
005:
006: int main(void){
007: ACSR |= (1<<ACBG);// na AIN0 zapojíme interní referenci,
008: // nastavujeme přerušení od komparátoru na jakoukoli
009: // změnu, ale přerušení zakazujeme
010: // jde nám jen o to sledovat vlajku přerušení ACI
011: // a chceme, aby log. 1 indikovala jakoukoli změnu výstupu
012: // komparátoru
013: ACSR = ~((1<<ACIS1) | (1<<ACIS0) | (1<<ACIE));
014: ADCSRB &= ~(1<<ACME);// nechci zapínat multiplexer
015: // vypneme vstupní buffery
016: DIDR0 |= (1<<ADC2D) | (1<<ADC1D);
017: DDRB |= (1<<DDB0);// výstup abychom se měli na co dívat
018:
019: while(1){
020: if(ACSR & (1<<ACI)){// sledujeme vlajku ACI, pokud je nastavena, došlo ke změně
021: // na výstupu komparátoru
022: _delay_us(500);// umrtvíme komparátor na dostatečnou dobu, aby se signál
023: // stihl vzdálit od reference
024: // nastavujeme výstup PB0 podle výstupu komparátoru (ACO)
025: if(ACSR & (1<<ACO)){PORTB=0b1;}
026: else{PORTB=0b0;}
027: ACSR |=(1<<ACI);// mažeme vlajku, aby mohla být znovu nastavena událostí
028: // (změnou výstupu komparátoru)
029: }
030: }
031: }
Vzorové zdrojové kódy slouží pouze k jednoduché demonstraci funkce, proto často obsahují globální proměnné, neobsahují většinou ukazatele, dále také neobsahují kontroly proměnných, definování nevyužitých pinů mikrokontroléru a podobné správné programátorské návyky. Proto je nutné tyto kódy brát s patřičnou rezervou.
 

Výsledek (obr. 7.) už zákmity neobsahuje, ale platíme za to daň v podobě “zdržení” v hlavní smyčce. Pokud bychom měli zároveň kontrolovat nějaké další události a nemohli bychom si takové zpoždění dovolit, museli bychom k tomuto účelu využít nějaký z časovačů, případně nějakou jinou fintu. U mnoha aplikací vám ale toto řešení nebude vadit. Pokud byste se ale dostali do bezvýchodné situace a ani jedno z předvedených řešení by vám nestačilo, nic vám nebrání použít externí komparátor a pomocí několika rezistorů si do něj zabudovat libovolnou hysterezi.

Obr. 7: Výsledek s

Obr. 7: Výsledek s “Časovou” hysterezí – na tomto obrázku není nic zajímavého vidět :D

Obr. 8: Ukázka zkušebního zapojení

Obr. 8: Ukázka zkušebního zapojení

 

V příštím díle si ukážeme další praktické ukázky jako například použití analogového komparátoru pomocí přerušení apod. Rozhodně se máte na co těšit ;-)

 
Autor: Michal Dudka
 

Následující a předchozí příspěvek v kategorii:

 
Následující: ATtiny24 analogový komparátor – 3. Díl Praktické ukázky II
Předchozí: ATtiny24 analogový komparátor – 1. Díl Teoretický popis

 
Tajned facebook
 

Za případné chyby v textu, ve zdrojovém kódě, nebo ve schématickém zapojení se omlouváme.
AUTOŘI NEBEROU ŽÁDNOU ODPOVĚDNOST ZA PŘÍPADNÉ ÚJMY NA ZDRAVÍ ČI MAJETKU.