A/D prevodník
Zo stránky SensorWiki
A/D prevodník
Úlohy
- Zapojte si na doštičke potenciometer s vývodom na niektorý z analógových vstupov. Môžete tiež vyskúšať fotorezistor, alebo termistor.
- Doplňte program o jednoduchý A/D prevod a zobrazte hodnotu z potenciometra na sériovom termináli (a príp. aj na LCD displej).
- Zistite, čo sa stane, ak budete merať nezapojený vstup ("zo vzduchu").
- Oboznámte sa s programom SerialPlot a zobrazte časový priebeh nameranej veličiny.
- Napokon pomocou potenciometra ovládajte intenzitu svitu niektorej LED diódy alebo polohu servomotorčeka.
Rekapitulácia
V tejto úlohe sa predpokladá znalosť funkcie A/D prevodníka z prednášky. Na posledných dvoch slajdoch máte pripravené funkcie na inicializáciu a načítanie výsledku prevodu. Preto sa v tomto návode nebudeme zaoberať týmito funkciami.

Hardware
Ako vstup do A/D prevodníka môžete použiť niektorý zo štyroch uvedených príkladov:
- obyčajný odporový delič, ktorým môžeme detekovať napríklad pokles napätia napájacej batérie
- potenciometer alebo trimer, ktorý máte v MISP kite
- fotorezistor, ale ten slúži len na orientačné merania, pretože nemáme referenčný zdroj osvetlenia
- termistor, ktorý tiež vieme využiť len orientačne, hoci k nemu by sme vedeli vypočítať teplotu podľa datasheetu (pozri tiež Stienhart-Hartovu rovnicu).
![]()
Schéma pripojenia vstupov k A/D prevodníku. Ak vám nepomôže, použite zapojovací diagram
A/D prevodník

