Reklama

Začínáme s mikrokontroléry ATmega – A/D převodník 4. díl (poslední praktické příklady)

Úvod

V tomto díle se podíváme na poslední tři praktické příklady použití A/D převodníku u mikrokontrolérů ATmega. Konkrétně si projdeme příklady:
 

  • G) ATmega644P – Diferenciální měření se zesilovačem
  • H) ATmega16 – Externím přerušením spouštěný ADC převod
  • I) ATmega16 – Free running ADC s přerušením

 

Zdrojové kódy ke stažení lze nalézt na konci článku.

G) ATmega644P – Diferenciální měření se zesilovačem

V předchozím příkladě jsme si ukázali jak zacházet s A/D převodníkem v diferenciálním režimu. A také jsme se zmínili, že asi tím nejzajímavějším je možnost využít interního zesilovače a rozdílový signál si zesílit. V tomhle příkladě si to vyzkoušíme. Situací, kdy se vám možnost zesílení signálu hodí je mnoho. My si předvedeme jednu. Představte si, že chcete měřit elektrický proud. Můžete mu do cesty vřadit rezistor o dobře známé hodnotě a měřit na něm úbytek napětí (tak to taky dělají ampérmetry v multimetrech). Přirozeně ale nechcete měřením obvod nijak výrazně narušovat. Proto musíte volit hodnotu rezistoru nízkou. Jenže na malém odporu jsou i malá napětí. A ty si přímo říkají o to, aby je člověk zesílil. My k tomu využijeme zesilovač, který je součástí A/D převodníku. Sestavíme si obvod tak jak je na Obr. 1. Proud budeme snímat na odporu o hodnotě 1 Ohm. S touto hodnotou se nám bude proud snadno přepočítávat (1 mV odpovídá 1 mA). V naší úloze budou proudy dosahovat maximálně hodnoty 100 mA, zvolíme tedy převod se ziskem 10x. Nejvyšší měřené napětí 100 mV bude mít po zesílení hodnotu 1 V a na jeho měření bude vhodné zvolit referenci 1.1 V. Tady pozor, tato volba je v rozporu s datasheetem, který uvádí, že pro diferenciální měření musí být reference nejméně 2.56 V. I přes to však příklad dopadl úspěšně – čímž jsem byl mile překvapen. Co je však ještě milejší fakt, že v tomto zapojení měřil převodník i záporné proudy (!). Tady ale pozor, během pokusů nesmíte nikdy překročit absolutní limit, který specifikuje, že na žádném pinu Mikrokontroléru nesmí být záporné napětí větší než 0.5 V. Znamená to tedy, že proud tekoucí opačným směrem (z GND “nahoru”) nesmí překročit hodnotu 500 mA. Samozřejmě tak jak je zapojení nakresleno nepřipadá tato situace v úvahu, pokud ale namísto RLOAD připojíte baterii, může proud téci oběma směry (nabíjení/vybíjení). Díky tomu že A/D převodník pracuje i se zápornými hodnotami, můžete měřit proud nabíjení i vybíjení baterie. Protože to ale je v rozporu s provozními parametry ADC, tak vám raději shchéma nenakreslím, abyste nepochytili špatné návyky :D Přesnost měření není přirozeně nijak valná, v rozsahu 0 – 100 mA dosahovala odchylka v nejhorším případě 2 mA. Kvalitním externím zesilovačem dosáhnete lepší přesnosti. Nicméně ne vždy ji budete vyžadovat. Dalším limitem, který je dobré mít na zřeteli je omezení maximální frekvence pro vstup A/D převodníku, když je zapnutý zesilovač. Ten totiž nezkresluje jen signály do 4 kHz. Vyšší frekvence může zkreslovat. Pro naše de facto stejnosměrné měření nás to ale nemusí trápit.

 
Obr. 1: Ukázka měření proudu pomocí diferenciálního ADC s vnitřním zesilovačem

Obr. 1: Ukázka měření proudu pomocí diferenciálního ADC s vnitřním zesilovačem

 

Pokud budete volit hodnotu snímacího odporu jinou (pro větší proudy volíte nižší odpor) nebo jiný zisk vnitřního zesilovače (třeba 200x) ulehčím vám práci a zapíšu obecnou rovnici pro výpočet napětí z výsledku měření.

 
I[mA] = ADC x VREF[mV]/(512 x ZISK x R)
 

Kde R je hodnota snímacího rezistoru, ZISK je zesílení zesilovačů (1x,10x,200x), Vref je referenční napětí a ADC je výsledek převodu. Při reprezentování desetinných čísel pomocí celočíselných typů je potřeba vhodně volit pozici desetinné čárky (viz Fixed poin arithmetic). Špatnou volbou se můžete připravit o část přesnosti, nebo si zbytečně komplikovat výpočty velkými číselnými typy. Já část přesnosti obětoval a zvolil jsem si jako základní jednotku mA. Když se náhodou v matematice ztratíte a chcete zjistit, jak dobře jste si zvolili pozici desetinné čárky, stačí sledovat jak se mění výsledek, když se o jedničku změní výsledek převodu. Můj výpočet přiřadí hodnotám ADC v rozsahu 301 až 304 vždy stejný výsledek 66 mA. Mohl bych tedy výslednou hodnotu vyjadřovat přesněji. Podobně o tom vypovídá i logika, že maximální měřitelný rozsah přibližně 2 x 110 mA (kladné a záporné) dělím pouze na 2×110 dílů. Ačkoli mám k dispozici A/D převodník s 1024 kroky. Mohl bych tedy počítat 1024 / 220 = 4 a půl krát přesněji. Protože je však odchylka celého měření typicky 1 mA, nemá smysl počítat výsledek přesněji.

Nezdržujme se dále teorií a okomentujme si samotný příklad. Všimněte si, že celé měření se stává ze dvou kroků. V prvém kroku nuluji offset. Jestli jste si prohlíželi tabulku všech možností vstupního multiplexeru, tak vás jistě udivila možnost měřit rozdíl napětí mezi ADC0 a ADC0, případně mezi ADC2 a ADC2. Tyto varianty slouží právě k nulování offsetu. Pokud by interní zesilovač vykazoval nějaký offset (přidával by k výsledku stále stejné napětí), můžete ho touto cestou změřit a následně od výsledku odečíst. A to přesně v příkladu dělám. Zapsáním hodnoty 0b01000 do MUX4..0 volím ADC0-ADC0 se ziskem 10x a měřím offset. Po změně multiplexeru musím počkat, než se výstup zesilovače ustálí (u 4 Khz je to přibližně 1 / f = 250 µs). Teprve poté mohu převádět a výsledek převodu upravit a uložit do proměnné offset. V druhé části měřícího cyklu pak volím diferenciální kanál ADC1-ADC0, opět čekám na ustálení výstupu zesilovače a od výsledku měření odečtu offset. Pak provedu již dříve diskutovanou aritmetiku a výsledek pošlu do PC. Stejně jako v předchozím příkladě si dejte pozor že příklad je připraven pro čip s kmitočtem 16 MHz.

 

