Przez te „wszystkie lata” i kilka projektów, z którymi miałem do czynienia, zarówno w Bascom AVR jak i Bascom 8051 udało mi się utworzyć bardzo optymalny kod dotyczący obsługi tego sensora temperatury i niezbędnych obliczeń. Mam na myśli kod najbardziej „oszczędzający” zasoby µC jaki udało mi się do tego celu stworzyć. Tak więc ten temat będzie o DS18B20 Bascom. Zawierać będzie przykładowe kody i procedury które popełniłem do obsługi tego termometru cyfrowego – oraz podobnych być może po kosmetycznych zmianach.
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
$regfile = "89c4051.dat" $crystal = 11059000 '$large 'AD 2014-02-17 start projektu - program 'sterownik wytrawiarki, sterowanie grzałką i bąblami 'P3.3 sterowanie grzałką ON - 0 / OFF - 1 'P1.1 sterowanie Bąble ON - 0 / OFF -1 ' Config Lcd = 16 * 2 Config Lcdpin = Pin , Db4 = P1.4 , Db5 = P1.5 , Db6 = P1.6 , Db7 = P1.7 , E = P1.3 , Rs = P1.2 Config 1wire = P3.5 Deflcdchar 0 , 8 , 20 , 8 , 3 , 4 , 4 , 4 , 3 ' stopnie Celsjusza na jednym znaku 'Deflcdchar 1 , 6 , 9 , 9 , 6 , 12 , 18 , 18 , 12 ' bąbelki Deflcdchar 1 , 2 , 5 , 2 , 8 , 20 , 9 , 2 , 1 ' replace ? with number (0-7) Deflcdchar 2 , 32 , 32 , 8 , 20 , 8 , 2 , 21 , 2 ' replace ? with number (0-7) Const Licznik = 56373 '60981 -5ms '56373 Config 1wire = P3.5 Config Timer1 = Timer , Gate = Internal , Mode = 1 'dla przerwania co 10ms wpisać do rejestru 56373, dla przerwania co 5ms wpisać 60981 On Timer1 Przerwanie_t_1 Load Timer1 , Licznik 'bez tej instrukcji timer nie startuje ... Enable Interrupts Enable Timer1 Start Timer1 Dim Pomiar_odczyt As Bit , Temperatura As Byte , Temp_d As Byte , T_ds As Integer , Minus As Bit Dim Tg As Byte , Praca As Byte , Zmiana As Bit , Grzalka_on As Bit , Tryb_bombli As Byte , Pwm As Byte , Stoper As Bit Dim 10ms As Byte , 100ms As Byte , Sekundy As Byte , Minuty As Byte , 20ms As Byte , Co100ms As Bit , 05sek As Bit , Co20ms As Bit , Czas_bombli As Byte '( Praca - Steruje Zadaniami Wykonywanymi Przez Sterownik 0 - Wyłączone Grzanie I Dmuchanie I Podświetlenie Lcd Załączone , świeci Lekko 1 - Nagrzewa Do Tg I Pilnuje , Bąble Cykliczne Lub Ciągłe , Odmierzanie Czasu Start 2 - 3 - Grzania Wystarczy - Auto Off Guzikologia Guzik1 Załącza Sterownik Lub Wyłącza - Na przemian Uaktywnia Tryb 1 Lub 0 Pracy , Grzałka On / Off , Lcd świeci Mocno , Bąble On / Off , Licznik Czasu On / Off Guzik0 Przełącza Tryb Bąbli Gdy Praca 1 , A Gdy Praca 0 Kasuje Licznik Czasu ') Guzik0 Alias P3.0 Guzik1 Alias P3.1 Bip Alias P3.2 'reset załącza sygnał Bomble Alias P3.4 'reset załącza Grzalka Alias P3.3 'reset załącza Lcd_led Alias P3.7 ' 'set załącza Test_led Alias P1.0 Test_led1 Alias P1.1 Set Zmiana ' inicjacja programowa nastaw Tg = 42 '"zaprogramowana" temperatura grzania Const Czas_procesu = 40 'maksymalny czas w minutach (trwania procesu) działania grzałki i bąbli itd Const Czas_bombli_nastawa = 5 ' czas automatycznych przełączeń w sekundach +1 '******************************************************************************* Cursor Off Cls Lcd "(C)2014" Reset Bip ' piszczymy Wait 1 Set Bip Cls Do If 05sek = 0 Then 'co pół sekundy obsługa sensora DS i wyświetlanie Set 05sek ' Test_led1 = Not Test_led1 Gosub Ds18b20 Pomiar_odczyt = Not Pomiar_odczyt Locate 1 , 1 If Minuty < 10 Then Lcd "0" Lcd Minuty ; ":" If Sekundy < 10 Then Lcd "0" Lcd Sekundy ; " " Locate 1 , 7 Lcd Chr(1) ; Chr(2) Lowerline Lcd Temperatura ; "," ; Temp_d ; Chr(0) ; " " Locate 2 , 7 Select Case Tryb_bombli Case 0: Lcd "Of" Case 1: Lcd "Au" Case 2: Lcd "On" End Select End If ' ooooooooooooooooooooooooooooooooooooo . . . ' ooooooooooooooooooooooooooooooooooooo If 20ms > 10 Then ' skan 10x na sekundę - podstawa 10ms i zlicza do 10 20ms = 0 Set Co20ms End If If Sekundy = 60 Then Sekundy = 0 Incr Minuty If Minuty = 60 Then Minuty = 0 End If ' If Co100ms = 1 Then ' Reset Co100ms ' End If Loop End '**************************** Przerwanie Timer1 - 10 ms**************************** Przerwanie_t_1: Counter1 = Licznik Start Timer1 Incr 10ms Incr 20ms If 10ms = 0 Or 10ms = 25 Then Reset 05sek End If If 10ms = 50 Or 10ms = 75 Then Reset 05sek End If If 10ms > Pwm Then Set Lcd_led Else Reset Lcd_led 'programowy "pwm" do jasności podświetlenia End If If 10ms = 100 Then 10ms = 0 If Stoper = 1 Then Incr Sekundy Incr Czas_bombli End If Return <strong>Ds18b20:</strong> 1wreset 1wwrite &HCC 'opuść sprawdzanie ID czujnika - skip ROM If Pomiar_odczyt = 0 Then 'test zmiennej decydującej o poleceniu do wykonania 1wwrite &H44 'rozkaz konwersji temperatury Else ' If Err = 0 Then 'gdy komunikacja zakończyła się sukcesem 1wwrite &HBE 'rozkaz odczytania z czujnika wartości T_ds = 1wread(2) Minus = T_ds.15 If Minus = 1 Then 'obliczenia wynikające z kodu U2 T_ds = Not T_ds ' lepsze od T_ds = Abs(t_ds) T_ds = T_ds + 1 End If T_ds = T_ds * 10 'temperatura *10, aby uzyskać wartość dziesiętną w liczbie całkowitej - pozbywamy się typu zmiennoprzecinkowego Shift T_ds , Right , 4 ' niezbędne dzielenie Temp_d = T_ds Mod 10 ' przepisanie wartości dziesiętnej do osobnej zmiennej Temperatura = T_ds / 10 ' przepisanie wartości całkowitych ' End If End If Return |
Przedstawiony wycinek programu obrazuje jak sobie praktycznie radzę w Bascom 8051 z tematem cyklicznego pomiaru i odczytu temperatury z sensora DS18B20, bez jego adresowania. Zmienna sterująca pomiar_odczyt cyklicznie, w miarę upływu czasu zmienia swoją wartość i wpływa na wykonywane procedury przez scalony sensor. W tym akurat przypadku nie potrzebowałem obsługi wartości ujemnych, lecz kod zawiera ich detekcję. Poszczególne instrukcje i obliczenia zostały przeze mnie tak dobrane aby podczas kompilacji powstawał możliwie mały kod wynikowy. Dla objaśnień kod zawiera obfite opisy.
Przykład kodu wywoływanego co stały okres czasu,
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 |
Termometr: 1wreset 1wwrite &HCC If Praca_odczyt = 0 Then 1wwrite &H44 Else 1wwrite &HBE Temp = 1wread(2) Minus = Temp.15 ' 1-ujemna, 0-dodatnia If Minus = 1 Then 'dla temperatury ujemnej obliczenia extra Temp = Not Temp 'lepsze od -> Temp = Abs(temp) Incr Temp End If ' Temp = Temp * 10 ' wyłaczone części dziesiętne Temp = Temp / 16 End If If Praca_odczyt = 0 Then Praca_odczyt = 1 Else Praca_odczyt = 0 End If Return |
oraz niezbędne deklaracje
1 2 |
Dim Temp As Integer , Praca_odczyt As Byte Dim Minus As Bit |
W tym przypadku instrukcja Shift Temp , Right , 4 generowała więcej kodu wynikowego niż instrukcja dzielenia. tak więc walcząc o każdy bajt w pamięci flash zdecydowałem się na takie rozwiązanie ;-P
Teraz z „innej beczki” 😉 dla wygody kiedyś przygotowałem sobie procedurę do obsługi kilku sensorów (DS18B20 Bascom AVR) podłączanych do osobnych wyprowadzeń MCU. Jakoś nie lubię stosować adresowania sensorów z uwagi na dużą ilość przetwarzanych danych. Nie przypadła mi też do gustu możliwość skanowania magistrali 1wire, i obsługa sensorów w ten sposób, z uwagi na przypadkowe „zamieszanie” mogące powstać w wyniku rozłączenia lub uszkodzenia jednego z nich. Więc kosztem dodatkowych wyprowadzeń stosuję poniższe rozwiązanie, i jemu podobne.
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 |
Sub Termometr(byval Nr_ds As Byte , Byval Praca_odczyt As Byte) 1wreset Pinb , Nr_ds 1wwrite &HCC , 1 , Pinb , Nr_ds If Praca_odczyt = 0 Then 1wwrite &H44 , 1 , Pinb , Nr_ds 'DS-y przeliczają Else 1wwrite &HBE , 1 , Pinb , Nr_ds Temp(nr_ds) = 1wread(2 , Pinb , Nr_ds) Minus = Temp(nr_ds).15 ' 1-ujemna, 0-dodatnia If Minus = 1 Then Temp(nr_ds) = Not Temp(nr_ds) Incr Temp(nr_ds) End If Temp(nr_ds) = Temp(nr_ds) * 10 ' wyłaczone części dziesiętne Shift Temp(nr_ds) , Right , 4 Temp_u = Temp(nr_ds) Mod 10 Temp(nr_ds) = Temp(nr_ds) / 10 End If Temp_str(nr_ds) = Str(temp(nr_ds)) + "." + Str(temp_u) If Minus = 1 Then Temp_str(nr_ds) = "-" + Temp_str(nr_ds) Temp_str(nr_ds) = Temp_str(nr_ds) + " " End Sub |
Niezbędne zmienne globalne do prawidłowej pracy przedstawiam poniżej.
1 2 |
Dim Temp(4) As Integer , Temp_u As Word , Minus As Byte , Bajty(9) As Byte ', Nr_ds = Byte ,Praca_odczyt As Byte , Dim Temp_str(4) As String * 6 , Termo As Byte |
Jak inicjować działanie procedury?
1 |
Call Termometr(1 , 0) 'który , co_robić |
Dla wyjaśnienia: pierwszy parametr decyduje o numerze pina w porcie do którego podłączono sensor oraz o pozycji (indeksie) w tablicy. W związku z tym nie może przyjmować wartości zero. Dodatkowo ze względu na rozległość tablicy najlepiej aby obsadzone były kolejne piny portu. Rozmiar tablicy warto dostosować do ilości używanych scalonych termometrów DS. Parametr drugi określa czy wysłany będzie rozkaz konwersji i uruchomienia wewnętrznego przetwornika, czy dane będą sczytane i przetworzone na potrzeby programu.
Poniżej kawałek kodu w postaci procedury do zmiany rozdzielczości dla DS18B20 i Bascom AVR.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Sub Ds18b20eeprom_write(byval Temp_hi As Byte , Byval Temp_lo As Byte , Byval Cfg_val As Byte) 1wreset 1wwrite &HCC 1wwrite &H4E 1wwrite Temp_hi 1wwrite Temp_lo 1wwrite Cfg_val 1wreset 1wwrite &HCC 1wwrite &H48 ' Waitms 15 1wreset End Sub |
Wartości Temp_hi, i Temp_lo, dla wspomnianej konfiguracji nie mają znaczenia, natomiast wartość Cfg_val trzeba odpowiednio dostosować dla uzyskania spodziewanego działania. Ja z zasady najpierw odczytuję bajt konfiguracyjny, a potem modyfikuję bity konfiguracyjne rozdzielczości. Po czym zapisuję tak zmodyfikowany bajt do układu scalonego Warto jeszcze wspomnieć, iż kiedy nie używamy funkcjonalności alarmu dwa bajty Temp_hi, i Temp_lo z rejestru układu DS można użytkować jako pamięć ogólnego przeznaczenia.
Dla przypomnienia przytaczam najistotniejsze dane z mojego opisu DS18B20.
Po podłączeniu zasilania IC przechodzi w stan bezczynności o obniżonym poborze mocy, a domyślną wartością rozdzielczości (zdefiniowaną przez producenta) jest 12 bitów. W rejestrach ustawiona jest wtedy temperatura +85°C. Aby zainicjować pomiar temperatury i przeprowadzenie konwersji A-to-D, urządzenie master musi wydać polecenie Convert T [44h]. Po zakończeniu konwersji uzyskane dane termiczne są przechowywane w rejestrze temperatury 2-bajty pamięci ScratchPad i DS18B20 wraca do stanu nieaktywnego. Dane określające temperaturę wyrażone są w stopniach Celsjusza. Przechowywane są w rejestrze temperatury jako 16 bitowa liczba z rozszerzonym kodowaniem znaku w formacie uzupełnień do dwóch.
Bity, znaku (S) wskazują, czy temperatura jest dodatnia czy ujemna. Dla dodatnich liczb S = 0 a dla liczb ujemnych S = 1. Jeśli DS18B20 jest skonfigurowany dla rozdzielczości 12-bitowej, wszystkie bity w rejestrze temperatury będą zawierać ważne dane. Dla rozdzielczości 11-bitowej, bit 0 jest niezdefiniowany, dla 10-bitowej bity 1 i 0, a dla rozdzielczości 9-bitowej bity 2, 1 i 0 są nieokreślone.
Każde urządzenie posiada unikalny 64 – bitowy kod seryjny przechowywany w wewnętrznej pamięci ROM.
MEMORY
Organizacja pamięci DS18B20:
CONFIGURATION REGISTER
Bajt 4 pamięci ScratchPad zawiera rejestr konfiguracji. Użytkownik może ustawić rozdzielczość wyniku konwersji temperatury DS18B20 wykorzystując w tym celu bity R0 i R1 w tym rejestrze… Domyślna wartość (default) tych bitów po załączeniu zasilania to R0 = 1, a R1 = 1 (rozdzielczość 12 bitów).
Należy pamiętać, że istnieje bezpośredni kompromis między rozdzielczością i czasem konwersji (zależy on od ustawionej precyzji).
Sekwencja transakcji dostępu do DS18B20 jest następująca:
Krok 1. Inicjowanie magistrali,
Krok 2. ROM Command (wymagane dla każdej kolejnej wymiany danych),
Krok 3. DS18B20 Function Command (wymagane dla każdej kolejnej wymiany danych).
Bardzo ważne jest, aby postępować zgodnie sekwencją dla każdego dostępu do DS18B20. Układ DS18B20 nie odpowiada, jeśli jakiekolwiek kroku w sekwencji brakuje lub krok jest poza kolejnością.
Polecenia ROM DS18B20
READ ROM [33h]
MATCH ROM [55h]
SKIP ROM [CCh]
ALARM SEARCH [ECh]
Polecenia FUNKCYJNE DS18B20
CONVERT T [44h]
WRITE SCRATCHPAD [4Eh]
READ SCRATCHPAD [BEh]
COPY SCRATCHPAD [48h]
RECALL E2 [B8h]
READ POWER SUPPLY [B4h]
Trochę o sprzęcie …
Wielkiej filozofii tutaj nie ma bo sensor posiada tylko trzy piny 😉 GND, DQ, VDD. Do pełni szczęścia wymagany jest rezystor pull-up podłączony do linii danych DQ, o wartości 4,7kΩ. Napięcie zasilania powinno zawierać się w zakresie 3.0V do 5.5V. Wygodną alternatywą może być zasilanie pasożytnicze gdzie IC „kradnie” moc z magistrali 1-Wire. W takim wypadku pin VDD należy podłączyć do sygnału GND. Tryb ten nie jest zalecany do pomiaru temperatur wyższych od 100°C. Maksymalny prąd pobierany przez sensor w trybie standby może wynieść 1000 nA (typowo 750nA). Natomiast w 125 °C prąd czuwania zazwyczaj wynosi 3μA. Najwyższy pobór mocy może wystąpić podczas przepisywania wartości z EEPROM do ScratchPad lub podczas wykonywania konwersji temperatury i nie powinien przekroczyć 1,5 mA.
A.D. 2015-09-19 komunikacja z sensorem – nowe spojrzenie …
Idąc w kierunku dalszej głębszej optymalizacji kodu, i zaglądając na ciąg instrukcji niezbędnych do odczytania temperatury z sensora, z innego kierunku, można całą procedurę przeprowadzić jeszcze lepiej. 😉
Do tej pory preferowałem drogę postępowania następującą:
kod wyposażony był w zmienną sterującą na przemiennym wysyłaniem rozkazu konwersji, lub ciągu instrukcji do odczytania danych.
Od tej pory zmienna ta niekoniecznie będzie potrzebna w większości prostych zastosowań. Sprawę można załatwić w sposób następujący:
przy starcie programu wysłanie rozkazu konwersji, co zadany interwał czasu (jak do tej pory) odczyt danych a po jego zakończeniu od razu rozkaz konwersji. Tym sposobem zbędna staje się zmienna sterująca i odczyty przebiegną dwa razy sprawniej 😛
Prezentacja układu z pomiarem temperatury za pośrednictwem przedstawionej metody.
Zestawienie aktualnie produkowanych termometrów DS* na witrynie producenta.
opis DS1822
opis DS1821
opis DS18B20
opis DS18S20
opis DS18S20-PAR
Czy aby ten algorytm nie obcina 0,1°C w dół?
Witam Łukaszu.
Bardzo ciekawa uwaga. Chętnie poznam jej podłoże.