Zapytaj gdziekolwiek jakiego czujnika temperatury użyć a najczęściej otrzymasz odpowiedź DS18B20. Układ Dallas’a jest strasznie popularny. W sklepach można również nabyć wersję wodoodporną co jeszcze wzmacnia jego popularność. Oczywiste jest to, że nie mogło zabraknąć u mnie tego czujnika.

Komunikacja

Z układem DS18B20 porozmawiać można jedynie poprzez interfejs 1-Wire. Cóż to jest?! Jest to wymysł firmy Dallas Semiconductors która to w późniejszym czasie połączyła się z Maxim’em.  Interfejs ten który jednocześnie jest też protokołem wykorzystuje jedynie jedną linię do komunikacji dwukierunkowej, stąd też jego nazwa.

Nie chciałbym się wdawać w szczegóły tego interfejsu oraz zasadę działania. W skrócie najważniejsze informacje to:

  1. Wszystkie urządzenia podłącza się do jednej magistrali.
  2. Jedynki i zera zdefiniowane są jako impulsy o zdefiniowanych przez interfejs stałych czasowych. Z tego względu też prędkość komunikacji niejako jest niezmienna.
  3. Transmisja jest szeregowa i odbywa się na zasadach master-slave .
  4. Protokół zawiera adresowanie dzięki któremu aktywujemy wybrany czujnik podobnie jak ma to miejsce przy I²C.

Polecam oficjalne dokumentacje Maxima, które tłumaczą szczegóły o wiele lepiej niżeli ja bym to zrobił. Na stronie firmowej jest np. kilka pomocnych artykułów: Link1 Link2

Połączenie

Jak już wcześniej wspomniałem urządzenia do magistrali 1-Wire podłącza się tylko jednym przewodem. Dodając do tego zasilanie, standardowo mamy 3 przewody. Linię danych obowiązkowo należy podciągnąć do linni zasilania przez rezystor. Na ogół jest on o wartości 4k7 ohm.

Hmm powiedziałem 3 przewody standardowo. A nie standardowo? A dwa! Mianowicie interfejs 1-Wire umożliwia komunikacje w tzw. trybie pasożytniczym, gdzie dodatnia linia zasilająca nie jest dostarczana do czujnika. Pin VDD przy układzie Slave zwierany jest wówczas do masy. Skąd w takim razie zasilanie w czujniku? Jak sama nazwa trybu mówi – pasożytuje on na linii danych. W układach slave 1-Wire znajduje się kondensator, który w momencie wystąpienia stanu wysokiego na linni danych, ładuje się i podtrzymuje zasilanie na czas trwania stanu niskiego.

W celach tego wpisu użyłem trybu zwykłego. Do Nucleo F401RE podłączyłem 4 czujniki DS18B20 na pinie PA1.

Programowanie

W internecie można znaleźć mnóstwo przykładów komunikacji 1-Wire dla chyba wszystkich mikrokontrolerów świata. Nie będę czarował, że moja biblioteka jest unikatowa oraz pisana od podstaw. Pisząc ją kiedyś odpierałem się różnymi otwartymi przykładami które są ogólnodostępne. Zrobiłem bibliotekę, która mi pasuje w użytkowaniu. Nie twierdzę, że jest ona najlepsza – jest dobra 🙂

Do działania biblioteki potrzebny jest timer z tickiem 1 µs. Łatwo jest go uzyskać stosując pełne megaherce do taktowania MCU. Wystarczy prescaler ustawić na (taktowanie MCU)-1. Pamiętaj, aby wpisać w CubeMX dużą wartość – np. 65000 – w Counter Period konfiguracji timer’a. 

Biblioteka składa się z dwóch członów. Pierwszym jest obsługa interfejsu 1-Wire. Jest ona bezobsługowa i nie wymaga żadnej konfiguracji w nagłówkach. Drugi człon to oczywiście obsługa DS18B20. Konfiguracja w pliku nagłówkowym obejmuje:

  1. Maksymalną liczbę układów DS18B20 któą można podłączyć do magistrali.
  2. Pin oraz port GPIO wykorzystany do celów 1-Wire.
  3. Nazwę handlera do timera z tickiem 1 µs.
  4. Flagę define, która decydyje o tym, czy korzystać z CRC. Można oszczędzić kilka bajtów czasu przy czytaniu możliwym kosztem poprawności odebranych danych.