001: //G) Atmega644P – Diferenciální ADC se zesilovačem –
002: // měření proudu
003: #include <avr/io.h>
004: #define F_CPU 16000000
005: #include <util/delay.h>
006: #include <stdio.h>// kvůli fci printf_P()
007: #include <avr/pgmspace.h>// kvůli fci printf_P()
008:
009: #define VREF 1126// měřením na AREF stanovená hodnota vnitřní
009: // reference ("1.1V") v mV
010: #define ZISK 10// zisk zesilovačů u ADC (10x)
011:
012: // odesílání UARTem
013: int usart_putchar(char var, FILE *stream);
014: void setup_uart(void);
015:
016: static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar, NULL, _FDEV_SETUP_WRITE);
017: int16_t hodnota,i,offset;
018:
019: int main(void){
020: setup_uart();// nastavení komunikace
021: DIDR0 = (1<<ADC0D) | (1<<ADC1D);
022: // ADCclock=F_CPU/128
023: ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
024:
025: while (1) {
026: // měření vnitřní nesymetrie (offsetu)
027: // interní reference 1.1V, výsledek zarovnat
028: // doleva (signed int !)
029: // diferenciální vstup ADC0-ADC0 (10x)
030: ADMUX = (1<<REFS1) | (1<<ADLAR) | 0b01000;
031: _delay_us(250);// zesilovače u ADC pracují max na 4kHz (počkat
032: // na ustálení jejich výstupu)
033: ADCSRA |= (1<<ADSC);// spustit převod
034: while(ADCSRA & (1<<ADSC)){}// čekej dokud je ADSC=1 (probíhá převod)
035: offset = ADC;// vyčti výsledek převodu (zarovnaný vlevo)
036: offset = offset>>6;// pozor, posun pro signed int ! interpretace záleží
037: // na překladači !
038:
039: // měření vnějšího napětí (proudu skrze rezistor R)
040: // diferenciální vstup ADC1-ADC0 (10x)
041: ADMUX = (1<<REFS1) | (1<<ADLAR) | 0b01001;
042: _delay_us(250);// zesilovače u ADC pracují max na 4kHz (počkat na
043: // ustálení jejich výstupu)
044: ADCSRA |= (1<<ADSC);// spustit převod
045: while(ADCSRA & (1<<ADSC)){}// čekej dokud je ADSC=1 (probíhá převod)
046: hodnota = ADC;// vyčti výsledek převodu (zarovnaný vlevo)
047: hodnota = hodnota>>6;// pozor, posun pro signed int ! interpretace
048: // záleží na překladači !
049: hodnota = hodnota–offset;// korekce
050: // přepočíst na proud v mA
051: i = (int16_t)((int32_t)hodnota*VREF/(512*ZISK));
052: printf_P(PSTR("ADC = %i, U = %i mA\n\r"),hodnota, i);
053: _delay_ms(700);
054: }
055: }
056:
057: void setup_uart(void){
058: stdout = &mystdout;// nastavení standardního výstupu na
059: // naši funkci usart_putchar()
060: UBRR0L = 103;// baudrate 9600 s clockem 16MHz
061: UCSR0B = (1<<TXEN0);// zapnout UART vysílač
062: // formát zprávy “8N1″
063: UCSR0C = (1<<UCSZ00) | (1<<UCSZ01);
064: }
065:
066: int usart_putchar(char var, FILE *stream) {
067: while (!(UCSR0A & (1<<UDRE0)));
068: UDR0 = var;
069: return 0;
070: }
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.
 
H) ATmega16 – Externím přerušením spouštěný ADC převod

V tomto příkladu si předvedeme, jak lze přesně ovládat okamžik zahájení A/D převodu. V modelovém příkladě přivádíme na pin ADC2 měřený signál (z oscilogramů níže je patrné, že jde o sadu pulzů z biologického vzorku). Na pin PD2 nějaký vnější obvod přivádí pulzy, jejichž vzestupnou hranou označuje okamžiky kdy je potřeba měřit napětí. Mikrokontrolér dokáže v rámci relativně krátkého času (hrubě pod 1 µs) zareagovat a zahájit převod. Bohužel samotné “samplování” A/D převodníkem trvá 1.5 jeho strojového cyklu, tedy typicky 15 µs (při 100 kHz hodin pro převodník). Pro rozumně pomalé signály (jako v našem příkladě) je ale okamžik převodu docela dobře definován. Popravdě tuto funkci jsem ještě nikdy nevyužil, takže vám asi neprozradím, jaká může najít uplatnění. Nicméně je dobré, když budete alespoň vědět, že máte něco takového k dispozici. Člověk totiž nikdy neví, co jednoho dne bude potřebovat.

