Operácie

Implementácia funkcie map(): Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
StudentMIPS (diskusia | príspevky)
 
(44 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 10: Riadok 10:
'''Literatúra:'''  
'''Literatúra:'''  
* [http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob]
* [http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob]
* [http://www.humanbenchmark.com/tests/reactiontime/index.php Vyskúšajte si zmerať reakciu on-line]
 




Riadok 19: Riadok 19:
A/D prevodník je 10-bitový, preto vracia hodnoty v rozsahu 0 až 1023. Túto hodnotu program najprv filtruje pomocou kĺzavého priemeru z 8 alebo 16 vzoriek.
A/D prevodník je 10-bitový, preto vracia hodnoty v rozsahu 0 až 1023. Túto hodnotu program najprv filtruje pomocou kĺzavého priemeru z 8 alebo 16 vzoriek.


Následne sa hodnota prepočíta pomocou vlastnej funkcie map(). Funkcia prepočítava interval 0 až 1023 na interval 0 až 500. Hodnota 500 predstavuje 5,00 V, pretože napätie je v programe uložené v stotinách voltu. Vďaka tomu je možné vypísať napätie s presnosťou 0,01 V bez použitia typu float.
Následne sa hodnota prepočíta pomocou vlastnej funkcie map(). Funkcia prepočítava interval 0 až 1023 na interval 0 až 500. Hodnota 500 predstavuje 5,00 V (hodnota 0 - 0,00 V), pretože napätie je v programe uložené v stotinách voltu. Vďaka tomu je možné vypísať napätie s presnosťou 0,01 V bez použitia typu float.


Potenciometer je zapojený ako delič napätia. Krajné vývody sú pripojené na VCC a GND, stredný vývod je pripojený na analógový vstup ADC4 mikrokontroléra ATmega328P.
Potenciometer je zapojený ako delič napätia. Krajné vývody sú pripojené na VCC a GND, stredný vývod je pripojený na analógový vstup ADC4 mikrokontroléra ATmega328P.
Riadok 26: Riadok 26:
[[Súbor:photo_2026-06-06_01-19-49.jpg|400px|thumb|center|Schéma zapojenia.]]
[[Súbor:photo_2026-06-06_01-19-49.jpg|400px|thumb|center|Schéma zapojenia.]]


Na meranie napätia bol použitý laboratórny modul s potenciometrom, ktorý bol pripojený k vývojovej doske. V programe sa využíva iba výstup z potenciometra, preto je v schéme zapojenia znázornený len potenciometer. Ostatné prvky modulu použité neboli. Pri otáčaní potenciometra sa mení vstupná hodnota A/D prevodníka, ktorá sa v programe načítava z kanála ADC4.
Na meranie napätia bol použitý laboratórny modul s potenciometrom, ktorý bol pripojený k vývojovej doske s mikrokontrolérom ATmega328P. V programe sa využíva iba výstup z potenciometra, preto je v schéme zapojenia znázornený len potenciometer. Ostatné prvky modulu použité neboli.
 
Pri otáčaní potenciometra sa mení vstupná hodnota A/D prevodníka. Táto hodnota sa v programe načítava z kanála ADC4. Na doske Arduino Uno tento vstup zodpovedá pinu A4.


[[Súbor:Photo_2026-06-06_01-32-33.jpg|400px|thumb|center|Celkový pohľad na zariadenie.]]
[[Súbor:Photo_2026-06-06_01-32-33.jpg|400px|thumb|center|Celkový pohľad na zariadenie.]]
Riadok 33: Riadok 35:
=== Algoritmus a program ===
=== Algoritmus a program ===


Algoritmus programu využíva toto a toto, základné funkcie sú takéto a voláma ich tuto...  
Na komunikáciu cez UART boli použité súbory uart.h a uart.c.
Výpis kódu je nižšie...
Hlavičkový súbor uart.h je pripojený v hlavnom programe a obsahuje deklarácie funkcií.
Súbor uart.c obsahuje samotnú implementáciu funkcií pre sériovú komunikáciu.
 
Program najprv inicializuje UART, A/D prevodník a PWM výstup. V nekonečnom cykle sa načíta hodnota z analógového vstupu ADC4. Táto hodnota sa upraví pomocou filtra kĺzavého priemeru a potom sa pomocou funkcie <code>map()</code> prepočíta na napätie.
 
Počet vzoriek filtra sa nastavuje pomocou konštanty:
 
<code>#define POCET_VZORIEK 16</code>
 
Ak chceme použiť priemer z 8 vzoriek, stačí zmeniť túto konštantu na:
 
<code>#define POCET_VZORIEK 8</code>
 
V programe bol použitý priemer zo 16 vzoriek, pretože pri väčšom počte vzoriek je výsledná hodnota stabilnejšia a menej citlivá na malé náhodné odchýlky merania.
Výsledok sa vypisuje cez UART vo formáte:
 
<code>ADC = hodnota, U = x.xx V</code>
 
Celá časť napätia sa získa delením hodnoty <code>napatie / 100</code> a desatinná časť pomocou <code>napatie % 100</code>.
 
Hodnota z A/D prevodníka sa zároveň používa aj na nastavenie PWM výstupu:
 
<code>OCR0A = hodnota / 4;</code>
 
Delenie číslom 4 je použité preto, že A/D prevodník má rozsah 0 až 1023, ale PWM register OCR0A má rozsah 0 až 255.
 
V programe je použitá aj úprava hodnoty v tvare <code>1023 - adc_read(kanal)</code>. Táto úprava bola potrebná preto, že pri zapojení potenciometra bola hodnota A/D prevodníka opačná: pri minimálnej polohe potenciometra - 1023 a pri maximálnej - 0.
 
Funkcia <code>map()</code> bola odvodená zo vzťahu pre lineárne prepočítanie hodnoty z jedného intervalu do druhého. Vstupná hodnota x je v rozsahu in_min až in_max a výstupná hodnota má byť v rozsahu out_min až out_max.
 
Najprv sa hodnota x posunie vzhľadom na začiatok vstupného intervalu:
 
<code>x - in_min</code>
 
Potom sa táto hodnota vynásobí veľkosťou výstupného intervalu:
 
<code>(x - in_min) * (out_max - out_min)</code>
 
Následne sa výsledok vydelí veľkosťou vstupného intervalu:
 
<code>((x - in_min) * (out_max - out_min)) / (in_max - in_min)</code>
 
Nakoniec sa pripočíta začiatok výstupného intervalu out_min. Výsledná rovnica je:
 
<code>y = ((x - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min</code>
 
Vo funkcii <code>map()</code> je použitý typ uint32_t, pretože pri výpočte vzniká medzivýsledok:
 
<code>1023 * 500 = 511500</code>
 
Táto hodnota sa nezmestí do typu uint16_t, preto musí byť výpočet vykonaný v 32-bitovej celočíselnej aritmetike. Funkcia <code>map()</code> preto vracia hodnotu typu uint32_t.




<tabs>
<tab name="AVR C-code"><syntaxhighlight  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 <avr/io.h>
#include <stdio.h>
#include "uart.h"
#define LED1 PD6               
#define POCET_VZORIEK 16
FILE uart_output = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
void adc_init(void)
{
    ADMUX = (1<<REFS0);                           
    ADCSRA = (1<<ADEN)                             
          | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); 
}
uint16_t adc_read(char channel)
{
    channel &= 0x0F;                 
    ADMUX = (ADMUX & 0xF0) | channel;
   
    ADCSRA |= (1<<ADSC);             
    while(ADCSRA & (1<<ADSC))       
    {
     
    }
   
    return (ADC);   
}
uint32_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min,uint16_t out_max)
{
return (uint32_t)(x - in_min)*(out_max - out_min)/(in_max - in_min) + out_min;
}
uint16_t klzavy_priemer(uint8_t kanal)
{
uint32_t sucet = 0;
for(uint8_t i=0; i < POCET_VZORIEK; i++)
{
sucet += 1023 - adc_read(kanal);
}
return sucet/POCET_VZORIEK;
}
void pwm_init(void)
{
DDRD |= (1<<PD6);
TCCR0A = (1<<COM0A1) | (1<<WGM01) | (1<<WGM00);
TCCR0B = (1<<CS01) | (1<<CS00);
}
int main(void)
int main(void)
{
{
   unsigned int measuredValue;
    
 
  uint16_t hodnota;
  uint32_t napatie;
 
  adc_init();
  uart_init();
  pwm_init();
  stdout = &uart_output;
 
 
   while (1)
   while (1)
   {
   {
     /*  relax  */
     hodnota = klzavy_priemer(4);
napatie = map(hodnota, 0, 1023, 0, 500);
if(hodnota>512)
{
PORTD |= (1<<PD6);
}
else{
PORTD &= ~(1<<PD6);
}
    printf("ADC = %u, U = %lu.%02lu V\n", hodnota, napatie/100, napatie % 100);
OCR0A = hodnota/4;
   }
   }


   return(0);
   return(0);
}
}


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


void adc_init(void);                                  // A/D converter initialization
Zdrojový kód:[[Médiá:Implementácia_funkcie_map().rar|Implementácia_funkcie_map()]]


unsigned int adc_read(char a_pin);
=== Overenie ===
</syntaxhighlight ></tab>
</tabs>


Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x ''zdrojaky.zip'':
Funkčnosť programu bola overená pomocou sériového výstupu v programe PuTTY. Pri otáčaní potenciometra sa menila hodnota A/D prevodníka aj vypočítané napätie.


Zdrojový kód: [[Médiá:Implementácia_funkcie_map().zip|Implementácia_funkcie_map().zip]]
Príklad výstupu:
 
=== Overenie ===


Ako ste overili funkciu, napríklad... Na používanie našej aplikácie stačia dve tlačítka a postup používania je opísaný v sekcii popis riešenia.
<pre>
Na konci uvádzame fotku hotového zariadenia.
ADC =  573, U = 2.80 V
ADC =  814, U = 3.97 V
ADC = 1023, U = 5.00 V
</pre>


[[Súbor:GeminiAI-image1.jpg|400px|thumb|center|Aplikácia.]]
Z výstupu je vidieť, že hodnota z A/D prevodníka je správne prepočítaná na napätie v rozsahu 0 až 5 V s presnosťou 0,01 V.


'''Video:'''
'''Video:'''
<center><youtube>D0UnqGm_miA</youtube></center>
<center><youtube>JwuDRjgm8WE</youtube></center>  <center><youtube>gPRCLfDSpoc</youtube></center>


== Čo by som urobil inak ==
== Čo by som urobil inak ==


Zamyslite sa spätne nad problémom, ktorý ste riešili a napíšte, čo sa vám nepodarilo a nabudúce by ste spravili inak.  
Ak by som projekt robila znova, najprv by som si presnejšie overila smer zmeny hodnoty z potenciometra. V mojom prípade bola hodnota A/D prevodníka pri otáčaní potenciometra opačná, preto bolo potrebné v programe použiť úpravu <code>1023 - adc_read(kanal)</code>.


Tiež by som použila samostatný potenciometer namiesto celého laboratórneho modulu. Zapojenie by bolo jednoduchšie a prehľadnejšie, pretože by sa využívali iba tri vývody potenciometra: VCC, GND a analógový vstup.


Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.


[[Category:AVR]] [[Category:MIPS]]
[[Category:AVR]] [[Category:MIPS]]

Aktuálna revízia z 14:49, 6. jún 2026

Záverečný projekt predmetu MIPS / LS2026 - Dariia Svystak


Zadanie

Implementácia funkcie map()] V prostredí ArduinoIde máme možnosť použiť funkciu map(). Prepíšte túto funkciu tak, aby sme napätie merané v rozsahu 0 až 5V pomocou 10 b-ého A/D prevodníka vedeli zobraziť s presnosťou na „0,01V“. Použite celočíselnú aritmetiku. Výstup A/D prevodníkom filtrujte pomocou filtra kĺzavého priemeru – priemer s 8, resp. 16 vzoriek.

Vývojová doska ACROB.

Literatúra:


Analýza a opis riešenia

A/D prevodník je 10-bitový, preto vracia hodnoty v rozsahu 0 až 1023. Túto hodnotu program najprv filtruje pomocou kĺzavého priemeru z 8 alebo 16 vzoriek.

Následne sa hodnota prepočíta pomocou vlastnej funkcie map(). Funkcia prepočítava interval 0 až 1023 na interval 0 až 500. Hodnota 500 predstavuje 5,00 V (hodnota 0 - 0,00 V), pretože napätie je v programe uložené v stotinách voltu. Vďaka tomu je možné vypísať napätie s presnosťou 0,01 V bez použitia typu float.

Potenciometer je zapojený ako delič napätia. Krajné vývody sú pripojené na VCC a GND, stredný vývod je pripojený na analógový vstup ADC4 mikrokontroléra ATmega328P.


Schéma zapojenia.

Na meranie napätia bol použitý laboratórny modul s potenciometrom, ktorý bol pripojený k vývojovej doske s mikrokontrolérom ATmega328P. V programe sa využíva iba výstup z potenciometra, preto je v schéme zapojenia znázornený len potenciometer. Ostatné prvky modulu použité neboli.

Pri otáčaní potenciometra sa mení vstupná hodnota A/D prevodníka. Táto hodnota sa v programe načítava z kanála ADC4. Na doske Arduino Uno tento vstup zodpovedá pinu A4.

Celkový pohľad na zariadenie.


Algoritmus a program

Na komunikáciu cez UART boli použité súbory uart.h a uart.c. Hlavičkový súbor uart.h je pripojený v hlavnom programe a obsahuje deklarácie funkcií. Súbor uart.c obsahuje samotnú implementáciu funkcií pre sériovú komunikáciu.

Program najprv inicializuje UART, A/D prevodník a PWM výstup. V nekonečnom cykle sa načíta hodnota z analógového vstupu ADC4. Táto hodnota sa upraví pomocou filtra kĺzavého priemeru a potom sa pomocou funkcie map() prepočíta na napätie.

Počet vzoriek filtra sa nastavuje pomocou konštanty:

#define POCET_VZORIEK 16

Ak chceme použiť priemer z 8 vzoriek, stačí zmeniť túto konštantu na:

#define POCET_VZORIEK 8

V programe bol použitý priemer zo 16 vzoriek, pretože pri väčšom počte vzoriek je výsledná hodnota stabilnejšia a menej citlivá na malé náhodné odchýlky merania. Výsledok sa vypisuje cez UART vo formáte:

ADC = hodnota, U = x.xx V

Celá časť napätia sa získa delením hodnoty napatie / 100 a desatinná časť pomocou napatie % 100.

Hodnota z A/D prevodníka sa zároveň používa aj na nastavenie PWM výstupu:

OCR0A = hodnota / 4;

Delenie číslom 4 je použité preto, že A/D prevodník má rozsah 0 až 1023, ale PWM register OCR0A má rozsah 0 až 255.

V programe je použitá aj úprava hodnoty v tvare 1023 - adc_read(kanal). Táto úprava bola potrebná preto, že pri zapojení potenciometra bola hodnota A/D prevodníka opačná: pri minimálnej polohe potenciometra - 1023 a pri maximálnej - 0.

Funkcia map() bola odvodená zo vzťahu pre lineárne prepočítanie hodnoty z jedného intervalu do druhého. Vstupná hodnota x je v rozsahu in_min až in_max a výstupná hodnota má byť v rozsahu out_min až out_max.

Najprv sa hodnota x posunie vzhľadom na začiatok vstupného intervalu:

x - in_min

Potom sa táto hodnota vynásobí veľkosťou výstupného intervalu:

(x - in_min) * (out_max - out_min)

Následne sa výsledok vydelí veľkosťou vstupného intervalu:

((x - in_min) * (out_max - out_min)) / (in_max - in_min)

Nakoniec sa pripočíta začiatok výstupného intervalu out_min. Výsledná rovnica je:

y = ((x - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min

Vo funkcii map() je použitý typ uint32_t, pretože pri výpočte vzniká medzivýsledok:

1023 * 500 = 511500

Táto hodnota sa nezmestí do typu uint16_t, preto musí byť výpočet vykonaný v 32-bitovej celočíselnej aritmetike. Funkcia map() preto vracia hodnotu typu uint32_t.


#include <avr/io.h>

#include <avr/io.h>
#include <stdio.h>
#include "uart.h"

#define LED1 PD6                
#define POCET_VZORIEK 16

FILE uart_output = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);

void adc_init(void)
{
    ADMUX = (1<<REFS0);                             
    ADCSRA = (1<<ADEN)                              
           | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);  
}
 uint16_t adc_read(char channel)
{
     channel &= 0x0F;                  
     ADMUX = (ADMUX & 0xF0) | channel; 
    
    ADCSRA |= (1<<ADSC);              
    while(ADCSRA & (1<<ADSC))         
    { 
       
    }
    
    return (ADC);    
	}
	
	
uint32_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min,uint16_t out_max)
	{
		return (uint32_t)(x - in_min)*(out_max - out_min)/(in_max - in_min) + out_min;
	}		
	
uint16_t klzavy_priemer(uint8_t kanal)
 {
	 uint32_t sucet = 0;
	 for(uint8_t i=0; i < POCET_VZORIEK; i++)
	 {
		 sucet += 1023 - adc_read(kanal);
	 }
	 return sucet/POCET_VZORIEK;
}


	
void pwm_init(void)
	{
	DDRD |= (1<<PD6); 

	TCCR0A = (1<<COM0A1) | (1<<WGM01) | (1<<WGM00);
	TCCR0B = (1<<CS01) | (1<<CS00); 
	}
int main(void)
{
  
  uint16_t hodnota;
  uint32_t napatie;
  
  adc_init();
  uart_init();
  pwm_init();
  stdout = &uart_output;
  
  
  while (1)
  {
    hodnota = klzavy_priemer(4);
	
	napatie = map(hodnota, 0, 1023, 0, 500);
	
	if(hodnota>512)
	{
	PORTD |= (1<<PD6);
	}
	else{
		PORTD &= ~(1<<PD6);
	}
    printf("ADC = %u, U = %lu.%02lu V\n", hodnota, napatie/100, napatie % 100);
	OCR0A = hodnota/4;
  }

  return(0);
}

Zdrojový kód:Implementácia_funkcie_map()

Overenie

Funkčnosť programu bola overená pomocou sériového výstupu v programe PuTTY. Pri otáčaní potenciometra sa menila hodnota A/D prevodníka aj vypočítané napätie.

Príklad výstupu:

ADC =  573, U = 2.80 V
ADC =  814, U = 3.97 V
ADC = 1023, U = 5.00 V

Z výstupu je vidieť, že hodnota z A/D prevodníka je správne prepočítaná na napätie v rozsahu 0 až 5 V s presnosťou 0,01 V.

Video:

Čo by som urobil inak

Ak by som projekt robila znova, najprv by som si presnejšie overila smer zmeny hodnoty z potenciometra. V mojom prípade bola hodnota A/D prevodníka pri otáčaní potenciometra opačná, preto bolo potrebné v programe použiť úpravu 1023 - adc_read(kanal).

Tiež by som použila samostatný potenciometer namiesto celého laboratórneho modulu. Zapojenie by bolo jednoduchšie a prehľadnejšie, pretože by sa využívali iba tri vývody potenciometra: VCC, GND a analógový vstup.