Jak zaprogramować czujnik temperatury DS18B20 na AVR ATTINY

Zaprogramowaliśmy już kilka ciekawych elementów, między innymi wysłaliśmy sygnał za pomocą gotowej biblioteki do wyświetlacza tak żeby coś wyświetlał. Napisaliśmy także własną funkcję do tworzenia odpowiedniego sygnału do sterowania serwomechanizmem. Dzisiaj pójdziemy krok dalej i zajmiemy się odbieraniem sygnału z czujnika.

Jeśli chodzi o odbieranie sygnału, wydaje mi się że dobrym przykładem będzie czujnik temperatury. Akurat mam pod ręką czujnik temperatury DALLAS 18B20 1647C4 +300AC jak na załączonym obrazku poniżej. Czujnik ten kosztuje kilka złotych więc podejrzewam że jeśli go kupisz twój budżet wytrzyma.

Jak podłączyć czujnik temperatury 18B20 do AVR ATTINY84A-PU?

Podejmijmy teraz próbę podłączenia naszego czujnika. Najlepiej na początku znaleźć notę katalogową i kilka informacji na temat jego działania. Najważniejsze informacje to:

  • Zakres temperatur -50…125°C 1-Wire
  • Napięcie zasilania: 3…5,5V

Z tego co widać powyżej nie powinno być problemu jeśli zasilimy sobie układ z programatora co jest najwygodniejsze do testów. Później można to na przykład podpiąć do dwóch albo trzech baterii paluszków 1,5V połączonych szeregowo, co powinno dać napięcie 3 lub 4,5V co mieści się w zakresie.

Jeśli udało się Tobie znaleźć kartę katalogową to już na pierwszej stronie możesz znaleźć opis poszczególnych nóżek. Dość jasno z niego wynika, że do pierwszej nóżki podciągamy GND do trzeciej VCC czyli plus a do środkowej podepniemy jeden z PINów naszego mikrokontrolera. Jeśli jeszcze masz problem z określeniem która nóżka jest która zauważ że obudowa jest zaokrąglona z jednej strony. Odwróć widok tak żeby płaska strona była na górze i już wszystko powinno być jasne.

Tylko pamiętaj że na poniższym schemacie jest pokazany widok od dołu czyli od tej strony nóżek. Jak go wetkniesz i spojrzysz z góry to będzie odwrotnie (oczywiście mi się udało podłączyć odwrotnie za pierwszym razem i dało się poczuć lekki zapach spalenizny po chwili – nie pomyl się!).

Obrazek posiada pusty atrybut alt; plik o nazwie czujnik_temperatury_schemat.jpg

Jak działa protokół 1-WIRE?

Do przesyłania danych z czujnika temperatury potrzebujemy tylko jednego PINu mikrokontrolera (co więcej możemy do tego jednego PINu podpiąć kilka czujników i będzie to działać). Co ważne tego jednego PINu używamy zarówno do wysyłania jak i odbierania danych.

Za chwilę przystąpimy do analizy, krok po kroku jak odbywa się komunikacja z naszym mikrokontrolerem ale wcześniej kilka słów jak to działa w teorii.

  1. Reset – pierwszą czynnością w komunikacji z czujnikiem temperatury DS18b20 jest wysłanie specjalnego sygnału reset do czujnika. Jest to inicjacja komunikacji. Sygnał powinien się rozpocząć od ustawienia stanu niskiego na ok 480us (mikrosekund). Po tym czasie linia powinna wrócić do stanu wysokiego, po ok 70us sprawdzamy stan linii. Jeśli wszystko jest połączone dobrze to powinien być stan niski. Następnie należy odczekać jeszcze 410 us przed dalszą komunikacją.
  2. Odczyt bitu – Aby odczytać bit należy to zainicjować poprzez ustawienie za pomocą mikrokontrolera stanu niskiego na 6us. Po ok 9us sprawdzamy stan linii i jeśli będzie niski to oznacza 0 a jeśli wysoki 1. Przed następną transmisją trzeba odczekać 55us.
  3. Wysyłanie bitu: Aby wysłać jedynkę należy ustawić stan niski na 6us i później stanu wysokiego na 64us. Aby wysłać zero należy ustawić stan niski na 60us a później stan wysoki na 10us.

