logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[AVR GCC] Jak obsłużyć temperatury ujemne w czujniku DS18B20 z AVR GCC?

snow 01 Gru 2006 15:08 5771 12
  • #1 3282837
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Witam,

    Mam program do obsługi tego czujnika, lecz działa on jedynie dla temperatur dodatnich a chciałbym także aby mierzone były temperatury ujemne. Dodatnie temperatury wyliczam według wzoru:

    temp = ((lsb & 0x0F) * 0.0625);
    temp +=((lsb >> 4)+(msb << 4));
    temp *= 10;
    temp0 = (unsigned int) temp;
    temp1 = (temp0 / 100);
    temp2 = ((temp0 % 100) / 10);
    temp3 = ((temp0 % 100)%10);


    I wyświetlam na wyświetlaczu LCD.

    Jeżeli ktoś byłby w stanie mi pomóc to prosiłbym o pomoc :)

    Pozdrawiam.
  • #2 3282912
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    snow napisał:
    ...Mam program do obsługi tego czujnika, lecz działa on jedynie dla temperatur dodatnich a chciałbym także aby mierzone były temperatury ujemne....

    Wywal ten "wzór" na śmietnik i zakonotuj sobie w pamięci , że DS18B20 zwraca temperaturę w postaci signed int . Ta informacja powinna Ci wystarczyc , by obliczyć i wyświetlić ją w prawidłowej postaci.

    Piotrek
  • #3 3282959
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Czyli tego przesuwania na bitach mam nie realizować? Jak złożyć msb i lsb w jedną zmienną signed int?
  • #4 3282974
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    snow napisał:
    Czyli tego przesuwania na bitach mam nie realizować? Jak złożyć msb i lsb w jedną zmienną signed int?

    Np. union lub zwykłe mnożenie :D

    Piotrek
  • #5 3283813
    gadula88
    Poziom 15  
    Posty: 152
    Pomógł: 12
    Ocena: 9
    A już chciałem założyć taki sam temat :)
    Czy ktoś mógłby wrzucić przykładowy kod działający też dla temperatur ujemnych? Bo próbowałem coś samu stworzyć, ale nie wychodzi wogóle :|
  • #6 3286677
    Gieno100
    Poziom 10  
    Posty: 55
    Ocena: 5
    Ja rozwiązałem ten problem w następujący sposób:
    
     unsigned short int odczytaj_temp(void)
    
    { 
            unsigned char temp1, temp2; 
    		unsigned short int tmp1, ulamek, t;
            		
    		wire_reset();
            wire_write_byte(0xcc);        	// pomin czytanie nr 
            wire_write_byte(0x44); 			// nakaz pomiar temperatury 
            _delay_ms(800);    				// czekaj na przetworzenie 800 dla 12 bitow 
    		wire_reset(); 
    		wire_write_byte(0xcc);        	// pomin czytanie nr 
    		wire_write_byte(0xbe);        	// zadaj odczytania temperatury 
    	    temp1 = wire_read_byte();    // pierwszy bajt mlodszy 
            temp2 = wire_read_byte();    // drugi bajt starszy 
    		
    		tmp1 = temp1;             			// w tmp1 bedziemy trzymac czesc ulamkowa 
                    tmp1 &= 0x0f;   			// wycinamy niepotrzebne bity HHHH 
                    temp1 >>= 4;    			// przesuwamy HHHH w prawo wycinamy LLLL 
                    temp2 <<= 4;    			// wycinamy 4 niepotrzebne bity znaku 
                    t = temp1 | temp2;  	// laczymy i w t mamy czesc calkowita 
    				if (t > 127)        		// gdy temperatura jest ujemna 
                        {                    	// musimy przeliczyc wynik 
                            t = 255 - t; 		// przeliczamy czesc calkowita 
                            tmp1 = 15 - tmp1; 	// przeliczamy czesc ulamkowa 		
                        } 		
    				else t=t+60;
    				ulamek = tmp1 * 625;        // ulamek o warosciach do 10000 
                    tmp1 = ulamek / 1000;   	// tmp1 musi byc w granicach od 0 do 9 
                    if (tmp1 ==10) tmp1 = 0; 
    				t=(t*10)+tmp1;
    				
    		return(t);
    }

    w ten sposób gdy temperatura jest ujemna to jest zapisywana normalnie tzn. zmieniony jest tylko znak na dodatni np. gdy jest -20,0 to zapisane jest jako 200, natomiast gdy temperatura jest dodatnia to dodawane do niej jest 60 czyli gdy jest 25,1 to mamy 851 ponieważ do mojego projektu termometr powinien mieć rozdzielczość do dzisiątej części stopnia to ostatecznie wynik jest przemnazany przez 10. Wiem że może nie jest to perfekcyjny algorytm ale to mi się udało wymyśleć i działa
  • Pomocny post
    #7 3287810
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    Gieno100 napisał:
    Ja rozwiązałem ten problem w następujący sposób
    
    //....		
    		tmp1 = temp1;             			// w tmp1 bedziemy trzymac czesc ulamkowa 
                    tmp1 &= 0x0f;   			// wycinamy niepotrzebne bity HHHH 
                    temp1 >>= 4;    			// przesuwamy HHHH w prawo wycinamy LLLL 
                    temp2 <<= 4;    			// wycinamy 4 niepotrzebne bity znaku 
                    t = temp1 | temp2;  	// laczymy i w t mamy czesc calkowita 
    				if (t > 127)        		// gdy temperatura jest ujemna 
                        {                    	// musimy przeliczyc wynik 
                            t = 255 - t; 		// przeliczamy czesc calkowita 
                            tmp1 = 15 - tmp1; 	// przeliczamy czesc ulamkowa 		
                        } 		
    				else t=t+60;
    				ulamek = tmp1 * 625;        // ulamek o warosciach do 10000 
                    tmp1 = ulamek / 1000;   	// tmp1 musi byc w granicach od 0 do 9 
                    if (tmp1 ==10) tmp1 = 0; 
    				t=(t*10)+tmp1;
    				
    		return(t);
    

    Hmmm...
    Inny "ekwiwalent" Twoich ... obliczeń ;)
    
    t = abs(((temp2 << 8) | temp1) * 10 / 16) + ((temp2 & 0x80) ? 0x00 : 600);
    

    Gieno100 napisał:
    ...w ten sposób gdy temperatura jest ujemna to jest zapisywana normalnie tzn. zmieniony jest tylko znak na dodatni np. gdy jest -20,0 to zapisane jest jako 200, natomiast gdy temperatura jest dodatnia to dodawane do niej jest 60 czyli gdy jest 25,1 to mamy 851 ponieważ do mojego projektu termometr powinien mieć rozdzielczość do dzisiątej części stopnia to ostatecznie wynik jest przemnazany przez 10. Wiem że może nie jest to perfekcyjny algorytm ale to mi się udało wymyśleć i działa

    A po co te kombinację z +60 :?:
    Czyż nie prościej , wyliczyć temperaturę z rozdzielczością 1/10 C w taki sposób
    t = ((temp2 << 8) | temp1) * 10 / 16

    Można taki wynik porównać z jakąś zadaną wartością , dodać do czegoś , czy też odjąć , lub wykonać inne działanie arytmetyczne.

    Dla mnie to jest niepojęte i niezrozumiałe :(
    O niepotrzebnym wydłużaniu(marnotrawstwie pamięci programu) kodu , już nie wspomnę ;)

    Piotrek
  • #8 3366742
    gadula88
    Poziom 15  
    Posty: 152
    Pomógł: 12
    Ocena: 9
    A czy ktos wie, jaka może być przyczyna. Jeśli czujnik będąc na 5 cm przewodach i z rezystorem 4k7 pokazuje poprawnie temperatury ujemne. Ale już na 3 metrowym przewodzie i tym samym rezystorze pokazują się krzaczki. Rezystor zmieniałem na 2k2 i dalej te same krzaczki. Niewiem co jest grane, do konwersji i wyświetlenia używam tego:

    a  = ((temp2 << 8) | temp1) * 10 / 16;
    lcd_str(utoa(a,buffer,10));


    Pozdrawiam, i życzę Wesołych Świąt
    Kamil

    EDIT
    Już wiem w czym był błąd, przy krótkim kablu wykorzystywałem "sprintf", a nie "utoa", które to nie mogło sobie poradzić z ujemnymi liczbami. Czy ktoś wie może czym zastąpić pamięciożerną funkcję "sprintf", zeby poprawnie pokazywało ujemne liczby:?:
  • #9 3396273
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Dziękuje za pomoc, rzeczywiście wzór którego używałem był błedny. Do konwersji na string używam funckji itoa(); Zastanawiam się czy da się jakoś wyświetlić przecinek z infromacji które przesyła ten układ czy trzeba kombinować z dzieleniem przez 10 i ręcznym wstawianiem znaku "." ?
  • #10 3396502
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    snow napisał:
    ...Zastanawiam się czy da się jakoś wyświetlić przecinek z infromacji które przesyła ten układ ...

    Da się , ale to kosztuje 1.5kB flasha
    
    //...
    #include <stdlib.h>
    //...
    dtostrf((float)((starszybajt<<8)|mlodszybajt)*10/16/10,5,1,tstring );
    

    Chyba "taniej" wychodzi "ręczne" formatowanie ;)

    Piotrek
  • #11 3396612
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Coś chyba źle robie. Mam tak:

    #include <stdlib.h>
    .
    .
    .
    
    signed int a;
    .
    .
    dtostrf((float)((msb<<8)|lsb)*10/16/10,5,1,a);
    .
    .
    lcd_puts(a);


    W rezultacie dostaje:

    1wire.c:64: warning: passing arg 4 of `dtostrf' makes pointer from integer without a cast
    1wire.c:74: warning: passing arg 1 of `lcd_puts' makes pointer from integer without a cast
    


    Chyba coś mam nie tak z deklaracjami rodzaju zmiennych :roll: Ale dokładnie nie wiem.
  • Pomocny post
    #12 3396912
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    snow napisał:
    Coś chyba źle robie...

    Tak , nie myślisz ;)
    Na 100% , argumentem dla lcd_puts jest wskaźnik na (tablicę)char , a i ostatni argument w dtostrf , to też ten sam typ ;)

    Piotrek
  • #13 3397004
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Fakt, troche zamotałem, teraz już ok z tablicą :)

    Temat można uznać za zamknięty ;) Dziękuje za pomoc.