Pre nameranú hodnotu N platí vzťah
Ako zdroj referenčného napätia použijeme priamo napájacie napätie 5V oddelené tlmivkou a filtrované paralelným kondenzátorom 100n (pozri interaktívnu schému zapojenia). Zodpovedajúce bity REFS1, REFS0 v registri ADMUX sú teda 01.
Zdroj hodinového signálu pre A/D prevodník musí byť v rozsahu 50 až 200 kHz!
Príklad najjednoduchšieho SW ovládaného A/D prevodu je uvedená v tomto príklade - pozri AVR A/D example.c
Poznámka k výpočtu: ak chcete naozaj prepočítavať AD hodnotu na napätie, nezaobídete sa bez reálnych čísiel typu float, resp. double. Neodporúčame to, pretože tým neúmerne narastie veľkosť kódu, ale ak to naozaj potrebujete, prečítajte si návod, ako upozorniť kompilátor, že chcete prilinkovať aj knižnicu pre prácu s reálnymi číslami: Typy premenných v avr-gcc.
Software
Ukážkový program pre prevod napätia na číslo pomocou zabudovaného A/D prevodníka. Obslužné funkcie sú deklarované v knižnici, definície si doplňte sami z prednášky.
#include <avr/io.h>
#include "adc.h"
int main(void)
{
unsigned int measuredValue;
adc_init(); // Init A/D converter
while (1)
{
measuredValue = adc_read(0);
/* tu s nou nieco spravime, napr. na LCD, UART,... */
}
return(0);
}
#include <avr/io.h>
void adc_init(void); // A/D converter initialization
unsigned int adc_read(char a_pin);
#include <avr/io.h>
... zvysok najdete v prednaske ...
#define aPin A0
int sensorValue = 0; // analog value
void setup()
{
Serial.begin(9600);
}
void loop() {
sensorValue = analogRead(aPin); // sample signal
Serial.println(sensorValue);
delay(100);
}
Vizualizácia
Vizualizáciu dát môžeme robiť rozlične. Jeden z možných spôsobov je vypisovať po sériovej linke v pravidelných intervaloch holé data a tie potom uložiť ako maticu do Matlabu a tam nakresliť graf, priebeh, čo treba...
Iná možnosť je použiť napr. program SerialPlot, ktorý kreslí prichádzajúce data priamo ako graf.
Download: SerialPlot 0.11.0
Download
Doplnky (2025): Pokročilé metódy spracovania dát
1. Obsluha cez prerušenie (Interrupts)
Pri bežnom meraní (tzv. busy waiting) procesor nečinne čaká približne 104 µs na dokončenie prevodu. V komplexných aplikáciách je toto zdržanie neprijateľné. Riešením je využitie prerušovacieho systému. Princíp:
- Procesor odštartuje prevod a robí inú prácu.
- V momente, keď ADC dokončí prácu, Hardvér nastaví príznak ADIF (ADC Interrupt Flag) po zapísaní výsledku.
- Ak je nastavený bit ADIE (ADC Interrupt Enable) a povolené globálne prerušenia, vyvolá sa obslužná rutina, v ktorej prečítame výsledok.
Úloha: Doplňte obsluhu prerušenia pre uloženie výsledku.
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint16_t adc_result = 0;
ISR(ADC_vect) // Obsluha prerušenia - vyvolá sa automaticky po skončení prevodu
{
// 1. Prečítajte 10-bitový výsledok z registra ADC
// 2. Uložte ho do globálnej premennej adc_result
/* DOPLŇTE KÓD SEM */
}
void adc_init_interrupt(void)
{
ADMUX = (1 << REFS0); // Referencia AVCC
// ADEN: povolenie ADC
// ADIE: povolenie prerušenia od ADC
// ADPS2:0: preddelič 128
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
sei(); // Globálne povolenie prerušení
}
2. Regulačná slučka s pevnou periódou vzorkovania
Pre stabilitu regulačných algoritmov je nevyhnutné, aby vzorkovanie prebiehalo v presne definovaných intervaloch. ATmega328P umožňuje tzv. automatické spúšťanie (Auto Triggering).
Princíp:
- Časovač (napr. Timer0) nastavíme tak, aby generoval udalosť každých 10 ms.
- V registri ADCSRA nastavíme bit ADATE (Auto Trigger Enable).
- Pomocou bitov ADTS (ADC Auto Trigger Source) v registri ADCSRB vyberieme ako zdroj štartu pretečenie časovača alebo zhodu (napr. Timer0 Compare Match A).
Prevod sa tak spustí automaticky hardvérom bez zásahu CPU, čo zaručuje minimálny jitter (časovú neistotu). Doplňte inicializáciu tak, aby časovač ovládal štart ADC.
void sampling_init_10ms(void)
{
// 1. Nastavenie Timer0 na generovanie udalosti každých 10ms
// (Využite vedomosti z predošlých cvičení o časovačoch)
// 2. Konfigurácia ADC
ADMUX = (1 << REFS0);
// Aktivácia Auto Trigger (ADATE) a nastavenie preddeliča 128
ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
// 3. Výber zdroja preklápania v ADCSRB (napr. Timer0 Compare Match A)
// Pozrite tabuľku "ADC Auto Trigger Source Selections"
/* DOPLŇTE KÓD SEM */
ADCSRB |= (/* bity ADTS */);
}
3. Digitálny filter
Pri meraní reálnych analógových veličín pomocou mikrokontroléra ATmega328P takmer vždy narazíte na problém so stabilitou výsledkov. Aj pri konštantnom vstupnom napätí bude posledný bit (LSB) výsledku kolísať v dôsledku vnútorného šumu čipu vyvolaného 16 MHz oscilátorom alebo vonkajšieho rušenia z napájacej siete. Digitálne filtrovanie je preto nevyhnutnou súčasťou spracovania signálu.
3.1. Bežný aritmetický priemer
Najjednoduchším riešením je odmerať určitý počet vzoriek (N), sčítať ich a výsledok vydeliť počtom meraní.
- Nevýhoda (Latencia): Systém musí vykonať všetkých N meraní, kým poskytne prvý výsledok. Ak meriame 64 vzoriek a jeden prevod trvá cca 104 µs, procesor čaká viac ako 6,6 ms.
- Nevýhoda (Skokovosť): Hodnota sa aktualizuje len raz za N cyklov, čo spôsobuje "trhaný" priebeh, ktorý nie je vhodný pre rýchle regulačné slučky.
3.2. Kĺzavý (plávajúci) priemer (Moving Average)
Kĺzavý priemer rieši problém odozvy tým, že udržiava históriu posledných vzoriek v kruhovom poli (bufferi). Každá nová vzorka nahradí v pamäti tú najstaršiu.
- Výhoda (Plynulosť): Nový, spresnený výsledok máme k dispozícii okamžite po každom jednom meraní ADC.
- Výhoda (Filtrácia): Algoritmus funguje ako digitálny dolnopriepustný filter, ktorý vyhladzuje krátkodobé špičky a šum.
- Optimalizácia: V mikropočítačových systémoch volíme dĺžku okna ako mocninu dvojky (napr. 8, 16, 32). Delenie je totiž procesorovo náročná operácia, ktorú v tomto prípade nahradíme rýchlym bitovým posunom doprava.
3.3. Úloha: Implementácia filtra Doplnte logiku funkcie tak, aby efektívne počítala priemer bez zbytočného premazávania celého poľa v každom kroku. Keďže 10-bitový ADC vrací hodnoty do 1023, je nutné zvoliť správny dátový typ pre sumu, aby nedošlo k pretečeniu. Použitie typu `float` sa neodporúča kvôli úspore pamäte[cite: 644, 1571].
Kostra programu na doplnenie:
#include <avr/io.h>
#define BUFFER_SIZE 8 // Musí byť mocnina 2 pre optimalizáciu delenia
uint16_t samples[BUFFER_SIZE]; // Kruhový buffer pre vzorky
uint8_t sample_idx = 0; // Index aktuálnej vzorky
uint32_t sum = 0; // Celkový súčet (pozor na dátový typ!)
void filter_init() // Funkcia inicializuje buffer na nulu
{
for (uint8_t i = 0; i < BUFFER_SIZE; i++)
{
samples[i] = 0;
}
sum = 0;
}
uint16_t get_moving_average(uint16_t new_sample) // Funkcia pridá novú vzorku a vráti aktuálny kĺzavý priemer
{
// 1. Od súčtu (sum) odpočítajte najstaršiu vzorku uloženú v poli
// 2. Do poľa na aktuálny index uložte novú vzorku (new_sample)
// 3. K súčtu (sum) pripočítajte túto novú vzorku
// 4. Posuňte index (sample_idx) na ďalšiu pozíciu (pozor na pretečenie buffera!)
/* DOPLŇTE KÓD SEM */
// 5. Vráťte priemer (súčet vydelený BUFFER_SIZE pomocou bitového posunu >>)
return (uint16_t)(sum >> 3); // Pre 8 vzoriek posun o 3 bity
}
int main(void)
{
adc_init(); // Inicializácia podľa prednášky
filter_init();
while(1)
{
uint16_t raw_val = adc_read(0); // Čítanie z kanála 0
uint16_t filtered_val = get_moving_average(raw_val);
...
}
}
Literatúra:
- AVR120: Characterization and Calibration of the ADC on an AVR
- Newbie's Guide to AVR A/D Converter
- Základná terminológia týkajúca sa ADC
- Moving Average aj s vyuzitim delenia 4, 16 a pod. cez >>