A/D převodník může být “trigrován” hned několika signály. Může to být výstup analogového komparátoru, nebo Externí přerušení (INT0). Dále pak signály od časovačů a to buď okamžik přetečení (Overflow), Compare událost a případně u Čítače/Časovače 1 i Capture událost. Zvláště užitečné bude “trigrování” časovačem, protože mimo jiné umožňuje dobře definovat vzorkovací frekvenci kontinuálního A/D převodu. Protože ale nemám jistotu, že se s časovači kamarádíte, zvolil jsem pro ukázku raději externí přerušení (INT0). Protože spouštěcí signál převodu může přijít naprosto kdykoliv, tak je na místě využít ke zpracování dat přerušení od A/D převodníku. Jinak bychom museli neustále sledovat stavový bit ADIF, což je nemalá komplikace pro program.

Nastavení není nijak zvlášť složité. V registru ADMUX, vybereme referenci (interní 2.56 V) a kanál (ADC2). Dále nastavíme hodiny pro převodník. Protože mikrokontrolér běží na 16 MHz, musím použít děličku 128, abych frekvenci hodin převodníku dostal do vhodných mezí (50 – 200 kHz). Nastavením bitu ADIE povolím přerušení od dokončení převodu a bitem ADATE povolím spouštění vnitřním signálem. Jeho zdroj si vybírám v registru SFIOR pomocí bitů ADTS2ATDS0. Tady udělám malou vsuvku jazyka “C”. Když potřebuji upravit část nějakého registru, o němž nemám informace, a nechci zasahovat do jeho zbylé části, musím to udělat ve dvou krocích. Nejprve všechny bity, které měním, smažu a pak do těch do kterých potřebuji, zapíšu jedničky. Jiný postup v “C”, který by zapsal libovolné hodnoty na libovolná místa a neovlivnil by ostatní bity neznám. Přirozeně mohl bych vycházet z toho, že těsně po startu je registr SFIOR v předem známém stavu a celou operaci udělat jedním zápisem, ale tento přístup je obecnější (správně bych ho měl aplikovat v podstatě na všechny přístupy do registrů). Tím je A/D převodník připraven. Teď zbývá, nastavím externí přerušení. O tom si můžete přečíst v předchozích dílech (ZDE a ZDE). V MCUCR aktivuji detekci vzestupné hrany na kanálu INT0 (PD2). Samotné externí přerušení (v registru GICR) ale nepovoluji. Nepotřebuji vyvolávat rutinu přerušení od INT0. Před globálním povolením všech přerušení pro jistotu smažu stavový bit ADIF, pro případ, že signál z jakéhokoli důvodu spustil A/D převod během nastavování. A ještě smažu stavový bit INTF0 v registru GIFR. To je nutnost, neboť právě nastavení tohoto bitu z log.0 do log.1 spustí A/D převod. Kdyby stavový bit zůstal z jakéhokoli důvodu nastaven, nikdy by se převod nespustil a program by na to čekal marně. Jakmile přijde spouštěcí signál (vzestupná hrana) na INT0 (PD2), dojde k nastavení stavového bitu INTF0, čímž se spustí A/D převod. Po jeho dokončení se zavolá rutina přerušení a program v ní nejprve vyčte výsledek převodu, poté smaže bit INTF0 (aby tím umožnil dalšímu spuštění) a nakonec dá pomocí proměnné prevod_dokoncen hlavní smyčce informaci o tom, že má k dispozici nová data. Nezapomínejte, že globální proměnná, která se nachází v hlavní smyčce programu a v rutině přerušení musí být deklarována jako “volatile”. Jinak si překladač bude myslet, že v rámci hlavní smyčky nemůže změnit hodnotu a celou smyčku v rámci optimalizace programu vypustí. V hlavní smyčce už pak jen pomocí funkce Printf odešleme vypočítanou hodnotu do PC.

 