Jak zapewne zauważyłeś sekwencje wysłania bitu podobnie jak w przypadku odczytu trwają po 70us.

Jak krok po kroku przetestować komunikację?

Spróbujmy teraz przeanalizować krok po kroku komunikację i odczyt temperatury z naszego czujnika DS18B20. W tym celu wykorzystamy wyświetlacz LCD z Jak zaprogramować wyświetlacz LCD (HD44780) na AVR ATTINY84A oraz czujnik podpięty pod PIN PB1. Zrobię program, który będzie działał w pętli wykonując pomiar co ok 1 sekundę. Mam wyświetlacz na 4 linie ale można zrobić to także na takim na 2.

Na początek w mojej funkcji głównej ustawię sobie licznik, który będzie liczył ilość pomiarów oraz wyświetlę tę informację na moim wyświetlaczu. Dalszy kod testów będę umieszczał zaraz przed odczekaniem sekundy na kolejne wykonanie kodu. Na ten moment moja główna funkcja programu będzie wyglądać jak poniżej (pamiętaj tylko o wykorzystaniu biblioteki do wyświetlacza LCD).

Na początku sprawdźmy jak nasz mikrokontroler widzi stan na linii zanim podejmiemy jakiekolwiek działania.

Na ten moment powinieneś być już pewien że na twojej linii jest wysokie napięcie. Oczywiście o ile wszystko dobrze jest podłączone.

Reset DS18B20 przez 1-WIRE

Teraz sprawdzimy czy nasz czujnik w ogóle będzie odpowiadał na nasz sygnał. W tym celu trzeba będzie wykonać wcześniej opisaną sekwencję resetu. Czyli ustawić stan niski na linii, odczekać 480us, zmienić stan z powrotem na wysoki i poczekać 70 mikrosekund. Po tym czasie sprawdzam tak jak powyżej jaki jest stan. Mogę jeszcze odczekać 410 mikrosekund na kolejną akcję o czym za chwilę.

Dlaczego sygnał trwa 480us a później czekam właśnie 70us? Bo tak jest napisane w większości źródeł, które widziałem. Żeby dokładniej zrozumieć temat myślę że lepiej zajrzeć do dokumentacji DS18B20. To właśnie z niej możesz się dowiedzieć że potrzebny jest sygnał 480us od mikrokontrolera, później czujnik potrzebuje do 60us na odpowiedź i pomiędzy tymi 60us a 240us obniża stan na linii jeśli wszystko działa poprawnie. Czyli równie dobrze odpowiedź można odczytać po 120us lub 200us.

Funkcja reset w oddzielnym pliku

Skoro wiemy już jak działa reset, to nadszedł czas aby wyodrębnić funkcję reset, która będzie nam zwracała informacja czy reset jest ok. Czyli jeśli mamy stan niski to jest ok i będziemy mogli wykonywać kolejne działania. Dalej będzie trochę trudniej.

Zacznijmy od stworzenia nowego pliku do obsługi komunikacji 1-wire. Nazwijmy plik ONE_WIRE.c i umieścimy go w katalogu głównym projektu (w tym samym co main.c). W pliku tworzymy metodę ONE_WIRE_RESET(), która będzie zwracać 1 przy poprawnej odpowiedzi czujnika i 0 jeśli coś poszło nie tak.

Teraz bardzo ważne aby dołączyć ten plik do naszego pliku głównego. Czyli na górze pliku main.c wstawiamy include „ONE_WIRE.c”. Teraz aby sprawdzić czy reset się powiódł wystarczy użyć tylko.

