Ultrazvukový radar s HC-SR04: Rozdiel medzi revíziami
Zo stránky SensorWiki
| (24 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
| Riadok 17: | Riadok 17: | ||
== Analýza a opis riešenia == | == Analýza a opis riešenia == | ||
Zariadenie je určené na meranie vzdialenosti prekážky pomocou ultrazvukového senzora HC-SR04. Ako riadiaca jednotka bola použitá vývojová doska Arduino Uno s mikrokontrolérom ATmega328P. Arduino zabezpečuje | Zariadenie je určené na meranie vzdialenosti prekážky pomocou ultrazvukového senzora HC-SR04. Ako riadiaca jednotka bola použitá vývojová doska Arduino Uno s mikrokontrolérom ATmega328P. Arduino zabezpečuje ovládanie senzora, spracovanie nameraného časového impulzu a následný výpočet vzdialenosti v centimetroch. Výsledok merania sa zobrazuje cez sériovú linku v počítači. | ||
Senzor HC-SR04 má štyri vývody: VCC, TRIG, ECHO a GND. Vývod VCC je pripojený na napájanie 5 V | Senzor HC-SR04 má štyri vývody: VCC, TRIG, ECHO a GND. Vývod VCC je pripojený na napájanie 5 V a vývod GND na zem. Vývod TRIG je pripojený na digitálny výstup Arduina a slúži na spustenie merania. Vývod ECHO je pripojený na digitálny vstup Arduina a prenáša časový impulz, ktorý sa ďalej spracúva v programe. | ||
Pri riešení bol využitý časovač Timer1 mikrokontroléra ATmega328P. Timer1 slúži na presné meranie dĺžky impulzu na výstupe ECHO. Nameraná hodnota sa následne prepočíta na vzdialenosť v centimetroch a vypíše sa cez sériovú linku do terminálu v počítači. | |||
=== Princíp merania === | === Princíp merania === | ||
Meranie sa začne krátkym spúšťacím impulzom na pine TRIG. Tento impulz má dĺžku približne 10 µs. Po jeho prijatí senzor vyšle ultrazvukový signál a čaká na jeho odraz od prekážky. Počas tohto čakania nastaví výstup ECHO do logickej 1. Keď sa odrazený signál vráti späť, ECHO sa nastaví do logickej 0. Program preto meria čas medzi nábežnou a zostupnou hranou ECHO impulzu. | |||
=== Výpočet vzdialenosti === | === Výpočet vzdialenosti === | ||
Ako sme už vyššie spomínali, meranie vzdialenosti je založené na meraní času, za ktorý sa ultrazvukový signál dostane od senzora k prekážke a po odraze späť k senzoru. Senzor HC-SR04 teda nemeria priamo vzdialenosť, ale čas trvania impulzu na výstupe ECHO. | Ako sme už vyššie spomínali, meranie vzdialenosti je založené na meraní času, za ktorý sa ultrazvukový signál dostane od senzora k prekážke a po odraze späť k senzoru. Senzor HC-SR04 teda nemeria priamo vzdialenosť, ale čas trvania impulzu na výstupe ECHO. | ||
| Riadok 54: | Riadok 56: | ||
Podrobne opíšte použité komponenty (okrem základnej dosky s ATmega328P procesorom), pridajte linky na datasheety alebo opis obvodu. | Podrobne opíšte použité komponenty (okrem základnej dosky s ATmega328P procesorom), pridajte linky na datasheety alebo opis obvodu. | ||
[[Súbor: | [[Súbor:HC-SR04_federmayer.jpg|600px|thumb|center|Celkový pohľad na zariadenie HC-SR04.]] | ||
=== Schéma zapojenia === | === Schéma zapojenia === | ||
Na schéme zapojenia je znázornené pripojenie ultrazvukového senzora HC-SR04 k vývojovej doske Arduino Uno. Senzor je napájaný z 5 V výstupu Arduina a jeho vývod GND je pripojený na spoločnú zem. Vývod TRIG je pripojený na digitálny pin D9 a vývod ECHO je pripojený na digitálny pin | Na schéme zapojenia je znázornené pripojenie ultrazvukového senzora HC-SR04 k vývojovej doske Arduino Uno. Senzor je napájaný z 5 V výstupu Arduina a jeho vývod GND je pripojený na spoločnú zem. Vývod TRIG je pripojený na digitálny pin D9 a vývod ECHO je pripojený na digitálny pin D8. V programe je pin D9 nastavený ako výstup a pin D8 ako vstup. Pin D8 bol zvolený preto, že na mikrokontroléri ATmega328P zodpovedá vstupu ICP1, ktorý umožňuje meranie impulzu pomocou funkcie Timer1 Input Capture. | ||
[[Súbor:Schema2_federmayer.png|500px|thumb|center|Schéma zapojenia.]] | |||
=== Algoritmus a program === | |||
Program je napísaný v jazyku C pre mikrokontrolér ATmega328P. Na meranie vzdialenosti využíva ultrazvukový senzor HC-SR04, časovač Timer1 a sériovú komunikáciu UART. Vývod TRIG je pripojený na pin D9 a slúži na spustenie merania. Vývod ECHO je pripojený na pin D8, ktorý zodpovedá vstupu ICP1 pre funkciu Input Capture časovača Timer1. | |||
Program je rozdelený do viacerých funkcií. Funkcia timer1_init() nastavuje Timer1 do normálneho režimu, zapína preddeličku 8 a povoľuje prerušenie Input Capture. Funkcia senzor_init() nastavuje smer pinov, pričom TRIG je výstup a ECHO je vstup. Funkcia senzor_start() spustí nové meranie tým, že vyšle približne 10 µs impulz na pin TRIG a zároveň pripraví Timer1 na zachytenie nábežnej hrany ECHO impulzu. | |||
Meranie dĺžky impulzu prebieha v obsluhe prerušenia ISR(TIMER1_CAPT_vect). Pri nábežnej hrane sa uloží aktuálna hodnota registra ICR1 do premennej start. Následne sa časovač prepne na zachytenie zostupnej hrany. Pri zostupnej hrane sa vypočíta šírka impulzu ako rozdiel ICR1 - start a nastaví sa príznak hotovo. | |||
V hlavnej funkcii main() sa najprv inicializuje UART, senzor a Timer1. Potom sa povolia prerušenia pomocou sei(). V nekonečnej slučke sa opakovane spúšťa meranie, čaká sa na jeho dokončenie a následne sa vypočíta vzdialenosť v centimetroch podľa vzťahu vzdialenost_cm = sirka_impulzu / 116. Výsledok sa vypisuje cez sériovú linku do terminálu. Ak sa meranie v stanovenom čase nedokončí, program vypíše chybové hlásenie. | |||
Zdroják + moje poznámky sú uvedené nižšie, spolu s knižnicami uart.h a uart.c. | |||
<tabs> | <tabs> | ||
<tab name="AVR C-code"><syntaxhighlight lang="c++" style="background: LightYellow;"> | <tab name="AVR C-code"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#define F_CPU 16000000UL | |||
#define TRIG PB1 // D9 | |||
#define ECHO PB0 // D8 | |||
#include <stdio.h> | |||
#include <avr/io.h> | #include <avr/io.h> | ||
#include <avr/interrupt.h> | |||
#include <util/delay.h> | |||
#include "uart.h" | |||
volatile unsigned int start = 0; | |||
volatile unsigned int sirka_impulzu = 0; | |||
volatile unsigned char hotovo = 0; | |||
FILE uart_stream = FDEV_SETUP_STREAM(uart_putc, uart_getc, _FDEV_SETUP_RW); | |||
void timer1_init(void) | |||
{ | |||
TCCR1A = 0x00; // normal mode | |||
TCCR1B = (1 << CS11); // clock 8 | |||
TCCR1B |= (1 << ICES1); // Capture on rising edge | |||
TCNT1 = 0; // 16-bit Counter reset | |||
TIMSK1 = (1 << ICIE1); // povolenie Input Capture prerusenia | |||
} | |||
void senzor_init(void) | |||
{ | |||
DDRB |= (1 << TRIG); // PB1 vystup | |||
DDRB &= ~(1 << ECHO); // PB0 vstup | |||
PORTB &= ~(1 << TRIG); // TRIG na zaciatku v 0, spusti sa az impulzikom | |||
} | |||
void senzor_start(void) | |||
{ | |||
hotovo = 0; // hotovo= ci uz meranie bolo dokoncene: 0 nie, 1 ano, teraz 0 lebo nove meranie nie je hotove | |||
sirka_impulzu = 0; // nulujem stary vysledok merania | |||
TCNT1 = 0; // hodnota timera1, teraz 0 lebo pocitam od 0 = vynulujem stopky pred meranim | |||
TCCR1B |= (1 << ICES1); // pri novom merani cakam na nabeznu hranu | |||
TIFR1 = (1 << ICF1); // vymazanie pripadneho stareho priznaku/sumu v input capture | |||
PORTB &= ~(1 << TRIG); // pred spustenim impulzu overim ze trig=0 = 0Volt | |||
_delay_us(2); // aby mal senzor pred spustenim stabilnu log 0 | |||
PORTB |= (1 << TRIG); // spustim impulz, trig=1 = 5Volt | |||
_delay_us(10); | |||
PORTB &= ~(1 << TRIG); // impulz skoncil dam spat do 0 | |||
//po tomto senzor vysle ultrazvuk a nastavi echo na log 1, timer1 input capture potom zachyti nabeznu a dobeznu hranu echo impulzu | |||
} | |||
//pozn. TCNT1 je register timeru1 obsahuje aktualnu hodnotu casovaca (stopky), teda timer1 pocita cas v TCNT1, a ked pride vybrana hrana, ulozi hodnotu do ICR1. | |||
//TCCR1B je riadiaci register pre timer1 = hovori ako ma pracovat - nastavuje ho, ICES1 je bit v tomto registri | |||
//ICES1 je bit v registri TCCR1B sluzi na vyber hrany pre input capture | |||
ISR(TIMER1_CAPT_vect) | |||
{ | |||
if (bit_is_set(TCCR1B, ICES1)) // zistujem aku hranu zachytaval timer1, ak ICES1=1 nabezna hrana, ak ICES1=0 dobezna hrana | |||
{ | |||
start = ICR1; // ICR1 ma hodnotu timer1 v okamihu zachytenia nabeznej hrany = tu zacal echo impulz | |||
TCCR1B &= ~(1 << ICES1); // vynulujem ICES1 lebo dalsiu hranu cakam dobeznu, teda nastavim timer1 aby dalsikrat cakal dobeznu hranu | |||
} | |||
else | |||
{ | |||
sirka_impulzu = ICR1 - start; //lebo teraz je v ICR1 cas konca echo impulzu | |||
hotovo = 1; //meranie je ukoncene | |||
TCCR1B |= (1 << ICES1); // nastavim takto aby pri dalsom merani cakal na nabeznu hranu | |||
} | |||
} | |||
int main(void) | int main(void) | ||
{ | { | ||
unsigned int vzdialenost_cm; | |||
unsigned char poc_cakania; // toto je pocitadlo cakania, aby program nefreezol keby sa echo impulz nevratil | |||
uart_init(); | |||
stdout = &uart_stream; | |||
senzor_init(); | |||
timer1_init(); | |||
sei(); //povoli prerusenia celkovo | |||
printf("HC-SR04 meranie vzdialenosti\r\n"); | |||
printf("TRIG = D9, ECHO = D8\r\n"); // \r\n znamena novy riadok v terminali | |||
while (1) | |||
{ | |||
senzor_start(); | |||
poc_cakania = 0; // zacinam cakat na vysledok noveho merania, teda musim ist od nuly | |||
while (!hotovo && poc_cakania < 60) // kym meranie este nie je dokoncene a cakam len do 60 krokov | |||
{ | |||
_delay_ms(1); // cakam 1ms, aby poc_cakania zodpovedal cca milisekundam | |||
poc_cakania++; | |||
}//tato slucka konci 2 moznostami: | |||
//hotovo sa nastavilo na 1 = meranie prebehlo | |||
//poc_cakania dosiahlo 60 = meranie zlyhalo alebo senzor nic nezachytil | |||
if (hotovo) // kontrola ci meranie bolo uspesne (bola zachytena aj nabezna aj dobezna) | |||
{ | |||
vzdialenost_cm = sirka_impulzu / 116; // lebo 1 tick je 0.5 [mikrosek], teda z rychlosti zvuku: vzdialenost = (0,0343 [cm/mikrosek] * cas)/2 (lebo meria sa tam a spat) ziskam: vzdialenost = cas/58, dalej viem ze cas = sirkaimpulzu*0.5, takze: vzdialenost = sirkaimpulzu*0.5/58 = sirkaimpulzu/116 | |||
printf("Vzdialenost: %u cm\r\n", vzdialenost_cm); | |||
} | |||
else | |||
{ | |||
printf("Chyba merania alebo mimo rozsah\r\n"); | |||
} | |||
_delay_ms(300); // aby sa merania nerobili furt, chvilu sa pocka | |||
} | |||
return 0; | |||
} | } | ||
</syntaxhighlight ></tab> | </syntaxhighlight ></tab> | ||
<tab name=" | <tab name="uart.h"><syntaxhighlight lang="c++" style="background: LightYellow;"> | ||
#ifndef uart_h_ | |||
#define uart_h_ | |||
void uart_init( void ); | |||
void uart_putc( char c ); | |||
void uart_puts( const char *s ); | |||
char uart_getc( void ); | |||
#endif /* UART_H_ */ | |||
</syntaxhighlight ></tab> | |||
<tab name="uart.c"><syntaxhighlight lang="c++" style="background: LightYellow;"> | |||
#include <avr/io.h> | #include <avr/io.h> | ||
#define F_CPU 16000000UL | |||
#define BAUD 9600 | |||
#include <util/setbaud.h> | |||
void uart_init( void ) | |||
{ | |||
UBRR0H = UBRRH_VALUE; | |||
UBRR0L = UBRRL_VALUE; | |||
#if USE_2X | |||
UCSR0A |= _BV(U2X0); | |||
#else | |||
UCSR0A &= ~(_BV(U2X0)); | |||
#endif | |||
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */ | |||
UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */ | |||
} | |||
void uart_putc(char c) | |||
{ | |||
if (c == '\n') | |||
{ | |||
uart_putc('\r'); | |||
} | |||
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */ | |||
UDR0 = c; | |||
} | |||
void uart_puts(const char *s) | |||
{ | |||
while (*s != '\0') | |||
{ | |||
uart_putc(*s); | |||
s++; | |||
} | |||
} | |||
char uart_getc(void) { | |||
loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */ | |||
return UDR0; | |||
} | |||
</syntaxhighlight ></tab> | </syntaxhighlight ></tab> | ||
</tabs> | </tabs> | ||
Zdrojový kód: [[Médiá:zdrojaky_Federmayer.zip|zdrojaky_Federmayer.zip]] | |||
Zdrojový kód: [[Médiá: | |||
=== Overenie === | === Overenie === | ||
Funkčnosť zariadenia bola overená praktickým meraním vzdialenosti. Po zapojení senzora k doske bol program nahratý do mikrokontroléra ATmega328P. Následne bol v počítači otvorený sériový terminál PuTTY, v ktorom sa zobrazovali namerané hodnoty vzdialenosti v cm. | |||
[[Súbor: | Overenie teda prebiehalo tak, že pred senzor bola umiestnená nejaká prekážka v rôznych vzdialenostiach. Pri zmene polohy prekážky sa menila aj hodnota vypisovaná v termináli. Tým sa overilo, že senzor správne reaguje na zmenu vzdialenosti a že program správne spracúva dĺžku impulzu ECHO. V prípade, že sa odrazený impulz nevrátil (napr. preto, že pred senzorom nebola prekážka), program vypísal chybové hlásenie. | ||
[[Súbor:Zariadenie_federmayer.jpg|500px|thumb|center|Moje zapojenie zariadenia.]] | |||
'''Video:''' | '''Video:''' | ||
<center><youtube> | <center><youtube>XPmf4I_7_0k</youtube></center> | ||
== Čo by som urobil inak == | == Čo by som urobil inak == | ||
Pri riešení projektu som postupoval tak, že som sa najskôr sústredil na základnú funkčnosť senzoru. Dôležité bolo hlavne správne zapojiť senzor, spustiť meranie a overiť, že sa nameraná vzdialenosť správne zobrazuje cez sériovú linku. Tento postup sa mi osvedčil, pretože pri prípadnej chybe bolo jednoduchšie zistiť, či problém vznikol v zapojení, v meraní impulzu alebo vo výpise do terminálu. | |||
Do budúcna by bolo možné projekt rozšíriť o zobrazovanie vzdialenosti na LCD displeji, čo som mal aj pôvodne v pláne. Avšak pri tomto riešení vznikol problém v tom, že displej, ktorý som mal k dispozícii doma, nebol rovnaký ako displej používaný na cvičeniach. Z toho dôvodu by bolo potrebné použiť inú knižnicu. Keďže používanie iných, cudzích knižníc je v rámci zadania zakázané, zvolil som riešenie so zobrazením výsledku cez sériovú linku. | |||
[[Category:AVR]] [[Category:MIPS]] | [[Category:AVR]] [[Category:MIPS]] | ||
Aktuálna revízia z 10:18, 4. jún 2026
Záverečný projekt predmetu MIPS / LS2026 - Werner Federmayer
Zadanie
Úlohou projektu bolo navrhnúť a naprogramovať ultrazvukový merač vzdialenosti s použitím senzora HC-SR04 a vývojovej dosky Arduino. Program má zabezpečiť vyslanie spúšťacieho impulzu na pin TRIG, odmeranie dĺžky impulzu na pine ECHO a následný výpočet vzdialenosti v centimetroch. Nameraná vzdialenosť sa zobrazuje cez sériovú linku.

Literatúra:
Analýza a opis riešenia
Zariadenie je určené na meranie vzdialenosti prekážky pomocou ultrazvukového senzora HC-SR04. Ako riadiaca jednotka bola použitá vývojová doska Arduino Uno s mikrokontrolérom ATmega328P. Arduino zabezpečuje ovládanie senzora, spracovanie nameraného časového impulzu a následný výpočet vzdialenosti v centimetroch. Výsledok merania sa zobrazuje cez sériovú linku v počítači.
Senzor HC-SR04 má štyri vývody: VCC, TRIG, ECHO a GND. Vývod VCC je pripojený na napájanie 5 V a vývod GND na zem. Vývod TRIG je pripojený na digitálny výstup Arduina a slúži na spustenie merania. Vývod ECHO je pripojený na digitálny vstup Arduina a prenáša časový impulz, ktorý sa ďalej spracúva v programe.
Pri riešení bol využitý časovač Timer1 mikrokontroléra ATmega328P. Timer1 slúži na presné meranie dĺžky impulzu na výstupe ECHO. Nameraná hodnota sa následne prepočíta na vzdialenosť v centimetroch a vypíše sa cez sériovú linku do terminálu v počítači.
Princíp merania
Meranie sa začne krátkym spúšťacím impulzom na pine TRIG. Tento impulz má dĺžku približne 10 µs. Po jeho prijatí senzor vyšle ultrazvukový signál a čaká na jeho odraz od prekážky. Počas tohto čakania nastaví výstup ECHO do logickej 1. Keď sa odrazený signál vráti späť, ECHO sa nastaví do logickej 0. Program preto meria čas medzi nábežnou a zostupnou hranou ECHO impulzu.
Výpočet vzdialenosti
Ako sme už vyššie spomínali, meranie vzdialenosti je založené na meraní času, za ktorý sa ultrazvukový signál dostane od senzora k prekážke a po odraze späť k senzoru. Senzor HC-SR04 teda nemeria priamo vzdialenosť, ale čas trvania impulzu na výstupe ECHO.
Rýchlosť zvuku vo vzduchu je približne v = 343 m/s pri izbovej teplote. Túto hodnotu môžeme previesť na centimetre za mikrosekundu:
343 m/s = 34300 cm/s = 0,0343 cm/μs
Nakoľko celková dráha predstavuje cestu od senzora k prekážke aj späť. Skutočná vzdialenosť prekážky od senzora je preto iba polovica tejto dráhy:
d = v * t / 2
Po dosadení rýchlosti zvuku v jednotkách cm/µs dostaneme:
d = (0,0343 · t) / 2
po úprave:
d = t / 58
kde t je čas impulzu ECHO v mikrosekundách a d je vzdialenosť v centimetroch.
V programe sa dĺžka impulzu Echo nemeria priamo v mikrosekundách, ale pomocou časovača ako počet tickov. Pri frekvencii mikrokontroléra 16 MHz a nastavenej preddeličke časovača 8 má jeden tick časovača dĺžku 0,5 µs. Ak označíme počet nameraných tikov ako N, potom čas impulzu je:
t = N * 0,5 μs
Po dosadení času vyjadreného pomocou počtu tikov dostaneme:
d = N * 0,5 / 58 = N / 116
Opíšte sem čo a ako ste spravili, ak treba, doplňte obrázkami... Podrobne opíšte použité komponenty (okrem základnej dosky s ATmega328P procesorom), pridajte linky na datasheety alebo opis obvodu.

Schéma zapojenia
Na schéme zapojenia je znázornené pripojenie ultrazvukového senzora HC-SR04 k vývojovej doske Arduino Uno. Senzor je napájaný z 5 V výstupu Arduina a jeho vývod GND je pripojený na spoločnú zem. Vývod TRIG je pripojený na digitálny pin D9 a vývod ECHO je pripojený na digitálny pin D8. V programe je pin D9 nastavený ako výstup a pin D8 ako vstup. Pin D8 bol zvolený preto, že na mikrokontroléri ATmega328P zodpovedá vstupu ICP1, ktorý umožňuje meranie impulzu pomocou funkcie Timer1 Input Capture.

Algoritmus a program
Program je napísaný v jazyku C pre mikrokontrolér ATmega328P. Na meranie vzdialenosti využíva ultrazvukový senzor HC-SR04, časovač Timer1 a sériovú komunikáciu UART. Vývod TRIG je pripojený na pin D9 a slúži na spustenie merania. Vývod ECHO je pripojený na pin D8, ktorý zodpovedá vstupu ICP1 pre funkciu Input Capture časovača Timer1.
Program je rozdelený do viacerých funkcií. Funkcia timer1_init() nastavuje Timer1 do normálneho režimu, zapína preddeličku 8 a povoľuje prerušenie Input Capture. Funkcia senzor_init() nastavuje smer pinov, pričom TRIG je výstup a ECHO je vstup. Funkcia senzor_start() spustí nové meranie tým, že vyšle približne 10 µs impulz na pin TRIG a zároveň pripraví Timer1 na zachytenie nábežnej hrany ECHO impulzu.
Meranie dĺžky impulzu prebieha v obsluhe prerušenia ISR(TIMER1_CAPT_vect). Pri nábežnej hrane sa uloží aktuálna hodnota registra ICR1 do premennej start. Následne sa časovač prepne na zachytenie zostupnej hrany. Pri zostupnej hrane sa vypočíta šírka impulzu ako rozdiel ICR1 - start a nastaví sa príznak hotovo.
V hlavnej funkcii main() sa najprv inicializuje UART, senzor a Timer1. Potom sa povolia prerušenia pomocou sei(). V nekonečnej slučke sa opakovane spúšťa meranie, čaká sa na jeho dokončenie a následne sa vypočíta vzdialenosť v centimetroch podľa vzťahu vzdialenost_cm = sirka_impulzu / 116. Výsledok sa vypisuje cez sériovú linku do terminálu. Ak sa meranie v stanovenom čase nedokončí, program vypíše chybové hlásenie.
Zdroják + moje poznámky sú uvedené nižšie, spolu s knižnicami uart.h a uart.c.
#define F_CPU 16000000UL
#define TRIG PB1 // D9
#define ECHO PB0 // D8
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "uart.h"
volatile unsigned int start = 0;
volatile unsigned int sirka_impulzu = 0;
volatile unsigned char hotovo = 0;
FILE uart_stream = FDEV_SETUP_STREAM(uart_putc, uart_getc, _FDEV_SETUP_RW);
void timer1_init(void)
{
TCCR1A = 0x00; // normal mode
TCCR1B = (1 << CS11); // clock 8
TCCR1B |= (1 << ICES1); // Capture on rising edge
TCNT1 = 0; // 16-bit Counter reset
TIMSK1 = (1 << ICIE1); // povolenie Input Capture prerusenia
}
void senzor_init(void)
{
DDRB |= (1 << TRIG); // PB1 vystup
DDRB &= ~(1 << ECHO); // PB0 vstup
PORTB &= ~(1 << TRIG); // TRIG na zaciatku v 0, spusti sa az impulzikom
}
void senzor_start(void)
{
hotovo = 0; // hotovo= ci uz meranie bolo dokoncene: 0 nie, 1 ano, teraz 0 lebo nove meranie nie je hotove
sirka_impulzu = 0; // nulujem stary vysledok merania
TCNT1 = 0; // hodnota timera1, teraz 0 lebo pocitam od 0 = vynulujem stopky pred meranim
TCCR1B |= (1 << ICES1); // pri novom merani cakam na nabeznu hranu
TIFR1 = (1 << ICF1); // vymazanie pripadneho stareho priznaku/sumu v input capture
PORTB &= ~(1 << TRIG); // pred spustenim impulzu overim ze trig=0 = 0Volt
_delay_us(2); // aby mal senzor pred spustenim stabilnu log 0
PORTB |= (1 << TRIG); // spustim impulz, trig=1 = 5Volt
_delay_us(10);
PORTB &= ~(1 << TRIG); // impulz skoncil dam spat do 0
//po tomto senzor vysle ultrazvuk a nastavi echo na log 1, timer1 input capture potom zachyti nabeznu a dobeznu hranu echo impulzu
}
//pozn. TCNT1 je register timeru1 obsahuje aktualnu hodnotu casovaca (stopky), teda timer1 pocita cas v TCNT1, a ked pride vybrana hrana, ulozi hodnotu do ICR1.
//TCCR1B je riadiaci register pre timer1 = hovori ako ma pracovat - nastavuje ho, ICES1 je bit v tomto registri
//ICES1 je bit v registri TCCR1B sluzi na vyber hrany pre input capture
ISR(TIMER1_CAPT_vect)
{
if (bit_is_set(TCCR1B, ICES1)) // zistujem aku hranu zachytaval timer1, ak ICES1=1 nabezna hrana, ak ICES1=0 dobezna hrana
{
start = ICR1; // ICR1 ma hodnotu timer1 v okamihu zachytenia nabeznej hrany = tu zacal echo impulz
TCCR1B &= ~(1 << ICES1); // vynulujem ICES1 lebo dalsiu hranu cakam dobeznu, teda nastavim timer1 aby dalsikrat cakal dobeznu hranu
}
else
{
sirka_impulzu = ICR1 - start; //lebo teraz je v ICR1 cas konca echo impulzu
hotovo = 1; //meranie je ukoncene
TCCR1B |= (1 << ICES1); // nastavim takto aby pri dalsom merani cakal na nabeznu hranu
}
}
int main(void)
{
unsigned int vzdialenost_cm;
unsigned char poc_cakania; // toto je pocitadlo cakania, aby program nefreezol keby sa echo impulz nevratil
uart_init();
stdout = &uart_stream;
senzor_init();
timer1_init();
sei(); //povoli prerusenia celkovo
printf("HC-SR04 meranie vzdialenosti\r\n");
printf("TRIG = D9, ECHO = D8\r\n"); // \r\n znamena novy riadok v terminali
while (1)
{
senzor_start();
poc_cakania = 0; // zacinam cakat na vysledok noveho merania, teda musim ist od nuly
while (!hotovo && poc_cakania < 60) // kym meranie este nie je dokoncene a cakam len do 60 krokov
{
_delay_ms(1); // cakam 1ms, aby poc_cakania zodpovedal cca milisekundam
poc_cakania++;
}//tato slucka konci 2 moznostami:
//hotovo sa nastavilo na 1 = meranie prebehlo
//poc_cakania dosiahlo 60 = meranie zlyhalo alebo senzor nic nezachytil
if (hotovo) // kontrola ci meranie bolo uspesne (bola zachytena aj nabezna aj dobezna)
{
vzdialenost_cm = sirka_impulzu / 116; // lebo 1 tick je 0.5 [mikrosek], teda z rychlosti zvuku: vzdialenost = (0,0343 [cm/mikrosek] * cas)/2 (lebo meria sa tam a spat) ziskam: vzdialenost = cas/58, dalej viem ze cas = sirkaimpulzu*0.5, takze: vzdialenost = sirkaimpulzu*0.5/58 = sirkaimpulzu/116
printf("Vzdialenost: %u cm\r\n", vzdialenost_cm);
}
else
{
printf("Chyba merania alebo mimo rozsah\r\n");
}
_delay_ms(300); // aby sa merania nerobili furt, chvilu sa pocka
}
return 0;
}
#ifndef uart_h_
#define uart_h_
void uart_init( void );
void uart_putc( char c );
void uart_puts( const char *s );
char uart_getc( void );
#endif /* UART_H_ */
#include <avr/io.h>
#define F_CPU 16000000UL
#define BAUD 9600
#include <util/setbaud.h>
void uart_init( void )
{
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= _BV(U2X0);
#else
UCSR0A &= ~(_BV(U2X0));
#endif
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */
}
void uart_putc(char c)
{
if (c == '\n')
{
uart_putc('\r');
}
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
UDR0 = c;
}
void uart_puts(const char *s)
{
while (*s != '\0')
{
uart_putc(*s);
s++;
}
}
char uart_getc(void) {
loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
return UDR0;
}
Zdrojový kód: zdrojaky_Federmayer.zip
Overenie
Funkčnosť zariadenia bola overená praktickým meraním vzdialenosti. Po zapojení senzora k doske bol program nahratý do mikrokontroléra ATmega328P. Následne bol v počítači otvorený sériový terminál PuTTY, v ktorom sa zobrazovali namerané hodnoty vzdialenosti v cm.
Overenie teda prebiehalo tak, že pred senzor bola umiestnená nejaká prekážka v rôznych vzdialenostiach. Pri zmene polohy prekážky sa menila aj hodnota vypisovaná v termináli. Tým sa overilo, že senzor správne reaguje na zmenu vzdialenosti a že program správne spracúva dĺžku impulzu ECHO. V prípade, že sa odrazený impulz nevrátil (napr. preto, že pred senzorom nebola prekážka), program vypísal chybové hlásenie.

Video:
Čo by som urobil inak
Pri riešení projektu som postupoval tak, že som sa najskôr sústredil na základnú funkčnosť senzoru. Dôležité bolo hlavne správne zapojiť senzor, spustiť meranie a overiť, že sa nameraná vzdialenosť správne zobrazuje cez sériovú linku. Tento postup sa mi osvedčil, pretože pri prípadnej chybe bolo jednoduchšie zistiť, či problém vznikol v zapojení, v meraní impulzu alebo vo výpise do terminálu.
Do budúcna by bolo možné projekt rozšíriť o zobrazovanie vzdialenosti na LCD displeji, čo som mal aj pôvodne v pláne. Avšak pri tomto riešení vznikol problém v tom, že displej, ktorý som mal k dispozícii doma, nebol rovnaký ako displej používaný na cvičeniach. Z toho dôvodu by bolo potrebné použiť inú knižnicu. Keďže používanie iných, cudzích knižníc je v rámci zadania zakázané, zvolil som riešenie so zobrazením výsledku cez sériovú linku.