001: // H) Atmega16 – Externím přerušením spouštěný ADC převod
002:
003: #include <avr/io.h>
004: #include <avr/interrupt.h>
005: #define F_CPU 16000000
006: #include <stdio.h>// kvůli fci printf_P()
007: #include <avr/pgmspace.h>// kvůli fci printf_P()
008:
009: #define VREF 2560// měřením na AREF stanovená hodnota
010: // vnitřní reference ("2.56V") v mV
011:
012: // odesílání UARTem
013: int usart_putchar(char var, FILE *stream);
014: void setup_uart(void);
015:
016: static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar, NULL, _FDEV_SETUP_WRITE);
017: uint16_thodnota,u;
018: volatile char prevod_dokoncen=0;
019:
020: ISR(ADC_vect){
021: hodnota=ADC; // dokončen převod ADC, vyzvednout data
022: GIFR |= (1<<INTF0);// mažeme vlajku od INT0 aby nám mohla příště spustit
023: // další převod
024: prevod_dokoncen=1;
025: }
026:
027: int main(void){
028: setup_uart();
029: // chytáme nástupnou hranu na INT0
030: MCUCR = (1<<ISC01) | (1<<ISC00);
031: // reference interní 2.56V, měříme na ADC2
032: ADMUX = (1<<REFS1) | (1<<REFS0) | 0b00010;
033: // ADC povolit, Auto Trigger povolit, Přerušení od
034: // ADC povolit, ADCclock=F_CPU/128
035: ADCSRA = (1<<ADEN) | (1<<ADATE) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
036: SFIOR &= 0x1f;// mažeme nastavení ADTS2 až ADTS1 (nevím
037: // co tam bylo minule)
038: SFIOR |= (1<<ADTS1);// nastavujeme trigger pro ADC (Externím přerušením INT0)
039: GIFR |= (1<<INTF0);// mažeme vlajku přerušení INT0 (pro jistotu, kdyby
040: // tam “visela” z minula)
041: ADCSRA |= (1<ADIF);// mažeme vlajku ADC (není nutné)
042: sei(); // globální povolení přerušení
043:
044: while (1){
045: if(prevod_dokoncen){
046: prevod_dokoncen=0;
047: u = (uint16_t)((uint32_t)hodnota*VREF/1023);
048: printf_P(PSTR("U=%umV\n\r"),u);
049: }
050: }
051: }
052:
053: void setup_uart(void){
054: stdout = &mystdout;// nastavení standardního výstupu na
055: // naši funkci usart_putchar()
056: UBRRL = 103;// baudrate 9600 s clockem 16MHz
057: UCSRB = (1<<TXEN);// zapnout UART vysílač
058: // formát zprávy “8N1″
059: UCSRC = (1<<URSEL)| (1<<UCSZ0) | (1<<UCSZ1);
060: }
061:
062: int usart_putchar(char var, FILE *stream) {
063: while (!(UCSRA & (1<<UDRE)));
064: UDR = var;
065: return 0;
066: }
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.
 

Na následujícím obrázku Obr. 2 pak můžete vidět výsledek. Žlutý průběh znázorňuje měřený signál a modrý průběh je spouštěcí signál. V krátkém časovém intervalu po příchodu vzestupné hrany na spouštěcím signálu proběhne převod. Výsledek převodu je pak pomocí USARTu odeslán do PC (viz růžová linka). Orientační odečtení úrovně vstupního signálu pomocí kurzorů ukazuje přibližně 700 mV. Z odeslaných dat můžeme vyčíst, že výsledek převodu byl 705 mV. Což je vzhledem k nejistotě s jakou odečítáme hodnotu pomocí kurzorů docela dobrá shoda.

 
Obr. 2: Externím signálem spouštěný A/D převod. Žlutý průběh je měřený signál, modrý průběh je spouštěcí signál.

Obr. 2: Externím signálem spouštěný A/D převod. Žlutý průběh je měřený signál, modrý průběh je spouštěcí signál.
I) ATmega16 – Free running ADC s přerušením