Konfiguracja w CubeMX jest banalnie prosta. Wystarczy zdefiniować pin wyjściowy GPIO dla magistrali jako zwykły GPIO Output i skonfigurować wspomniany wcześniej timer. Dodatkowo wrzuciłem sobie znajdującą się na Nucleo LEDkę do migania, UART do printowania temperatur na PC oraz wyjście testowe do podglądania na analizatorze logicznym.

Jak działa mój kod? Informacje o każdym z podłączonych czujników zawiera tablica struktur Ds18b20Sensor_t o ilości elementów podanych w nagłówku jako maksymalna ilość czujnikówZnajdują się tam informacje o pełnym adresie, ostatnio poprawnie odczytanej temperaturze oraz powodzeniu ostatniego odczytu. Dodatkowo powołałem zmienną mówiącą o tym ile zostało wykrytych czujników DS18B20. Może ich być mniej niż definiowany w nagłówku MAX. Zastanawiałem się nad dynamicznym alokowaniem pamięci dla  ilości podłączonych czujników, ale czy to miałoby sens? Na ogół ich ilość nie zmienia się w trakcie życia całego urządzenia, więc można ilość elementów w tablicy ustawić na sztywno.

Dostęp do danych takich jak adres czy tempeartura jest możliwy jedynie poprzez dedykowane funkcje. Nie ma ręcznego grzebania w strukturze. Najważniejsze funkcje:

1
void DS18B20_Init(DS18B20_Resolution_t resolution);

Jest to oczywiście inizjalizacja. Wewnątrz inicjalizacji startowana jest magistrala 1-Wire oraz  są wyszukiwane i liczone podłączone czujniki DS18B20. W argumencie podać należy wymaganą rozdzielczość dla układów. Pamiętaj, że rozdzielczość ma ogromny wpływ na czas konwersji temperatury na postać cyfrową. Dostępne są opcje:

1
2
3
4
DS18B20_Resolution_9bits
DS18B20_Resolution_10bits
DS18B20_Resolution_11bits
DS18B20_Resolution_12bits

 

1
void DS18B20_StartAll(void);

Jest to rozesłanie do wszystkich podłączonych czujników komendy startu konwersji tempeatury. Proste.

 

1
void DS18B20_ReadAll(void);

Funkcja odczytania skonwertowanej temperatury do odpowiednich elementów w tablicy czujników. Każdy czujnik wykryty w funkcji inicjującej jest odczytywany.

 

1
uint8_t DS18B20_Quantity(void);

Zwraca liczbę czujników wykrytych podczas inicjalizacji.

 

1
uint8_t DS18B20_GetTemperature(uint8_t number, float* destination);

Wpisuje wartość temperatury z i-tego czujnika do miejscu w pamięci pod wskaźnikiem destination.

 

1
2
void DS18B20_GetROM(uint8_t number, uint8_t* ROM);
void DS18B20_WriteROM(uint8_t number, uint8_t* ROM);

Operacje na adresach czujników. Można dzięki nim np. zmieniać kolejność czujników w tablicy.

Biorąc to wszystko i wrzucając do jednego garnka otrzymałem przykładowy kod printowania wszystkich czujników na terminal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* USER CODE BEGIN 2 */
DS18B20_Init(DS18B20_Resolution_12bits);
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
DS18B20_ReadAll();
DS18B20_StartAll();
uint8_t ROM_tmp[8];
uint8_t i;
for(i = 0; i < DS18B20_Quantity(); i++)
{
if(DS18B20_GetTemperature(i, &temperature))
{
DS18B20_GetROM(i, ROM_tmp);
memset(message, 0, sizeof(message));
sprintf(message, "%d. ROM: %X%X%X%X%X%X%X%X Temp: %f\n\r",i, ROM_tmp[0], ROM_tmp[1], ROM_tmp[2], ROM_tmp[3], ROM_tmp[4], ROM_tmp[5], ROM_tmp[6], ROM_tmp[7], temperature);
HAL_UART_Transmit(&huart2, (uint8_t*)message, sizeof(message), 100);
}
}
HAL_UART_Transmit(&huart2, (uint8_t*)"\n\r", sizeof("\n\r"), 100);
HAL_Delay(1000);
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
/* USER CODE END 3 */

Uwaga niebezpieczeństwo!!! Kompilator STM32 ma taką cechę, że po wygenerowaniu kodu w CubeMX nie masz od razu możliwości printowania zmiennych typu float! Będą same zera lub w ogóle nic 🙁 Aby było to możliwe trzeba dodać flagę -u _printf_float do linkera.

Teraz, gdy już wszystko jest jasne, zobacz wynik działania tego małego skrawka programu:

