Skocz do zawartości

[C][Atmega8] Kilka pytań dotyczących HC-SR04


Pomocna odpowiedź

Napisano

Witam wszystkich,
otóż męczę się od wczoraj z poprawnym zaprogramowaniem czujnika ultradźwiękowego HC-SR04. Przeczytałem już multum różnych poradników na ten temat, ale wciąż nie jestem pewien jak sobie z tym poradzić. Dlatego też mam parę pytań (jeśli jakieś pytanie jest głupie to mi wybaczcie, jestem amebą intelektualną robotyki):

1. Jaki timer powinienem podłączyć do czujnika i dlaczego? (w Atmega8)

2. Jak powinny być na początku ustawione piny w Echo i Trig? (w sensie DDRx i PORTx na zero czy na jeden)

3. Jeśli dobrze zrozumiałem poradniki i dokumentacje to mam na 10us zmienić napięcie Trig na wysokie, a potem z powrotem na niskie. Kiedy fala się odbija to wraca do Echo i on zmienia swoje napięcie na wysokie na czas od zmiany Trig na napięcie niskie do wyłapania fali. Kiedy pin Echo równa się 1 to włączyć timer i wyłączyć go, gdy Echo wróci do stanu 0. Czas, który przed chwilą zliczył timer podzielić przez 58, żeby dystans wyszedł w cm. Czy dobrze to rozumiem? Jeśli nie to proszę o wskazanie błędu w moim myśleniu. (mam nadzieję, że da się zrozumieć co napisałem)

Z góry dzięki za odpowiedzi.

PS: Mam nadzieję, że temat nie łamie regulaminu, ale jeśli tak to proszę o zmianę na adekwatny do zasad forum.

Nie chcę zakładać nowego tematu więc napiszę tutaj (właściwie to jest na temat).

Napisałem sobie kod, który powinien zaświecić diodą, kiedy Echo odbiera sygnał z Trig (żeby przetestować czy w ogóle działa mi ten czujnik). Wydaje mi się, że do tego nie potrzeba timerów, więc napisałem coś takiego:

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define Echo PB0
#define Trig PD0

volatile int dlugosc = 0;
int main(void)
{
DDRD |= (1<<Trig);
DDRB &= ~(1<<Echo);
PORTB &= ~(1<<Echo);


   while(1)
   {
  if(dlugosc == 0)
  {
	  DDRB &= ~((1<<PB7) | (1<<Echo));
	  PORTB &= ~((1<<PB7) | (1<<Echo));
  }
  else
  {
	  DDRB = 0b10000000;
	  PORTB = 0b10000000;
  }

  _delay_ms(1000);
  PORTD |= (1<<Trig);
  _delay_us(10);
  PORTD &= ~(1<<Trig);

  if(!(PINB & (1<<Echo)))
  {
	  if(dlugosc == 0)
	  {
		  dlugosc = 1;
	  }
	  else
	  {
		  dlugosc = 0;
	  }
  }

}
}

Mógły mi ktoś powiedzieć co źle robię? Już od trzech dni próbuję ogarnąć ten czujnik i zero postępu...

ps: led jest podłączony do PB7.

Po pierwsze:

      PORTD |= (1<<Trig);
     _delay_us(10);
     PORTD &= ~(1<<Trig); 

10 us to minimalny czas przez jaki musi być '1' na 'Trig'. Więc jeśli twoja Atmega ma niedokładny zegar (np. wewnętrzny RC) to ten czas może okazać się np. 9.5 us i czujnik nie zadziała. A jeśli masz źle ustawioną częstotliwość w projekcie np. 1 MHz, a w rzeczywistości procek pracuje na 8 MHz to będziesz miał ustawiany pin 'Trig' na tylko 1.25 us. Polecam więc w ten delay wstawić 100 us:

      PORTD |= (1<<Trig);
     _delay_us(100);
     PORTD &= ~(1<<Trig); 

Po drugie:

      if(dlugosc == 0)
     {
         DDRB &= ~((1<<PB7) | (1<<Echo));
         PORTB &= ~((1<<PB7) | (1<<Echo));
     }
     else
     {
         DDRB = 0b10000000;
         PORTB = 0b10000000;
     }

