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

Atmega8 + ds18b20 + [C] + błędy i konfiguracja

Tymek92 30 Cze 2010 23:21 3085 16
  • #1 8249219
    Tymek92
    Poziom 10  
    Zrobiłem sobie w ramach przygotowania do większego projektu termometr na ds'ie, temperaturę wskazuje pięknie gdyby nie jeden mały szczegół. Sypie błędami. średnio co 5 sekund wyskakuje mi temperatura 500st, 80st. ect ect. Tu moje pytanie: czy da się to wyeliminować programowo, czy zrobiłem jakiegoś babola w programie?

    Program:
    #include <avr/io.h>  
    #include <avr/delay.h> 
    #include <avr/signal.h> 
    #include <stdlib.h> 
    #define F_CPU 1000000
    
    #define RED _BV(7) 			//
    #define GREEN _BV(5)  		//jakies
    #define IN 4 				//smieci
    #define DEL_TIME 500 		//
    
    
    #define WE 7
    #define PORT_1Wire PIND 
    #define set DDRD&=~_BV(WE) 
    #define clear DDRD|=_BV(WE)
    
    #define dziesiatkiON PORTB |=_BV(1);
    #define dziesiatkiOFF PORTB &= ~_BV(1);
    #define jednosciON PORTB |=_BV(2);
    #define jednosciOFF PORTB &=~_BV(2);
    #define setkiON PORTB |=_BV(0);
    #define setkiOFF PORTB &=~_BV(0);
    #define DOFF PORTB &=~ ((1 << 0) | (1 << 1) | (1 << 2));
    
    
    
    char buf[8];
    char x [10] = {0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9};
    volatile int a;
    volatile int wyswietlacz;
    
    //************************************************************************************* 
    void send(char bit); 
    unsigned char read(void); 
    void send_byte(char wartosc); 
    unsigned char read_byte(void); 
    void error(int i);
    volatile liczba;
    volatile dziesiatki;
    volatile jednosci;
    volatile setki;
    //************************************************************************************* 
     
    unsigned char RESET_PULSE(void) 
    { 
       unsigned char PRESENCE; 
       clear; 
       _delay_us(500); 
       set; 
       _delay_us(30); 
       if(bit_is_clear(PORT_1Wire, WE)) {PRESENCE = 1;} else {PRESENCE = 0;} 
       _delay_us(470); 
       if(bit_is_set(PORT_1Wire, WE)) {PRESENCE = 1;} 
       else {PRESENCE = 0;} 
       return PRESENCE; 
    } 
    //************************************************************************************* 
    void send(char bit) 
    { 
       clear; 
       _delay_us(5); 
       if(bit == 1) 
          set; 
       _delay_us(80); 
       set; 
    } 
    //************************************************************************************* 
    unsigned char read(void) 
    { 
       unsigned char PRESENCE = 0; 
       clear; 
       _delay_us(2); 
       set; 
       _delay_us(15); 
       if(bit_is_set(PORT_1Wire, WE)) PRESENCE = 1; 
       else PRESENCE = 0; 
       return (PRESENCE); 
    } 
    //************************************************************************************* 
    void send_byte(char wartosc) 
    { 
       unsigned char i; 
       unsigned char pom; 
    
       for(i=0; i<8; i++) 
       { 
          pom = wartosc >> i; 
          pom &= 0x01; 
          send(pom); 
       } 
       _delay_us(100); 
    } 
    //************************************************************************************* 
    unsigned char read_byte(void) 
    { 
       unsigned char i; 
       unsigned char wartosc = 0; 
    
       for(i=0; i<8; i++) 
       { 
          if(read()) wartosc |= 0x01 << i; 
          _delay_us(15); 
       } 
       return (wartosc); 
    } 
    
    
    int main( void ) 
    { 
    	liczba=1;
       	DDRB = 0xFF; // Ustawienie bitu LED jako wyjścia
    	TCCR1B |= (1 << WGM12); // Ustawia timer w tryb CTC
    	OCR1A = 3333; // Ustawia wartość pożądaną na 1Hz dla preskalera 64
    	TCCR1B |= (1 << CS10); // Ustawia timer z preskalerem Fcpu/256
    	TIMSK |= (1 << OCIE1A); // Zezwolenie na przerwania dla CTC
    	sei(); // Zezwolenie globalne na przerwania
    	DDRC = 0xFF;
    	PORTC = 0x00;
    	PORTB = 0xFF;
    	wyswietlacz=0;
    	dziesiatkiOFF;
    	jednosciOFF;
    
    	//*****************************************************************************
    
    	RESET_PULSE();
    	send_byte(0xCC);
    	send_byte(0x4E);
    	send_byte(0x00);
    	send_byte(0x00);
    	send_byte(0x7F);  // ROZDZIALKA 9 BIT
    	_delay_ms(500);
    	_delay_ms(500);
    	RESET_PULSE();
    	send_byte(0xCC);
    	send_byte(0x48);
    
    
    	//*****************************************************************************
        
       while(1) 
       {    
    		wyswietl_temp();
    		_delay_ms(200);
    		dziesiatki = (liczba/10)%10;
    		jednosci = liczba%10;
    		setki = liczba/100;
    
       } 
    } 
    
    
    //************************************************************************************* 
    
    //************************************************************************************* 
    
    //************************************************************************************* 
    void wyswietl_temp(void) 
    { 
       int i; 
       unsigned char sprawdz; 
       char temp1 = 0, temp2 = 0; 
       _delay_ms(200); 
       sprawdz = RESET_PULSE(); 
       if(sprawdz == 1) 
       { 
          send_byte(0xCC); 
          send_byte(0x44); 
          _delay_ms(100); 
          sprawdz = RESET_PULSE(); 
          send_byte(0xCC); 
          send_byte(0xBE); 
    
          temp1 = read_byte(); 
          temp2 = read_byte(); 
    
          int t = (temp1 + (temp2 * 256)) / 16;
    
          liczba=t;
       } 
       else 
       { 
          liczba=999;
       } 
    } 
    //************************************************************************************* 
    
    
    ISR(TIMER1_COMPA_vect)
    {
    
    DOFF;
    switch (a)
    {
    	case 2:
    		setkiON;
    		PORTC = x[setki];
    		a=0;
    		break;
    	case 1:
    		dziesiatkiON;
    		PORTC = x[dziesiatki];
    		a++;
    		break;
    	case 0:
    		jednosciON;
    		PORTC = x[jednosci];
    		a++;
    		break;
    		
    }
    
    }
    


    Z tego powstanie stacja w domu na Atmedze16 na 15 czujników do szklarni (każdy na osobnej linii bo będą się zakłócać + odległość w sumie 100m i różne warunki na poszczególnych częściach przewodów) + pare przycisków i wyświetlacze (temperatura + nazwa szklarni)

    Proszę o jakieś rozwiązanie sypania błędami pomiarów? może wyciąganie średniej z 10 odczytów? czujnik działa na 9-bit. ale chciałbym go zrobić na rozdziałce co 0.1 stopnia. (w końcu ważne czy mam 0.4 stopnia czy już 1 - roślini muszą żyć xD)
  • #2 8249297
    markosik20
    Poziom 33  
    Tymek92 napisał:
    ... ale chciałbym go zrobić na rozdziałce co 0.1 stopnia. (w końcu ważne czy mam 0.4 stopnia czy już 1 - roślini muszą żyć xD)


    Co Ci po rozdzielczości 0.1°C jak sam czujnik ma większy błąd i dryft. Odczytasz że jest 0.4°C a tak naprawdę będzie -0.2°C :wink:.
  • #3 8249315
    __Maciek__
    Poziom 20  
    dziwny problem ..

    AAaaa .. a czemu skoro zmniejszyłeś rozdzielczość do 9 bitów to robisz wartość / 16 ??
    / czy nie trzeba by odpowiednio zmienić działki .. ?? /

    Masz tu moją bibliotekę dla wielu Ds-ów ( używałem 8 ) chodzi bardzo sprawnie - miałem problemy z zakłóceniami / siały mi falowniki /.

    Otrzymujesz tablicę z wartościami temp w dziesiątych częściach stopnia.

    
    /******************************************************************************
    
    One wire library
    
    ******************************************************************************/
    #define F_CPU		11059200 //częstotliwość zegara w Hz
    
    #include "ds18b20.h"
    #include <avr/interrupt.h>
    #include <avr/io.h>
    #include <util/delay.h>
    
    int Temperatura ;
    int	 Temp[8] ;
    uint8_t cnt;
    char	TempError ;
    volatile int DS18B20_Timeout;
    
    uint8_t  OW_PIN ; // PB4
    uint8_t *OW_IN  ; // PINB
    uint8_t *OW_OUT ; // PORTB
    uint8_t *OW_DDR ; // DDRB
    
    #define OW_GET_IN()    bit_is_set(*OW_IN,OW_PIN)  // *OW_IN & (1 << OW_PIN) 
    #define OW_DIR_IN()    *OW_DDR &= (~(1 << OW_PIN )) 
    #define OW_DIR_OUT()   *OW_DDR |= (1 << OW_PIN) 
    
    enum {A,B};
    char watek = A;
    
    // definicja sensorow	
    uint8_t SENS[8][2] = {{&PORTD,5},{&PORTD,6},{&PORTD,7},{&PORTB,0},{&PORTB,1},{&PORTB,2},{&PORTB,3},{&PORTB,4}};
    
    void
    ow_setup(unsigned char sensor) 
    {
    
    	uint8_t *ptr = (uint8_t*)(SENS[sensor][0]) ;	
    
    	OW_OUT = ptr ;
    	OW_DDR = ptr-1 ;
    	OW_IN  = ptr-2 ;
    
    	OW_PIN = (SENS[sensor][1]) ;
    	
    }
    
    unsigned char 
    ow_reset(void) // 900 us
    {
       uint8_t err=1;
       cli();
       OW_DIR_OUT();   
       _delay_us(480);
       OW_DIR_IN();
       _delay_us(70);
       err = OW_GET_IN();
       _delay_us(410);
       sei();
    return err;
    }
    
    unsigned char 
    ow_read_bit(void) // 70 us
    {   
       unsigned char b = 0;
       
       OW_DIR_OUT();
       _delay_us(6);
       OW_DIR_IN();
       _delay_us(9);
       b = OW_GET_IN();
       _delay_us(55);
       
    return b;
    }
    
    void 
    ow_write_bit(char b) // 70 us
    {  
       
       OW_DIR_OUT(); 
       _delay_us(6);
       if (b) OW_DIR_IN();
       _delay_us(64);
       OW_DIR_IN();
       
    }
    
    void 
    ow_write_byte(char data)
    {
    unsigned char i, temp;
       cli();
       for(i=0;i<8;i++)
       {
       temp=data>>i;
       temp &= 0x01;
       ow_write_bit(temp);
       }
       sei();
    }
    
    unsigned char 
    ow_read_byte (void)
    {
    unsigned char i;
    unsigned char value=0;
       cli();
       for (i=0;i<8;i++)
       {
       if(ow_read_bit()) value |= 0x01<<i;
       _delay_us(6);
       }
       sei();
    return (value);
    }
    
    void
    TempDS18B20 (void)
    {
       unsigned char msb, lsb = 0;
    
       switch (watek)
       {
       case A: 	
         if (DS18B20_Timeout) { break; } 
    	
    	  cnt++; 
    	  if(cnt>7) cnt = 0 ;
    	   	
    	  ow_setup( cnt ) ; 
    
    	  if ( ow_reset() ) { Temp[cnt] = 10000 ; break ; } 
    	  
    	  ow_write_byte(0xCC); 
    	  ow_write_byte(0x44);	  
    	  DS18B20_Timeout = 760 * 20; 
    	  watek = B ;
    	  break;
    
       case B:
         if (DS18B20_Timeout) { break; }
    	  
    	  ow_reset() ;
          ow_write_byte(0xCC);
          ow_write_byte(0xBE); 	  
    	  lsb = ow_read_byte() ;
    	  msb = ow_read_byte() ;  
    	  	  	  
    	  Temp[cnt] = (msb<<8 | lsb) * 10 / 16 ;
    	  
    	  DS18B20_Timeout = 1 * 20 ; 
    	   
    	  PORTD ^= _BV(4) ; 
    	  
    	  watek = A ;
    	  break;
    
       default:break;
       } ;
    
    }
    
    


    No i nagłówek

    
    
    /******************************************************************************
    
    One wire library
    Biblioteka odczytuje temperaturę z czujnika ds18b20 
    
    ******************************************************************************/
    
    #ifndef _DS18B20_H
    #define _DS18B20_H
    
    void TempDS18B20(void);
    
    #endif
    


    w głównym programie to w pętli :
    
    TempDS18B20();
    

    oraz w przerwaniu co 50us :
    
    if (DS18B20_Timeout>0) DS18B20_Timeout--;
    

    No i oczywiście definicje :
    
    extern volatile unsigned int DS18B20_Timeout;
    extern int	 Temp[8] ;
    extern int Temperatura ;
    
  • #4 8249639
    flapo213
    Poziom 21  
    Witaj,

    Temperatura rzędu 500C jest nie mozliwa do odczytania z DS18x20. Czujnik może co nawyżej wskazać 125C. Niestety procedury których użwasz do przeliczenia odczytanych danych na temperaturę są błędnie napisane. U uśrednianiu wyniku to tu raczej nie ma mowy bo DS18X20 jest czujnikiem cyfrowym z własnym kontrolerem któy już sam sobie uśrednia wynik więc nie ma potrzeby go już uśredniać, zresztą przy np 12 bitowym ustawieniu gdzie wynik pojawia się po 750ms to 10 próbek miałbyć po 8 sekundach gdzieś mniej więcej więc lipa. Z konwersją danych z czujnika jest tak że nie jest to takie proste dla różnych rozdzielczości jest inaczej dodatkowo ujemna temperatura U2 itd. No chwilę trzeba nad tym posiedzieć.

    Pozdrawiam
  • #5 8249694
    Tymek92
    Poziom 10  
    Maciek, dzięki za biblioteczkę :D na bank się przyda.

    W programowaniniu nie jestem dobry. Raczej sobie raczkuje. Program docelowo był pisany na rodziałkę 12bit czyli muszę zmienić
    
    int t = (temp1 + (temp2 * 256)) / 16;
    

    na:
    
    int t = (temp1 + (temp2 * 256)) / 9;
    

    tak? (przeczytałem notę, trzeba tak xD) - tu wychodzi brak pełnego myślenia o programowaniu µC ;p

    Głupie pytanie: czy mój program potrafi rozpoznać ujemną temp? jeżeli nie to jak mu to "wklepać"?

    edit:
    
    int t = (temp1 + (temp2 * 256)) / 9;
    

    jak tak zrobiłem to temperatura oscyluje około 47stopni...


    Jej, skąd ten błąd?
  • #6 8249735
    __Maciek__
    Poziom 20  
    No właśnie wychodzi brak myślenia ....

    Masz pomiar 9 bit .... zatem 512 działek dla temperatur od -55 do 125 degC

    przy pomiarze 12 bitowym 1 działka wychodzi 1/16 degC .. przy pomiarze 9 bitowym ~0.38 degC ... więc dlaczego wartość dzielisz przez 9 ??
  • #7 8249904
    Tymek92
    Poziom 10  
    To żeby było poprawnie co powinienem zmienić? (chyba nic, bo temperaturę odczytuje teraz poprawnie na /16 pomimo zmiany rozdzielczości- też do końca nie znam się na programowaniu ds'ów ponieważ dopiero jestem początkującym)

    edit: wyeliminowałem błędy filtrując to co odczytuję z ds'a taką oto częścią prymitywnego kodu
    
       while(1) 
       {    
    		wyswietl_temp();
    		_delay_ms(200);
    		if (p==0)
    		{
    			liczba=wartosc; //wartosc to to co odczytam z ds'a a liczba to            to co wyswietlam
    			p=1;
    		}
    		else
    		{
    			if(wartosc>(liczba+3)||wartosc<(liczba-3))
    			{
    			}
    			else
    			{
    				liczba=wartosc;
    			}
    		}
    			
    		dziesiatki = (liczba/10)%10;
    		jednosci = liczba%10;
    		setki = liczba/100;
    
       } 
    }
    



    Ten kod obsługi ds'a to z "INTERFEJS 1-WIRE – CEZARY KLIMASZ"
    [1] Grebosz J.: Symfonia C++. Kraków 1999, Kallimach
    [2] Opracowanie własne – Programowanie mikrokontrolerów AVR w jezyku C. Kraków 2007
    [3] Dallas Semiconductor : DS18B20 datasheet.
    [4] http://wikipedia.org

    znalezione przez google w pdf'ie
  • #8 8249954
    tmf
    VIP Zasłużony dla elektroda
    Już chyba po raz 10 widzę ten sam kod obsługi. On ma babol na babolu, co już wielokrotnie było dyskutowane. Ciekaw jestem skąd wy go bierzecie? Przecież on jest napisany przez gościa który uczył się programowania (szczegóły można znaleźć w archiwum).
  • #9 8250103
    gaskoin
    Poziom 38  
    poza tym, w ciągu ostatniego tygodnia były chyba dwa czy trzy tematy na temat tego termometru, gdzie m.in ja wrzuciłem linka na stronę dallasa z opisem jak to podłączyć do avr (wszystkie masz na osobnych liniach, więc w kodzie proponowanym przez dallasa wystarczy dokonać paru zmian)
  • #10 8253513
    flapo213
    Poziom 21  
    Witam,

    Dobra rada dla wszystkich którzy rzeźbią kod konwertujący wynik pobrany z DS18X20 stwórzcie sobie projekt w pctowym sofcie typu VC, borland, qt4, czy najprostszym środowisku gcc co by się to dało debugować. Jedyne co trzeba zrobić to dostosować szerokości zmiennych bo są inne. A skąd dane może poajwić się pytanie z datasheet DS18x20 jest tableka z przykładowymi wartościami dla temperatur. Po co rzeźbić to w mikrokontrolerze.

    Pozdrawiam
  • #12 8253852
    flapo213
    Poziom 21  
    Witam,

    do kolegi TMF,

    chyba nie zrozumiałeś co chciałem przekazać. Użyłem magicznego sformułowania funkcji konwertujących,w moim poście nie ma słowa o niskopoziomowym pobieraniu wartości z czujnika. Samo odczytanie wartości z rejestrów DS18x20 i wyświetlenie ich bez stosownej obróbki nie ma żadnego sensu.

    Intencją mojego postu jest wskazanie prostszej drogi przecież ATM8 nie ma możliwości debugu, jeśli ktoś nie śmiga zbyt dobrze w C a próbuje to pc-towe symulowanie jest świetna sprawą. Celowo nie wymieniłem AVRstudio bo to jest tylko fajne dla avr-ów a warto liznąć jakiegoś pc-towego prostego kompilatora.


    Tematem postu jest błędny jak twierdzi twórca tematu odczyt 500C. Więc wnioskuję że niskopoziomówka działa dobrze a jest problem z przekonwerowaniem.

    Pozdrawiam
  • #13 8253926
    gaskoin
    Poziom 38  
    aaaaaaaaaaaaaaaaaa macie: (ds18b20, 16 bit)
    
    public class Calculate {
        public static float Temperature(int temp_lsb,int temp_msb){
            temp_lsb = temp_lsb + ((temp_msb&0x07)<<8);
    
            if(temp_msb >= 8) temp_lsb = temp_lsb - 0x0800;
            return (float)temp_lsb/0x10;
        }
    }
  • #14 8254123
    tmf
    VIP Zasłużony dla elektroda
    flapo213 napisał:
    Witam,

    do kolegi TMF,

    chyba nie zrozumiałeś co chciałem przekazać. Użyłem magicznego sformułowania funkcji konwertujących,w moim poście nie ma słowa o niskopoziomowym pobieraniu wartości z czujnika. Samo odczytanie wartości z rejestrów DS18x20 i wyświetlenie ich bez stosownej obróbki nie ma żadnego sensu.

    Intencją mojego postu jest wskazanie prostszej drogi przecież ATM8 nie ma możliwości debugu, jeśli ktoś nie śmiga zbyt dobrze w C a próbuje to pc-towe symulowanie jest świetna sprawą. Celowo nie wymieniłem AVRstudio bo to jest tylko fajne dla avr-ów a warto liznąć jakiegoś pc-towego prostego kompilatora.
    Pozdrawiam


    Przecież te funkcje konwertujące sprowadzają się do podzielenia przez 16. To jest zwykły kod U2. Więc co tu sprawdzać na PC?
    Zresztą na PC będzie właśnie trudniej, bo integer na PC != integer na AVR.
  • #15 8254276
    gaskoin
    Poziom 38  
    tmf napisał:

    Przecież te funkcje konwertujące sprowadzają się do podzielenia przez 16. To jest zwykły kod U2


    racja, mój kod też nic więcej nie zawiera, sklejenie dwóch intów 8 bitowych w jeden 16 bitowy i podzielenie go przez 16


    Nie zawsze się przecież tak robi, że funkcje obliczające przenosi się na komputer. Czasem wynik powinien być przesyłany gotowy, ale to już zależy od kontekstu użycia. Jak kolega z tematu robi termoregulator do szklarni, to raczej nie będzie w niej stawiał komputera.
  • #16 8559798
    Mariojas
    Poziom 17  
    Dlaczego mówicie o innym dzieleniu dla odczytu 12bit i 9bit? Mam mało doświadczenia z tym czujnikiem, ale w dokumentacji wyraźnie jest napisane że przy 9bit różnica jest taka że bity 0 1 2 są niezdefiniowane. Czyli w moim rozumowaniu jest tam losowa informacja. Przez to tracimy tylko na dokładności danej. Jeśli się mylę to proszę mnie poprawić.
  • #17 8560389
    gaskoin
    Poziom 38  
    Dzielenie w obu przypadkach powinno być takie samo.
REKLAMA