V předchozím příkladě, jsme si předvedli signálem spouštěný převod. Záměrně jsme opominuli jeden signál, kterým jde převod spustit. A to je ukončení předchozího převodu. Díky tomu, lze spustit A/D převodník v režimu “free running”, v němž probíhá převod stále dokola. Po skončení jednoho převodu je zahájen další. Taková funkce najde uplatnění v několika případech. V situacích, kdy potřebujete neustále monitorovat jeden kanál a chcete mít záruku, že kdykoli se program rozhodne bude mít k dispozici co nejčerstvější data. V takovém případě spustíte převodník ve Free-running módu a kdykoli se vám zamane, tak přečtete obsah “registru” ADC a v něm bude výsledek posledního převodu. Data tak nebudou nikdy starší než dobu jednoho převodu. Další možností kdy se vám tento režim může hodit je monitorování časového průběhu nějakého signálu. Prostě když potřebujete “osciloskop”. K takovým účelům typicky využijete časovačem spouštěného převodu, ale jestliže potřebujete měřit co nejrychleji, může být Free-running režim vhodnější. Přitom informaci o okamžicích záznamu neztrácíte, protože víte, jak dlouho jeden převod trvá. My si využití tohoto režimu ukážeme na primitivním příkladu. Opět budeme mít neznámý vstupní signál, jehož časový průběh chceme proměřit. Aby to ale nebyla úplná nuda, tak zkusíme z A/D převodníku vyždímat co nejvíc. Dle datasheetu víme, že bychom měli A/D převodník taktovat frekvencí 50 – 200 kHz. Což by při 13 cyklech na jeden převod odpovídalo periodě 65 µs. To bychom náš signál, který trvá přibližně 100 µs, neměli nejmenší šanci zachytit. Musíme tedy A/D převodník “přetaktovat”. Což samo o sobě není nic nepřípustného. V datasheetu se dočtete, že pokud oželíte přesnost, smíte taktovat převodník až na 1 MHz. My ale nebudeme troškařit a pustíme mu 2 MHz (vzhledem k hodinám mikrokontroléru – 16 MHz volíme děličku pro A/D převodník 8). Jeden převod by tak měl trvat přibližně 6.5 µs.

Měřený signál přivedeme na ADC1. Na pinu PC0 budeme signalizovat okamžiky ukončení převodu (to je jen pro názornost). A/D převodník konfigurujeme podobně jako v předchozím příkladu. Tedy pomocí ADIE povolujeme přerušení, bitem ADATE vybíráme automatický “trigrovaný” převod. Nastavíme interní 2.56 V referenci a zvolíme k převodu kanál ADC1. Zdroj spouštění specifikujeme v registru SFIOR kde všechny tři bity ATDSx vynulujeme. První převod je nutné spustit “ručně”, tedy nastavením bitu ADSC. Pak se už A/D převodník rozběhne sám a pravidelně volá rutinu přerušení. V ní program výsledek převodu uloží do pole hodnota. Celý záznam má 32 vzorků (jejich počet specifikuje definice SAMPLES). Po změření všech vzorků převod zastavíme vynulováním bitu ADCSRA. Hlavní smyčka pak celý blok dat zpracuje a odešle do PC. Tím celý program končí. Resetováním mikrokontroléru ho spustíte znovu. Přirozeně by se celý proces dal pojmout nějak komplexněji, ale to už je dáno konkrétní aplikací. Takže je na vás, zda a jak příklad včleníte do vašeho projektu.

 