Pin jako wejście lub wyjście definiuje się tylko raz, przed pętlą główną. Pin leda ustawiasz tu jako wejście, a przecież powinno to być wyjście.

Po trzecie:

      PORTD |= (1<<Trig);
     _delay_us(10);
     PORTD &= ~(1<<Trig);

     if(!(PINB & (1<<Echo)))

Oczekujesz tu, że stan pinu 'Echo' zmieni się od razu po wystawieniu jedynki na 'Trig', tak się nie stanie. Lepiej czekać na zmianę stanu pinu 'Echo' przy użyciu instrukcji while:

      while(!(PINB & (1<<Echo)))
  • Lubię! 1

Wracam ponownie,
Otóż już zacząłem pisanie tego czujnika tak, żeby mierzył odległość. Powiem nawet, że jestem już w połowie sukcesu. LCD wyświetla dobre wartości dla odległości od 10 - 29 cm. Teraz wypiszę jakie są problemy:

1. Gdy odległość jest mniejsza od 10 to źle wskazuje wynik np. dla 9 cm pokazuje 90 cm, dla 8 cm pokazuje 80 cm itd.

2. Gdy odległość jest większa od 29 cm, licznik zaczyna wariować.

3. Czasem zdarza się, że licznik zatrzymuje się na jakiejś liczbie i po prostu przestaje działać (trzeba wyłączyć i włączyć).

A tutaj kod:

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define Echo PB0
#define Trig PD0

volatile int dlugosc = 0;
char konwersja[3];
int main(void)
{
DDRD |= (1<<Trig);
DDRB &= ~(1<<Echo);
TCCR1B |= (1 << CS10);
LCD_Initalize();
LCD_Clear();
   while(1)
   {
	 PORTD |= (1<<Trig);
	 _delay_us(500);
	 PORTD &= ~(1<<Trig);

	 while(!(PINB & (1<<Echo)))
	 {
		 TCNT1 = 0;
	 }
	 while(PINB & (1<<Echo))
	 {
		 if(TCNT1 != 0)
		 {
			 dlugosc = TCNT1 * 34 / 1000 / 2;
		 }
	 }
	 sprintf(konwersja, "%i", dlugosc);
	 LCD_GoTo(0, 0);
	 LCD_WriteText(konwersja);
	 LCD_GoTo(5, 0);
	 LCD_WriteText("cm");
}
}

Z góry dzięki za pomocne odpowiedzi.

1. Gdy odległość jest mniejsza od 10 to źle wskazuje wynik np. dla 9 cm pokazuje 90 cm, dla 8 cm pokazuje 80 cm itd.

Mam wrażenie że problem leży tu:

       LCD_GoTo(0, 0);
       LCD_WriteText(konwersja);
       LCD_GoTo(5, 0);
       LCD_WriteText("cm");

Zero zostaje z poprzedniego odczytu, nie czyścisz go w żaden sposób.

  • Lubię! 1
1. Gdy odległość jest mniejsza od 10 to źle wskazuje wynik np. dla 9 cm pokazuje 90 cm, dla 8 cm pokazuje 80 cm itd.

Mam wrażenie że problem leży tu:

       LCD_GoTo(0, 0);
       LCD_WriteText(konwersja);
       LCD_GoTo(5, 0);
       LCD_WriteText("cm");

Zero zostaje z poprzedniego odczytu, nie czyścisz go w żaden sposób.

Tak, to było problemem! Dzięki wielkie.

PS: Przy okazji zauważyłem, że nie wyświetla się w ogóle 7 cm. (Edit: oprócz tego jeszcze 14 i chyba 21, ale domyślam się, że to przez ten wzór na dystans).

PS2: Gdy odległość jest większa od 34 to zaczyna mi naliczać jakby od nowa odległość tzn. zamiast 35cm pokazuje mi 1, zamiast 36cm pokazuje mi 2 itd.

PS: Przy okazji zauważyłem, że nie wyświetla się w ogóle 7 cm. (Edit: oprócz tego jeszcze 14 i chyba 21, ale domyślam się, że to przez ten wzór na dystans).

