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

Atmega16 ds18b20 [c] inny problem

felekfala 25 Sie 2010 22:49 1865 13
REKLAMA
  • #1 8438904
    felekfala
    Poziom 19  
    Witam
    Wiem, że ostatnio był wysyp podobnych tematów ale mam problem z czujnikiem DS18B20, którego sam nie dam rady rozwiązać.
    Otóż po włączeniu układu na wyświetlaczu pojawia się temperatura ok 25 stopni, która rzeczywiście odpowiada temperaturze otoczenia. Następnie co 300ms na wyświetlaczu ma się pojawiać aktualna temperatura niestety następne odczyty są błędne i na lcd pojawia mi się ok 85 stopni (czasami ok 127 i to już od razu po włączeniu przy pierwszym pomiarze). Poniżej przedstawiam listingi
    onewire.c:

    
    
    #include  <avr\io.h>
    #include <inttypes.h>
    #include <util/delay.h>
    
    
    #include "one_wire.h"
    #include "harddef.h"
    #include "makra.h"
    
    /////////////////////
    //Definicje funkcji
    ////////////////////
    
    /* Definicje w pliku harddef.h
    #define ONE_WIRE 0	
    #define ONE_WIRE_PORT C
    */
    
    
    // Reset
    unsigned char one_wire_reset (void)
    {
    	unsigned char presence; // zmienna przetrzymujaca 
    							// odpowiedź slave
    							// Wyzerowanie one wire
    	DDR(ONE_WIRE_PORT) |= _BV(ONE_WIRE);
    	
    	
    	_delay_us(700);
    	
    	// Zwolnienie magistrali
    	
    
    	DDR(ONE_WIRE_PORT) &= ~_BV(ONE_WIRE);
    	// Oczekiwanie na ustawienie lini w stan niski
    	// przez slave
    	_delay_us(80); 
    	// Sprawdzamy poziom lini one wire, 
    	// presence = 1 jeśli linia w stanie niskim
    	
    	if (!(PIN(ONE_WIRE_PORT)&_BV(ONE_WIRE))) 
    	{
    		// Odczekanie przez master 470 ms na koniec okresu nie aktywności
    		_delay_us(400);
    		// Sprawdzenie czy SLAVE podciągął linię do zasilania
    		if (PIN(ONE_WIRE_PORT)&_BV(ONE_WIRE)) 
    		presence = 1 ;
    		else
    		presence = 0;
    		return presence;
    	}
    	// Zwrot bitu potwierdzenia 
    	
    	else
    	{
    		presence = 0;
    		//_delay_us(470);
    		return presence;
    	}
    		
    }
    
    
    
    // wysyłanie do układu pojedyńczego bitu
    
    void one_wire_write_bit (char bit)
    {
    	cli(); // Wyłączenie przerwań 
    			// Ustawienie linii w stan niski
    	
    	
    	
    	DDR(ONE_WIRE_PORT) |= _BV(ONE_WIRE);
    	
    	_delay_us(10);
    	// Jeśli bit = 1 to po 5ms 
    	// podćiągnięcie lini do zasilania
    	// i odczekanie 80 ms
    	if(bit==1)
    		// Podciągnięcie linii do Vdd
    	DDR(ONE_WIRE_PORT) &= ~_BV(ONE_WIRE);
    	_delay_us(80);
    		// Podciagniecię linii do zasilania
    	DDR(ONE_WIRE_PORT) &= ~_BV(ONE_WIRE);
    	
    	// Odblokowanie przerwań
    	sei(); 
    }
    
    
    // Wysłanie do układu bajtu
    
    void one_wire_write_byte (char bajt)
    {	
    	unsigned char i; // Zmienna licznikowa
    	unsigned char temp; // Zmienna tymczasowa,
    	
    	
    	for (i=0; i<8; i++)
    	{
    		temp = bajt >> i;
    		temp &= 0x01;
    		one_wire_write_bit(temp);
    	}
    	
    	_delay_us(100);
    }
    
    // Funkcja odczytu bitu
    unsigned char one_wire_read_bit (void)
    {
    	
    	
    	unsigned char  bit;
    	
    	cli();  // zablokowanie obsługi przerwań
    			// na czas odczytu bitu
    			// Wyzerowanie linii	
    			
    	DDR(ONE_WIRE_PORT) |= _BV(ONE_WIRE);
    
    	
    	_delay_us(5);
    	// Odblokowanie linii
    
    	DDR(ONE_WIRE_PORT) &= ~_BV(ONE_WIRE);
    	
    	_delay_us(5); // Zwłoka przed odczytem
    	
    	// Odczyt bitu, jeśli 0 to... else...
    	
    	if (!(PIN(ONE_WIRE_PORT) & _BV(ONE_WIRE))) 
    	{	
    		sei();	// Odblokowanie przerwań
    		_delay_us(55); //Zwłoka na podciągniecie do Vcc
    						// +15 us zwłoki w funkcji nadrzednej 
    						// one_wire_read_byte
    		bit = 0;	
    		return bit; // Zwrot bitu
    	}
    	else
    	{
    		sei();	// Odblokowanie przerwań
    		bit = 1;
    		_delay_us(55);
    		return bit; // Zwrot bitu
    	}
    }	
    
    // Funkcja odczytu bajtu
    unsigned char one_wire_read_byte (void)
    {
    	unsigned char  bajt; // Zmienna przechowujaca odczytany bajt
    	unsigned char  i;
    	// Odczyt 8 bitów i złączenie ich w bajt
    	
    	
    	for (i=0; i<8; i++)
    	{
    		// Jeśli odczytany bit = 1 to wpisanie jedynki
    		if (one_wire_read_bit())
    		bajt |= 0x01<<i;
    		_delay_us(15);
    		
    	}
    	return bajt;
    }
    



    main.c gdzie uC co 300ms ma aktualizować tempetarurę

    
    int main(void)
    {
    	
    	unsigned char sprawdz;
    char temp1 = 0, temp2 = 0;
    char buf[8];
     
    	
    	// Inicjalizacja
    	
    	lcd_init();
    	lcd_cls();
    	
    	
    	// Pętla główna
    	
    	
    		
    	while(1)
    	{	
    		
    		
    	sprawdz = one_wire_reset();
    		if (sprawdz == 1)
    		{
    			one_wire_write_byte(0xCC);
    			one_wire_write_byte(0x44);
    			_delay_ms(1000);
    			
    			sprawdz = one_wire_reset();
    			one_wire_write_byte(0xCC);
    			one_wire_write_byte(0xBE);
    			
    			temp1=one_wire_read_byte();
    			temp2=one_wire_read_byte();
    			
    			sprawdz = one_wire_reset();
    		
    			float temp = 0;
    		
    			temp = (float)(temp1+(temp2*256))/16;
    	
    			dtostrf(temp,1,1,buf);
    			lcd_cls();
    			lcd_str_P((prog_char*)PSTR("temp = "));
    			lcd_str(buf);				// Wyświetlenie temperatury
    			
    	
    		
    			_delay_ms(300);
    			
    		}
    		else
    		{
    			lcd_cls();
    			lcd_command(LCDC_DDA|64);
    			lcd_str_P((prog_char*)PSTR("cisza = "));
    			_delay_ms(100);
    		}
    	}
    	return 0;	
    
    }



    Z góry dziękuje za pomoc
  • REKLAMA
  • #3 8439546
    Skyttop
    Poziom 11  
    Może jest problem z czasami? 1-Wire jest bardzo na to wrażliwy. Ja zastosowałem własną funkcję:
    
    inline __attribute__((gnu_inline)) void therm_delay(uint16_t delay)
    {
    	while(delay--) asm volatile("nop");
    }
    


    Oczywiście musisz zadeklarować częstotliwość, z jaką CPU pracuje, np:
    #define F_CPU 8000000UL

    Używasz tej funkcji tak samo jak _delay_ms(...), wpisując np. dla 700us po prostu:
    therm_delay(us(700));
  • REKLAMA
  • #4 8439607
    flapo213
    Poziom 21  
    Witaj,

    z tego co piszesz to raczej z czasami a przynajmniej tymi niskopoziomowymi nie ma problemu. Twój problem polega na tym że czujnik Ci się resetuje i pokazuje Ci defaultową temperaturę 85C. Spójrz w dokumentację co powinieneś odczytać po resecie czujnika.

    Chodzi o to że czujnik zaraz po włączeniu zasialania i zresetowaniu go posiada w swoim buforze temperaturowym taką standardową defaultową temperaturę 85 temperatura ta zmienia się po poprawnym zakończeniu komendy convert 0x44 wtedy nadpisuję tą defaultową wartość.

    W jakim trybie zasilasz czujnik 3 przewodowa czy 2 przewodowo tzw. parasite power

    Pozdrawiam
  • #5 8439642
    felekfala
    Poziom 19  
    Korzystam z kwarcu zew. o f = 8MHz, #define F_CPU zapisuje w Makefile.

    Czujnik zasilam w trybie normalnym (tj. 3 przewodowym).

    Nie jest tak że mam dokładnie 85 stopni a jest to wartość zbliżona np:
    Przy przy pierwszym odczycie mam 23,9C w następnych 87,9C czyli o 64 C więcej. Przy podgrzewaniu dłonią czujnika temperatura wzrasta ale wartość zatrzymuje się (co jakiś czas resetuje układ zdejmując zasilanie stąd znam dwie temperatury) na 30,8C (przy pierwszym pomiarze od razu po włączeniu) oraz 94,8 dla reszty pomiarów.

    Różnica między wskazaniami czasami wynosi 64,2 lub 64,4 więc nie jest zawsze stała.
  • #6 8439681
    janbernat
    Poziom 38  
    Tu:
    " // Odczekanie przez master 470 ms na koniec okresu nie aktywności
    _delay_us(400); "
    Piszesz o ms a używasz opóźnienia w mikrosekundach.
    I w paru innych miejscach też.
  • REKLAMA
  • #8 8440927
    janbernat
    Poziom 38  
    Wydaje mi się że gdzieś tu siedzi pułapka:

    temp = (float)(temp1+(temp2*256))/16;

    Ale może mi się tylko wydaje.
    temp1 i temp2 są 8-bitowe char.
    Może powinny być int.
    Ale może mi się tylko wydaje.
  • REKLAMA
  • #9 8441105
    gaskoin
    Poziom 38  
    bezpieczniej dać unsigned char, bo char ma zakres tylko do 127, przy temperaturze 25 stopni celsjusza powinno Ci pokazać tego typu badziewie
  • #11 8441426
    gaskoin
    Poziom 38  
    dla 25 stopni celsjusza bajty wyglądają tak mniej więcej:

    hex:
    01 90

    dec:
    1 144

    144 już jest poza zasięgiem zmiennej char dostaniesz więc bzdury, powinna być unsigned char (jak nie wiesz dlaczego to poczytaj o zakresach zmiennych, a jak Ci się nie chce to daj int) albo czymkolwiek co ma zakres większy od 127

    Poza tym na pierwszy rzut oka widzę, że masz złe czasówki, zerknij więc do manuala i je popraw a potem napisz, że nie działa i pytaj dlaczego
  • #12 8441965
    felekfala
    Poziom 19  
    Korzystałem już na początku z unsigned char, int oraz z uint8_t bez skutku.

    Jeśli chodzi o czasówki to korzystałem z książki "Projektowanie systemów mikroprocesorowych" Pawła Hadama. Czasy tez zgadzają się z manualem jęsli nie będę wdzięczny za wskazanie danego czasu.

    Po za tym skąd ten pierwszy właściwy pomiar i następne które jeśli chodzi o przyrosty temp. zachowują się odpowiednio.
  • #14 8446374
    felekfala
    Poziom 19  
    Rzeczywiście pomogło :D dzięki

    Błąd strasznie głupi. W funkcji odczytu całego bajtu nie daje wartości początkowej zmiennej bajt dlatego przy ponownym uruchomieniu tej funkcji
    przypisywana jest jej przypadkowa wartość. Swoją drogą dlaczego przy pierwszym uruchomieniu tka nie było?

    Dzięki wszystkim.
REKLAMA