001: // I) Atmega16A – Free running ADC s přerušením
002:
003: #include <avr/io.h>
004: #define F_CPU 16000000
005: #include <util/delay.h>
006: #include <stdio.h>// kvůli fci printf_P()
007: #include <avr/pgmspace.h>// kvůli fci printf_P()
008: #include <avr/interrupt.h>
009:
010: #define SAMPLES 32// počet vzorků v měření
011:
012: // odesílání UARTem
013: int usart_putchar(char var, FILE *stream);
014: void setup_uart(void);
015:
016: static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar, NULL, _FDEV_SETUP_WRITE);
017: // pole změřených hodnot
018: volatile uint16_thodnota[SAMPLES];
019: volatile char i=0;// počítadlo vzorků
020:
021: int main(void){
022: DDRC |= (1<<DDC0);// PC0 k indikaci okamžiku převodu
023: setup_uart();// nastavení komunikace
024: // měříme na ADC1, s interní referencí 2.56V
025: ADMUX = (1<<REFS1) | (1<<REFS0) | 0b00001;
026: // povolit převodník, povolit přerušení, povolit
027: // "trigger", ADC clock F_CPU/8
028: ADCSRA = (1<<ADEN) | (1<<ADATE) | (1<<ADIE) | (1<<ADPS1) | (1<<ADPS0);
029: _delay_us(75);// čas na stabilizaci interní reference
030: SFIOR &= 0x1f;// [ADTS2..ATDS0]=0 (trigger source: Free running)
031: sei();// globální povolení přerušení
032: ADCSRA |=(1<<ADSC);// spuštění prvního převodu (od teď běží ADC samo)
033: while(1){
034: if(i==SAMPLES){// až je převod dokončen
035: for(i=0;i<SAMPLES;i++){// odešli všechny vzorky do PC
036: printf_P(PSTR("%u\n\r"),hodnota[i]);
037: }
038: i=0;
039: }
040: }
041: }
042:
043: ISR(ADC_vect){
044: PORTC=0xff;// indikace kdy došlo k převodu
045: hodnota[i]=ADC;// uložíme výsledek převodu
046: i++;// příště ukládáme další vzorek
047: if(i>=SAMPLES){ADCSRA=0;}// pokud jsou změřený všechny vzorky, vypni ADC
048: PORTC=0;// konec indikace
049: }
050:
051: void setup_uart(void){
052: stdout = &mystdout;// nastavení standardního výstupu na naši
053: // funkci usart_putchar()
054: UBRRL = 103;// baud rate 9600 s clockem 8MHz
055: UCSRB = (1<<TXEN);// zapnout UART vysílač
056: // formát zprávy “8N1″
057: UCSRC = (1<<URSEL) | (1<<UCSZ0) | (1<<UCSZ1);
058: }
059:
060: int usart_putchar(char var, FILE *stream) {
061: while (!(UCSRA & (1<<UDRE)));
062: UDR = var;
063: return 0;
064: }
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.
 

Na obrázku Obr. 3, je patrný průběh převodu. Žlutou barvou je znázorněn měřený signál, modrou barvou jsou pak zvýrazněny okamžiky převodu. Vidíte, že perioda převodu je přibližně 6.4 µs, což v rámci tolerance odečtu osciloskopem odpovídá předpokládané hodnotě. Celkem by mělo, proběhnou 32 převodů. Na obrázku Obr. 4 pak vidíte jednoduchou rekonstrukci signálu ze změřených dat. Všimněte si, že do PC posíláme surová data, bez přepočtu na napětí. Při tomto druhu měření je to typické, protože zaokrouhlováním při přepočtu bychom ztratili část informace. A protože jsou data určena k pozdějšímu zpracování, neprovádíme na nich žádné ztrátové operace. Orientačním výpočtem (ADC / 1024 x 2.56) můžeme vidět, že vrchol křivky dosahuje přibližně 1.5 V (což odpovídá záznamu z osciloskopu).

 
Obr. 3: Žlutý průběh - měřený signál, Modrý průběh - značky okamžiků převodu

Obr. 3: Žlutý průběh – měřený signál, Modrý průběh – značky okamžiků převodu

 

 
Obr. 4:  Jednoduchá rekonstrukce změřeného průběhu

Obr. 4: Jednoduchá rekonstrukce změřeného průběhu

 

 
Autor: Michal Dudka
 

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

 
Předchozí: Začínáme s mikrokontroléry ATmega – A/D převodník 3. díl (další praktické příklady)

 
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.