Jak odczytać adres czujnika DS18B20?

Pierwszą informacją jaką operujemy jest adres czujnika. Jak wcześniej wspominałem do jednej linii możemy podłączyć kilka czujników tego typu i odnosić się do nich po ich adresach. Dzięki temu rozwiązaniu możemy w dowolnym momencie odczytać temperaturę z dowolnego czujnika.

Aby odczytać adres czujnika do linii musi być podpięty tylko jeden czujnik. Teraz będzie trochę trudniej bo musimy najpierw wysłać cały bajt danych będący odpowiednią komendą a później odebrać 8 bajtów adresu. Jak pamiętamy bajt to 8 bitów.

Komendy możemy znaleźć w dokumentacji czujnika tak jak na obrazku poniżej. Jak widać mamy tutaj opisane komendy w dość przystępny sposób jak na dokumentację techniczną oraz mamy ich kody.

Aby odczytać adres czujnika należy wykorzystać komendę Read Rom. Komenda ta ma kod 33h, jest to zapis szesnastkowy i w języku C przekształcamy to na kod 0x33. Bardzo prawdopodobne że poświęcę oddzielny artykuł na kody szesnastkowe, w każdym razie kod ten odpowiada liczbie 51 dziesiętnie.

W skrócie kody te to pary liczb w zapisie szesnastkowym (czyli od 0 do F), każda z tych liczb ma swoją reprezentację na 4 bitach. Na przykład liczba 3 ma kod 0011 binarnie. Aby zamienić to po prostu łączymy kod dwóch trójek czyli mamy 00110011 a zamiana kodu binarnego na dziesiętny to po prostu dodawanie potęg liczby dwa tam gdzie są jedynki jak idziemy od prawej do lewej. Tutaj będzie to 1 + 2 + 0 + 0 + 16 + 32 = 51. To tak bardziej jako ciekawostka bo program sam sobie zamieni kod szesnastkowy na binarny.

Jako że musimy jakoś wysłać komendę do naszego czujnika potrzebujemy odpowiednią metodę będącą w stanie wysłać bajt danych za pomocą komunikacji 1-WIRE. Analogicznie jak wcześniej napiszemy dwie metody ONE_WIRE_SEND_BIT(uint8_t bit) oraz ONE_WIRE_SEND_BYTE(uint8_t data). Do pierwszej funkcji będziemy przekazywać pojedynczy bit i wysyłać go wykorzystując algorytm opisany wcześniej. Do drugiej funkcji przekażemy bajt danych. Obie funkcje umieszczamy w pliku ONE_WIRE.c

Teraz czas na metodę do wysłania całego bajtu. Oczywiście wykorzystamy metodę wyżej do przesyłania kolejnych bitów a więc będzie to prosta funkcja z pętlą.

Tutaj być może trochę trudniejsze do zrozumienia będzie przesunięcie bitu czyli składnia data & (1 << i) o co tutaj chodzi? Wyobraź sobie że data to zmienna przechowująca na ten moment liczbę 0x33 czyli 51, w systemie binarny będzie to po prostu ciąg 0011 0011 czyli nasze 8 bitów komendy. W pętli zmienną i przy każdym przebiegu zwiększamy o 1. Czyli w tym przypadku przy każdym przebiegu pętli będziemy się przesuwać od prawo do lewo odczytując kolejny bit przesuwając się o i miejsc od prawej. Będziemy po kolei wysyłać 1 1 0 0 1 1 0 0.

Po wysłaniu komendy powinniśmy już o otrzymać jakąś odpowiedź od naszego czujnika. Spróbujemy sobie wyświetlić 20 pierwszych bitów adresu na naszym wyświetlaczu. Akurat mój ma 20 znaków w rzędzie więc będzie to stosunkowo proste.

