Operácie

A/D prevodník: Rozdiel medzi revíziami

Zo stránky SensorWiki

Balogh (diskusia | príspevky)
Balogh (diskusia | príspevky)
dBez shrnutí editace
 
(13 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 17: Riadok 18:


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.
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.
[[Súbor:MIPS_AD-Suciastky.jpg|300px|right]]
=== 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 [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;'>
[[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]]''
</div>


=== A/D prevodník ===
=== A/D prevodník ===
Riadok 46: 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 53: Riadok 73:


<tabs>
<tabs>
<tab name="AVR C-code"><source lang="c++" style="background: LightYellow;">
<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 74: Riadok 94:




</source></tab>
</syntaxhighlight></tab>
<tab name="adc.h"><source lang="c++" style="background: LightYellow;">
<tab name="adc.h"><syntaxhighlight lang="c++" style="background: LightYellow;">
#include <avr/io.h>
#include <avr/io.h>


Riadok 81: Riadok 101:


unsigned int adc_read(char a_pin);
unsigned int adc_read(char a_pin);
</source></tab>
</syntaxhighlight></tab>
<tab name="adc.c"><source lang="c++" style="background: LightYellow;">
<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 ...


</source></tab>
</syntaxhighlight></tab>
<tab name="Arduino code"><source lang="arduino" style="background: #9dd1e1;">
<tab name="Arduino code"><syntaxhighlight lang="arduino" style="background: #9dd1e1;">


#define aPin A0
#define aPin A0
Riadok 104: Riadok 124:
   delay(100);
   delay(100);
}
}
</source></tab>
</syntaxhighlight></tab>
</tabs>
</tabs>


Riadok 145: 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 151: 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 >>






'''TODO: prerobit na verziu 2022:'''
[[Mikropočítačové systémy (MIPS)#Cvičenia|Návrat na zoznam cvičení...]]
* prekontrolovat, ci je vsetko platne a pridat nieco z tohoto:
* pridat obsluhu cez prerusenie
* sampling - pridat timer a vzorkovat v presnych casoch
* pridat Moving Average odtialto https://stackoverflow.com/questions/28820904/how-to-efficiently-compute-average-on-the-fly-moving-average aj s vyuzitim delenia 4,16 a pod. cez >>
 
 
 
 


[[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

  1. 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.
  2. 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).
  3. Zistite, čo sa stane, ak budete merať nezapojený vstup ("zo vzduchu").
  4. Oboznámte sa s programom SerialPlot a zobrazte časový priebeh nameranej veličiny.
  5. 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

Terminal 1.9b

Serial Plot 0.11.0



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:


Návrat na zoznam cvičení...