A/D prevodník: Rozdiel medzi revíziami
Zo stránky SensorWiki
dBez shrnutí editace |
|||
| (12 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
| Riadok 7: | Riadok 7: | ||
# Zistite, čo sa stane, ak budete merať nezapojený vstup ("zo vzduchu"). | # Zistite, čo sa stane, ak budete merať nezapojený vstup ("zo vzduchu"). | ||
# Oboznámte sa s programom SerialPlot a zobrazte časový priebeh nameranej veličiny. | # Oboznámte sa s programom SerialPlot a zobrazte časový priebeh nameranej veličiny. | ||
# Napokon pomocou potenciometra ovládajte intenzitu svitu niektorej LED diódy. | # Napokon pomocou potenciometra ovládajte intenzitu svitu niektorej LED diódy alebo polohu servomotorčeka. | ||
=== Rekapitulácia === | === Rekapitulácia === | ||
| Riadok 19: | Riadok 20: | ||
[[Súbor:MIPS_AD-Suciastky.jpg|300px|right]] | |||
=== Hardware === | === Hardware === | ||
| Riadok 26: | Riadok 28: | ||
* potenciometer alebo trimer, ktorý máte v MISP kite | * 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 | * 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 [http://senzor.robotika.sk/mmp/2322640.pdf datasheetu]. | * termistor, ktorý tiež vieme využiť len orientačne, hoci k nemu by sme vedeli vypočítať teplotu podľa [http://senzor.robotika.sk/mmp/2322640.pdf datasheetu] (pozri tiež [https://en.wikipedia.org/wiki/Steinhart%E2%80%93Hart_equation Stienhart-Hartovu rovnicu]). | ||
<div style='text-align: center;'> | <div style='text-align: center;'> | ||
[[Súbor:MIPS_ADC-Schema01.png| | [[Súbor:MIPS_ADC-Schema01.png|800px]]<BR> | ||
''Schéma pripojenia vstupov k A/D prevodníku. Ak vám nepomôže, použite [[Media:MIPS_ADC-Schema02.png|zapojovací diagram]]'' | ''Schéma pripojenia vstupov k A/D prevodníku. Ak vám nepomôže, použite [[Media:MIPS_ADC-Schema02.png|zapojovací diagram]]'' | ||
</div> | |||
=== A/D prevodník === | === A/D prevodník === | ||
| Riadok 60: | Riadok 63: | ||
Príklad najjednoduchšieho SW ovládaného A/D prevodu je uvedená v tomto príklade - pozri [[AVR A/D example.c]] | Príklad najjednoduchšieho SW ovládaného A/D prevodu je uvedená v tomto príklade - pozri [[AVR A/D example.c]] | ||
<BR><BR> | |||
<FONT Color = "red">'''Poznámka k výpočtu:'''</FONT> 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]]. | |||
<BR><BR> | |||
<BR><BR> | |||
=== Software === | === Software === | ||
| Riadok 67: | Riadok 73: | ||
<tabs> | <tabs> | ||
<tab name="AVR C-code">< | <tab name="AVR C-code"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#include <avr/io.h> | #include <avr/io.h> | ||
#include "adc.h" | #include "adc.h" | ||
| Riadok 88: | Riadok 94: | ||
</ | </syntaxhighlight></tab> | ||
<tab name="adc.h">< | <tab name="adc.h"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#include <avr/io.h> | #include <avr/io.h> | ||
| Riadok 95: | Riadok 101: | ||
unsigned int adc_read(char a_pin); | unsigned int adc_read(char a_pin); | ||
</ | </syntaxhighlight></tab> | ||
<tab name="adc.c">< | <tab name="adc.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#include <avr/io.h> | #include <avr/io.h> | ||
... zvysok najdete v prednaske ... | ... zvysok najdete v prednaske ... | ||
</ | </syntaxhighlight></tab> | ||
<tab name="Arduino code">< | <tab name="Arduino code"><syntaxhighlight lang="arduino" style="background: #9dd1e1;"> | ||
#define aPin A0 | #define aPin A0 | ||
| Riadok 118: | Riadok 124: | ||
delay(100); | delay(100); | ||
} | } | ||
</ | </syntaxhighlight></tab> | ||
</tabs> | </tabs> | ||
| Riadok 159: | Riadok 165: | ||
[[Obrázok:Icon_StampPlotLite.png]] [http://www.parallax.com/Portals/0/Downloads/sw/spl.zip StampPlot Lite 1.7] | [[Obrázok:Icon_StampPlotLite.png]] [http://www.parallax.com/Portals/0/Downloads/sw/spl.zip StampPlot Lite 1.7] | ||
--> | --> | ||
== 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. | |||
<tabs> | |||
<tab name="AVR C-code"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
#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í | |||
} | |||
</syntaxhighlight></tab> | |||
</tabs> | |||
=== 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. | |||
<tabs> | |||
<tab name="AVR C-code"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
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 */); | |||
} | |||
</syntaxhighlight></tab> | |||
</tabs> | |||
=== 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: | |||
<tabs> | |||
<tab name="AVR C-code"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
#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); | |||
... | |||
} | |||
} | |||
</syntaxhighlight></tab> | |||
</tabs> | |||
Literatúra: | Literatúra: | ||
| Riadok 165: | Riadok 333: | ||
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=56429 Newbie's Guide to AVR A/D Converter] | * [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=56429 Newbie's Guide to AVR A/D Converter] | ||
* [https://www.arnabkumardas.com/arduino-tutorial/adc-concept/ Základná terminológia týkajúca sa ADC] | * [https://www.arnabkumardas.com/arduino-tutorial/adc-concept/ Základná terminológia týkajúca sa ADC] | ||
* [https://stackoverflow.com/questions/28820904/how-to-efficiently-compute-average-on-the-fly-moving-average Moving Average] aj s vyuzitim delenia 4, 16 a pod. cez >> | |||
[[Mikropočítačové systémy (MIPS)#Cvičenia|Návrat na zoznam cvičení...]] | |||
[[Category:AVR]][[Category:MMP]][[Category:DVPS]] | [[Category:AVR]][[Category:MIPS]][[Category:MMP]][[Category:DVPS]] | ||
Aktuálna revízia z 19:56, 2. apríl 2026
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 >>