Jeśli otrzymałeś na wyświetlaczu ciąg zer i jedynek to najprawdopodobniej wszystko masz dobrze połączone i napisane i możemy przejść do napisania funkcji do odczytu poszczególnych bitów i bajtów. I jeszcze obrazek jak by to mogło wyglądać.

Zanim ruszymy dalej spróbujmy z powyższego ciągu wyłuskać 2 pierwsze bajty adresu czujnika. Mamy tutaj 20 bitów odczytanych po kolei, czyli 2,5 bajta. Trzeba pamiętać że bity są odczytywane od najmniej znaczącego czyli najpierw dzielimy ciąg na 8 bitowe co da 00010100 11001010 1000 a później odwracamy bity co powinno dać: 00101000 01010011 i mamy 2 liczby w postaci binarnej. Po zamianie na system dziesiętny otrzymamy 8 + 32 = 40 oraz 1 + 2 + 16 + 64 = 83. Dzięki tym obliczeniom będziemy w stanie sprawdzić czy nasza funkcja do odczytywania bajtów będzie działać poprawnie.

To teraz aby było łatwiej spróbujemy napisać metodę ONE_WIRE_READ_BIT(void), za pomocą której odczytamy bit z naszego czujnika. Już wiemy w jaki sposób to zrobić bo napisaliśmy już to wyżej w pętli.

Żeby sprawdzić czy dalej działa w naszej pętli main.c tam gdzie odczytujemy bity można na chwilę wypróbować naszą funkcję.

Teraz jeszcze możemy potrzebować funkcji do odczytania całego bajtu danych. Adres to 8 bajtów czyli 64 bity, spróbujemy sobie to zaraz odczytać w jakiś sposób. Podejmijmy próbę napisania funkcji ONE_WIRE_READ_BYTE(void). Będzie to głównie pętla wykorzystująca funkcję ONE_WIRE_READ_BIT().

Co dzieje się w pętli? Wcześniej zdefiniowaliśmy zmienną byte i ustawiliśmy na zero czyli 00000000 i teraz w pętli po kolei ustawiamy wartości zwrócone przez funkcję ONE_WIRE_READ_BIT(). Jeśli odczytaliśmy 1 to w miejsce i wstawiamy 1 a jeśli 0 to 0 i tak budujemy jakąś liczbę w systemie binarnym. Dzięki temu powinny nam wyjść takie same liczby jak liczyliśmy wyżej. Czyli pierwsze dwie jakie odczytamy to będą 40 oraz 83. W przypadku innego czujnika adres powinien być inny.

Spróbujmy wyświetlić cały adres na ekranie. Odczytam poszczególne bajty, zapiszę do tablicy a potem wyświetlę oddzielając kropką poszczególne bajty adresu. Nie mieściło mi się w jednej linii więc rozbiłem na dwie. Modyfikujemy teraz funkcję główną programu.

I wynik jakby to mogło wyglądać

Wiemy już jak wymieniać informacje z czujnikiem DS18B20, wiemy gdzie w dokumentacji znaleźć komendy i jak się nimi posługiwać. Teraz nadszedł czas na podjęcie próby odczytania temperatury z naszego czunika.

Jak odczytać temperaturę z czujnika DS18B20?

Ogólny algorytm odczytu temperatury polega na zainicjowaniu odczytu, odczekaniu chwilę i odczytaniu pomiaru z pamięci czujnika. Odczytujemy 2 bajty danych, które należy później odpowiednio przeliczyć. Spróbujmy napisać odpowiedni kod.

I na koniec wynik ostateczny z termometra.

To powyżej pokazuje tylko jak odczytać temperaturę z czujnika, kilka eksperymentów krok po kroku. Aby wykorzystać to w jakimś projekcie potrzeba jeszcze trochę pracy żeby ten kod poukładać, nieco zoptymalizować i przydało by się jeszcze trochę uzupełnić temat o odczyt sumy kontrolnej dla weryfikacji wyniku a także rozpracować podłączenie kilku czujników do jednej linii.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *