Operácie

Ultrazvukový radar s HC-SR04: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
StudentMIPS (diskusia | príspevky)
Riadok 74: Riadok 74:


Zdroják + moje poznámky sú uvedené nižšie, spolu s knižnicami uart.h a uart.c.
Zdroják + moje poznámky sú uvedené nižšie, spolu s knižnicami uart.h a uart.c.


<tabs>
<tabs>

Verzia z 07:40, 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.

Bloková schéma zariadenia na meranie vzdialenosti pomocou ultrazvukového senzora HC-SR04.


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.

Celkový pohľad na zariadenie.

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.

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.

#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;
}

Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x zdrojaky.zip:

Zdrojový kód: zdrojaky.zip

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. Na konci uvádzame fotku hotového zariadenia.

Aplikácia.

Video:

Č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.


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