PS2: Gdy odległość jest większa od 34 to zaczyna mi naliczać jakby od nowa odległość tzn. zamiast 35cm pokazuje mi 1, zamiast 36cm pokazuje mi 2 itd.

Tak, to przez ten wzór.

dlugosc = TCNT1 * 34 / 1000 / 2;

TCNT1 jest typu 'uint16_t', czyli "TCNT1 * 34" też musi się mieścić w tym typie (maksimum 65535), co daje maksymalną dlugosc: 65535 / 2000 = 32.7675. Aby tego uniknąć należy rzutować na typ o większym zakresie. Na przykład tak:

dlugosc = TCNT1 * 34UL / 2000UL;
PS: Przy okazji zauważyłem, że nie wyświetla się w ogóle 7 cm. (Edit: oprócz tego jeszcze 14 i chyba 21, ale domyślam się, że to przez ten wzór na dystans).

PS2: Gdy odległość jest większa od 34 to zaczyna mi naliczać jakby od nowa odległość tzn. zamiast 35cm pokazuje mi 1, zamiast 36cm pokazuje mi 2 itd.

Tak, to przez ten wzór.

dlugosc = TCNT1 * 34 / 1000 / 2;

TCNT1 jest typu 'uint16_t', czyli "TCNT1 * 34" też musi się mieścić w tym typie (maksimum 65535), co daje maksymalną dlugosc: 65535 / 2000 = 32.7675. Aby tego uniknąć należy rzutować na typ o większym zakresie. Na przykład tak:

dlugosc = TCNT1 * 34UL / 2000UL;

Niestety to nie pomogło.

  • 3 tygodnie później...

Juz spiesze z pomoca, w moim przypadku czujnik zaczynal wariowac a procek sie zawieszac kiedy zasilanie i GND HC-SR04 bylo podlaczone blisko zasilania i GND procka.

Zaczalem wiec odsuwac czujnik od mikroprocka i bylo coraz lepiej ale dopiero zawieszenia procka ustaly gdy zaczalem zasilac czujnik napieciem sprzed stabilizatora a gnd bylo tuz obok gnd od baterii.

Druga smocza zasada jest to ze wszystkie delaye w przypadku pomiarow tak krotkich sygnalow bez udzialu przetwornika ADC poprostu zabijaja cala konstrukcje, musisz je ograniczyc do minimum, a w szczegolnosci ten wyzwalajacy pomiar to juz MAKSYMALNIE 15us, sproboj tez taktowac procesor jak najwieksza czestotliwoscia zeby zadne stany ci nie umknely, u mnie jest to 8 MHZ.

A no i oczywiscie najlepiej to by bylo generowac przerwania od stanu wysokiego/niskiego na pinie ECHO.

W zalaczniku dorzucam ci moj kod uzbrojony w pomiar medianowy czyli skladajacy sie z kilku pomiarow i sredniej z nich wyciagnietej.

Oczywiscie kod moznaby usprawnic dodaniem jeszcze jednego timera ktory by odliczal superokladnie czas sygnalu ECHO, ale w sumie to co jest daje wystarczajace rezultaty.

W zalaczniku masz kompletny kod (bez biblioteki HD44780)

W algorytmie znajduje sie rowniez prototyp funkcji ktora w przypadku wystapienia w krotkiej chwili 1 do 2 bardzo rozniacych sie pomiarow odrzuca je i kaze wykonac te pomiary od nowa az do skutku, jednak jest niedopracowana i w sumie pomiar medianowy dal juz wystarczajace wyniki.

Pozostaje rowniez kwestia tego ze ten czujnik nie jest idealny, przy malych przedmiotach i rownoczesnej odleglosci >50 cm wystarczy niewielki kat odchylenia przedmiotu (tak ze fala ultradzwiekowa nie odbija sie od prostopadlej przeszkody) i masz po pomiarze i tego sie juz nie da raczej naprawic.

Ew rozwiazaniem byloby zastosowanie 2 takich czujnikow w kooperacji i dzialalyby one jak uklad stereofoniczny, jednak to juz wyzsza szkola jazdy a moj projekt mial slozyc jedynie do nauki i nic wiecej.

addons.h

addons.c

serwo+odleglosc.c

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...