Ciepło mam przy biurku, co nie? 🙂

Paaanie a ile to czasu zajmie?

Oczywiście musiałem sprawdzić ile czasu zajmuje transferowanie potrzebnych danych przez 1-Wire!

Sama komunikacja do odczytu temperatury trwa około 14 ms.

Komenda startu konwersji dla wszystkich czujników to ok 2,3 ms.

Więc całość dla jednego czujnika to ok 16,4 ms. Wiedząc, że komenda startu jest wysyłana jedynie raz dla wszystkich czujników, wzór na czas odczytu i startu konwersji dla n czujników to 14 * n + 2,3 [ms]. Sprawdzam.

Dwa czujniki. 14 * 2 + 2,3 = 30,3. Pomiar to 30,43.

Cztery czujniki. 14 * 4 + 2,3 = 58,3. Pomiar 58,45.

Czyli wszystko się zgadza. Czy można na czymś urwać kilka milisekund? Na braku CRC.

11,75 ms dla oczytu pojedynczego czujnika.

Komenda startu konwersji oczywiście bez zmian. Nie ma tu nic związanego z CRC. W przypadku nieużywania CRC wzór na czas obsługi n czujników wynosi 11,75 * n + 2,3 [ms].

Potwierdzenie na czterech czujnikach.

11,75 * 4 + 2,3 = 49,3. Z pomiaru wychodzi 49,05. Zgadza się 🙂

Jednak bądź ostrożny rezygnując z CRC! Może zdażyć się, że odczytasz pierdoły, a teraz nie masz jak te skontrolować.

Czy można więcej urwać? Można, ale nie w DS18B20 🙁 1-Wire bowiem oferuje coś takiego jak tryb Override, który umożliwia przyśpieszenie transmisji około 10-krotnie. Nasz czujnik temperatury nie obsługuje tego trybu więc niestety nie mam co sprawdzać. Generalnie mało jest urządzeń 1-Wire obsługujących przyśpieszoną komunikację.

Podsumowanie

Czujniki temperatury DS18B20 są bardzo przyjemne. Obsługa nie jest nadmiernie skomplikowana, a w internecie istenieją tysiące przykładów jak sobie z nimi poradzić. Dorzucam jeden od siebie mając nadzieję, że jest napisany sensownie. Niewątpliwą zaletą jest możliwość obsługi wielu urządzeń za pośrednictwem tylko jednej linii danych. Świetnie sprawdzi się to przy wielopunktowym pomiarze. Dodatkowo wersje wodoodporne idealnie nadają się do pomiaru temperatury wody np. w akwarium 🙂

Dziękuję Ci za przeczytanie tego wpisu. Jeśli taka tematyka Ci odpowiada, daj mi znać w komentarzu. Będę też wdzięczny za propozycję tematów które chciałbyś abym poruszył.

Kod standardowo dostępny jest na moim GitHubie: link

Jeśli zauważyłeś jakiś błąd, nie zgadzasz się z czymś, chciałbyś coś dodać istotnego lub po prostu uważasz, że chciałbyś podystkutować w tym temacie, napisz komentarz. Pamiętaj, że dyskusja ma być kulturalna i zgodna z zasadami języka polskiego.

Rozstrzygnięcie konkursu

W poprzednim wpisie ogłosiłem mini konkurs. Czas wyłonić zwycięzców. Spośród wszystkich niepowtarzających się po imieniu, IP i mailu oraz spełniających wszystkie wymagania komentarzy wybrałem 10 moim zdaniem najciekawszych zastosowań wyświetlacza.

Wśród nich wylosowałem dwóch zwycięzców według zasad podanych w konkursie. Są to komenmtarze numer 4 i 10 z powyższego screenshot’a. Film z przeprowadzonego losowania dostępny jest pod adresem https://youtu.be/vqYASbZ2Xr0

Gratulacje dla zwycięzców! Napisałem już do Was maila w celu finalizacji konkursu. Pewnie jeszcze nie jeden konkurs pojawi się na blogu. Trochę za dużo szpargałów mam w swojej szafie 😉


Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Serwis wykorzystuje pliki cookies. Korzystając ze strony wyrażasz zgodę na wykorzystywanie plików cookies. więcej informacji

Wrażenie zgody na pliki Cookies jest konieczne, aby uzyskać najlepsze wrażenia z przeglądania strony. Jeżeli nadal nie wyraziłeś zgody na używanie plików Cookies, zaakceptuj poniżej klikając w przycisk "Akceptuj" na banerze.

Close