Podsumowanie tematu

✨ Dyskusja dotyczy obsługi ujemnych temperatur z czujnika DS18B20 w środowisku AVR GCC. Problemem było prawidłowe odczytanie i wyświetlenie temperatur ujemnych, gdyż oryginalny wzór działał tylko dla wartości dodatnich. Zalecane jest traktowanie odczytu temperatury jako signed int, co pozwala na poprawne łączenie bajtów MSB i LSB w jedną zmienną typu signed int, np. za pomocą unii lub operacji bitowych. Przykładowy kod pokazuje, jak odczytać temperaturę i przeliczyć ją z rozdzielczością 1/16°C, uwzględniając znak. Poruszono także kwestie wyświetlania wartości z przecinkiem dziesiętnym – można użyć funkcji dtostrf(), jednak jest to kosztowne pod względem pamięci, więc często stosuje się ręczne formatowanie liczby i wstawianie znaku ".". W dyskusji zwrócono uwagę na problemy z funkcją sprintf() przy ujemnych liczbach i zaproponowano użycie itoa() lub własnych metod konwersji. Poruszono również problem zakłóceń na długich przewodach i konieczność odpowiedniego doboru rezystora podciągającego. Ostatecznie rozwiązanie polega na prawidłowym łączeniu bajtów, użyciu signed int, oraz odpowiedniej konwersji i formatowaniu danych do wyświetlenia na LCD.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA