Implementácia funkcie map(): Rozdiel medzi revíziami
Zo stránky SensorWiki
| (29 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] | ||
| 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 | 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 37: | Riadok 39: | ||
Súbor uart.c obsahuje samotnú implementáciu funkcií pre sériovú komunikáciu. | 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. | 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: | Počet vzoriek filtra sa nastavuje pomocou konštanty: | ||
#define POCET_VZORIEK 16 | |||
<code>#define POCET_VZORIEK 16</code> | |||
Ak chceme použiť priemer z 8 vzoriek, stačí zmeniť túto konštantu na: | Ak chceme použiť priemer z 8 vzoriek, stačí zmeniť túto konštantu na: | ||
#define POCET_VZORIEK 8 | |||
<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 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: | Výsledok sa vypisuje cez UART vo formáte: | ||
ADC = hodnota, U = x.xx V | <code>ADC = hodnota, U = x.xx V</code> | ||
Celá časť napätia sa získa delením hodnoty napatie / 100 a desatinná časť pomocou napatie % 100. | 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: | Hodnota z A/D prevodníka sa zároveň používa aj na nastavenie PWM výstupu: | ||
OCR0A = hodnota / 4; | <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. | 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. | 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 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. | 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: | Najprv sa hodnota x posunie vzhľadom na začiatok vstupného intervalu: | ||
x - in_min | <code>x - in_min</code> | ||
Potom sa táto hodnota vynásobí veľkosťou výstupného intervalu: | Potom sa táto hodnota vynásobí veľkosťou výstupného intervalu: | ||
(x - in_min) | <code>(x - in_min) * (out_max - out_min)</code> | ||
Následne sa výsledok vydelí veľkosťou vstupného intervalu: | Následne sa výsledok vydelí veľkosťou vstupného intervalu: | ||
((x - in_min) | <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: | Nakoniec sa pripočíta začiatok výstupného intervalu out_min. Výsledná rovnica je: | ||
y = ((x - in_min) | <code>y = ((x - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min</code> | ||
Vo funkcii map() je použitý typ uint32_t, pretože pri výpočte vzniká medzivýsledok: | Vo funkcii <code>map()</code> je použitý typ uint32_t, pretože pri výpočte vzniká medzivýsledok: | ||
1023 | <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. | 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. | ||
| Riadok 175: | Riadok 181: | ||
</syntaxhighlight ></tab> | </syntaxhighlight ></tab> | ||
Zdrojový kód:[[Médiá:Implementácia_funkcie_map().rar|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: | |||
<pre> | |||
ADC = 573, U = 2.80 V | |||
ADC = 814, U = 3.97 V | |||
ADC = 1023, U = 5.00 V | |||
</pre> | |||
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> | <center><youtube>JwuDRjgm8WE</youtube></center> <center><youtube>gPRCLfDSpoc</youtube></center> | ||
== Čo by som urobil inak == | == Č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 <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. | |||
[[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.

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.

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.

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.