Poznajemy ultradźwiękowy czujnik odległości – układ HC-SR04. Emituje on fale dźwiękowe o częstotliwości 40 kHz (czyli spoza zakresu słyszalności człowieka) i czeka na jej powrót. Mierząc czas nadejścia tej informacji (powrotnej) znamy odległość do obiektu, od którego odbiła się fala dźwiękowa (a właściwie 2x odległość, bo fala pokonała drogę w jedną stronę i z powrotem).
Działanie układu odbywa się zgodnie z następującym protokołem (schematem):
- włączamy TRIG (stan wysoki =5V) na 10 us aby uruchomić procedurę wyemitowania dźwięku (8 impulsów 40 kHz);
- wyłączamy TRIG (stan niski =0V), pin ECHO przechodzi w stan wysoki i zmieni go na niski, gdy sygnał wróci (a jeśli nie wróci, ECHO zmieni się po czasie 38 ms);
- mierzymy czas trwania sygnału wysokiego na pinie ECHO – znając ten czas i zakładając prędkość dźwięku 340 m/s mamy już odległość do obiektu.
Pierwszy programik wykorzystuje funkcję pulseIn() w celu określenia długości trwania sygnału HIGH na pinie ECHO.
#define TRIG 8
#define ECHO 7
unsigned int t2;
void setup() {
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
Serial.begin(9600);
}
void loop() {
digitalWrite(TRIG, 0);
delayMicroseconds(5);
digitalWrite(TRIG, 1);
delayMicroseconds(10);
digitalWrite(TRIG, 0);
//z wykorzystaniem funkcji pulseIn
t2=pulseIn(ECHO, HIGH);
Serial.print(t2);
Serial.print(", ");
Serial.print(t2/58.0);
Serial.println(" [cm]");
delay(100);
}
Nie jestem wielkim zwolennikiem funkcji pulseIn() – choć działa bardzo dobrze. Do odczytu stanu pinów służy funkcja digitalRead(), którą trzeba tylko odpowiednio „zapętlić” — poniższy kod właśnie to realizuje. Wykorzystujemy też funkcję mierzenia czasu działania Arduino w mikrosekundach (czyli w 1/1000 milisekund!) czyli funkcji micros(). Oto i kod:
#define TRIG 8
#define ECHO 7
unsigned int t1,t2;
byte aaa;
void setup() {
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
Serial.begin(9600);
}
void loop() {
digitalWrite(TRIG, 0);
delayMicroseconds(5);
digitalWrite(TRIG, 1);
delayMicroseconds(10);
digitalWrite(TRIG, 0);
//czekanie na przelaczenie pinu ECHO w stan HIGH... wszak nic nie dzieje sie natychmiast!
while (digitalRead(ECHO) == LOW);
t1=micros();
while ((aaa=digitalRead(ECHO)) == HIGH);// Serial.println(aaa);
t2=micros()-t1;
Serial.print(t2);
Serial.print(", ");
Serial.print(t2/58.0);
Serial.println(" [cm]");
delay(100);
}
Dwie rzeczy wymagają wyjaśnienia: 1) Pętle while kończą się średnikiem – czyli pętla nie wykonuje nic! od razu wraca do sprawdzenia warunku kontynuowania pętli – tak szybko, jak się da; nie ma strat czasu na wypisywanie rzeczy, na jakieś obliczenia…. 2) czekamy na ustawienie pinu ECHO w stan HIGH tak długo, jak czujnik SR_HC04 to zrobi (bo choć protokół o tym właśnie mówi, to nic nie dzieje się natychmiast – nawet światło ma skończoną prędkość); dlatego pojawiła się pętla trwająca tak długo, aż stan pinu ECHO jest LOW. Po tej pętli mamy pewność, że ECHO jest już HIGH i uruchamiamy „stoper” (zapisujemy aktualną wartość mikrosekund) do zmiennej t1. Teraz ponownie zapętlamy program aż stan pinu ECHO zmieni się na LOW – to sygnalizuje powrót sygnału do układu. Ta pętla posługuje się pomocniczą zmienną aaa (ale beznadziejna nazwa) zapisującą stan pinu ECHO – niby niepotrzebnie, ale można odkomentować następną linię i będziemy widzieć jej wartość. To kolejna mała rzecz wymagająca komentarza. Po tej pętli ponownie zapisujemy „stoper” – czyli aktualną wartość mikrosekund, tworząc zmienną t2 równą różnicy zatrzymanego czasu i wartości t1… Zmienna t2 ma teraz dokładnie takie samo znaczenie jak w pierwszym przykładzie (programie) i dalsza część pozostaje niezmieniona.
Które rozwiązanie lepsze? W przypadku jednego czujnika pewnie pierwsze jest fajniejsze, ale gdy mamy więcej czujek działających jednocześnie? Wówczas rozwiązanie z pulseIn() nie zagra, bo zatrzyma wykonanie programu i nie pozwoli na wykrycie sygnału wracającego do innego czujnika – czyli popsuje wyniki dla drugiego, trzeciego czujnika. Drugie rozwiązanie można łatwo rozbudować do współpracy z wieloma czujnikami (np. wprowadzając zmienne t2, t3, t4… ) mierzące czas powrotu sygnału z kolejnych czujek. Ale nie upieram się nad wyższością jednego nad drugim. Mi bardziej pasuje funkcja digitalRead() i dlatego wolę drugi programik. Warto na koniec wspomnieć, że jest dedykowana biblioteka do obsługi układu SR-HC04 (gdzie? poszukaj!) i wiem, że świetnie nadaje się do obsługi wielu czujników.
(c) K.G. 2021