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

Atmega8 + DS18B20 - błędny odczyt kodu czujnika w WinAVR

olo777 28 Gru 2005 13:56 4565 13
REKLAMA
  • #1 2122641
    olo777
    Poziom 11  
    Posty: 7
    Witam

    Od paru dni mecze sie z napisaniem obslugi 1wire. Czujnik DS18B20 podlaczony mam na 3 przewodach (Vcc, GND i sygnal zwarty z Vcc przez rezystor 4k7). Uklad chyba zlozony jest poprawnie bo program napisany w CodeVisionAVR dziala i pokazuje dobra temperatute.

    Ponizej przedstawiam program z ktorym mam klopoty, kompiluje go w WinAVR. Wyglad na to ze dziala funkcja ow_reset bo dostaje na lcd komunikat ok. Dalej probuje odczytac 64bitowy kod czujnika, niestety otrzymuje osiem wartosci 255.

    Wpliku temp.c i temp.h linijki z komentarzem to inna tez bezowocna proba odwolania sie do czujnika.

    Atmega8 dziala z zegarem 8Mhz (uisp -dlpt=0x378 -dprog=bsd --wr_fuse_l=0xe4)

    Bede wdzieczny za pomoc w znalezieniu bledu.

    plik term.c
    #include <avr/io.h>
    #include "term.h"
    #include "delay.c"
    #include "lcd.c"
    
    
    unsigned char ow_reset(void)
       {
       uint8_t err=1;
       OW_DIR_OUT();
        OW_OUT_LOW(); 
    	
    	delay_us(480);
    
    	OW_DIR_IN(); 
    	
    	delay_us(70);
    	//err = OW_GET_IN();
    	if (bit_is_clear(OW_IN,OW_PIN)) {err=0;}
    	delay_us(410);
    
    return err;
    }
    
    
    unsigned char ow_read_bit(void)
       {
       
       uint8_t b=1;
       	OW_DIR_OUT();
    	OW_OUT_LOW();
    	delay_us(6);
    	OW_DIR_IN();
    	delay_us(9);
    	//b = OW_GET_IN();
    	if (bit_is_clear(OW_IN,OW_PIN)) {b=0;}
    	delay_us(55);
    	return b;
       }
    
    
    void ow_write_bit(char b)
       {
       
       if (b) 
    	{
    	OW_DIR_OUT();
    	OW_OUT_LOW(); 
    	
    	delay_us(6);
    	OW_DIR_IN();
    	delay_us(64);
    	}
       else
       {
       OW_DIR_OUT();
       OW_OUT_LOW(); 
    	
    	delay_us(60);
    	OW_DIR_IN();
    	delay_us(10);
       }
    }
    
    void ow_write_byte(char data)
    {
    unsigned char i, temp;
    for(i=0;i<8;i++)
    	{
    	temp=data>>1;
    	temp &= 0x01;
    	ow_write_bit(temp);
    	
    	}
    }
    
    unsigned char ow_read_byte (void)
    {
    unsigned char i;
    unsigned char value=0;
    	for (i=0;i<8;i++)
    	{
    	if(ow_read_bit()) value |= 0x01<<i;
    	delay_us(6);
    	}
    return (value);
    }
    
    void lcd_putuint8(uint8_t a){
            uint8_t z;
            z = a  / 100;
            if (z) lcd_putc(z + 0x30);
            a -= z * 100;
            z = a / 10;
            if (z) lcd_putc(z + 0x30);
            a -= z * 10;
            lcd_putc(a + 0x30);
    
    }
    
    
    int main (void)
    {
     uint8_t n;
    char dat[9];
    
    lcd_init(LCD_DISP_ON);
    lcd_puts("Test HD44780");
    delay_ms(1500);
    lcd_clrscr();
    
    
    	
    	
    if (!ow_reset())
    {
    lcd_puts("OK");
    delay_ms(1500);
    lcd_clrscr();
    
    		ow_reset();
    		ow_write_byte(0x33);
    		for (n=0;n<8;n++) { dat[n]=ow_read_byte();}
    		
    		for (n=0;n<8;n++) 
    			{
    			lcd_putuint8(dat[n]);
    			if (n==3) { lcd_puts("\n");}
    			else lcd_puts(" ");
    			}
    
    }
    else
    {
    lcd_puts("Brak czujnika");
    delay_ms(1500);
    lcd_clrscr();
    }
    
    return(0);
    }
    


    plik term.h
    
    /*
    
    #define OW_PIN  PD5
    #define OW_IN   PIND
    #define OW_OUT  PORTD
    #define OW_DDR  DDRD
    
    #define OW_GET_IN()   ( OW_IN & (1<<OW_PIN))
    #define OW_OUT_LOW()  ( OW_OUT &= (~(1 << OW_PIN)) )
    #define OW_OUT_HIGH() ( OW_OUT |= (1 << OW_PIN) )
    #define OW_DIR_IN()   ( OW_DDR &= (~(1 << OW_PIN )) )
    #define OW_DIR_OUT()  ( OW_DDR |= (1 << OW_PIN) ) 
    
    */
    
    #define OW_PIN  PD5
    #define OW_IN   PIND
    #define OW_OUT  PORTD
    #define OW_DDR  DDRD
    
    #define OW_OUT_LOW()  cbi(OW_OUT,OW_PIN)
    #define OW_OUT_HIGH() sbi(OW_OUT,OW_PIN)
    #define OW_DIR_IN()   cbi(OW_DDR,OW_PIN)
    #define OW_DIR_OUT()  sbi(OW_DDR,OW_PIN)
      
    


    plik delay.c
    #include "delay.h"
    
    void delay_us(unsigned int us)
    
    {
    
        unsigned int delay_loops;
    
        register unsigned int  i;
    
        delay_loops = (us+3)/5*CYCLES_PER_US; // +3 for rounding up (dirty)
    
        for (i=0; i < delay_loops; i++) {};
    
    }
    
     
    
    void delay_ms(unsigned int ms)
    
    {
    
                unsigned int i;
    
                for (i=0;i<ms;i++)
    
                            {
    
                            delay_us(999);
    
                            asm volatile (
    
                            "WDR"::);
    
                                                   }
    
    } 


    plik delay.h
    
    #define F_CPU        8000000                    /* 1MHz zegar procesora */
    
    #define CYCLES_PER_US ((F_CPU+500000)/1000000) /* cpu cycles per microsecond */
    
    
    void delay_us(unsigned int us);
    
    void delay_ms(unsigned int ms);
    
  • REKLAMA
  • #2 2124486
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    Na 1-szy ogień poszła funkcja ...
    
    void ow_write_byte(char data)
    {
    unsigned char i, temp;
    for(i=0;i<8;i++)
       {
       temp=data>>1;
       temp &= 0x01;
       ow_write_bit(temp);
       
       }
    } 
    

    ... , która jest do ... poprawki :D

    Piotrek
  • REKLAMA
  • Pomocny post
    #3 2125054
    Fandango
    Poziom 13  
    Posty: 23
    Pomógł: 2
    Ocena: 4
    Ja miałem podobne problemy i okazało się, że to z powodu funkcji czasowej delay_us. Zastąpiłem ją inną i wszystko zaczęło chodzić OK.
    Zastosuj tą:

    void delayus(unsigned char t) //parametr t od 0 do 255.
    {
    do{asm("nop");}while(--t);
    }

    W miejscu gdzie masz zdefiniowaną częstotliwość oscylatora wpisz też:

    #define F_CPU 8000000ul
  • #4 2125974
    olo777
    Poziom 11  
    Posty: 7
    Fandango wszystko zaczelo dzialac !!!
    Serdeczne dzieki za pomoc, miales racje blad byl w opoznieniach.


    zumek, zastanawialem sie nad ta funkcja, nie wiem co tam jest zle, funkcja pochodzi ze strony dallas semiconductor gdzie opisana jest kominukacja 1-wire, z tamtad tez sa zastosowane czasy opoznien przy komunikacji
    http://www.maxim-ic.com/appnotes.cfm/appnote_number/126
  • #5 2126928
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    olo777 napisał:

    ...
    zumek, zastanawialem sie nad ta funkcja, nie wiem co tam jest zle, funkcja pochodzi ze strony dallas semiconductor gdzie opisana jest kominukacja 1-wire, z tamtad tez sa zastosowane czasy opoznien przy komunikacji
    http://www.maxim-ic.com/appnotes.cfm/appnote_number/126

    A ja , myślę sobie tak:" masz podaną na talerzu gotową funkcję, a rozbudowujesz ją niepotrzebnie i to jeszcze z błędami ;)
    Teraz meritum:
    
    void ow_write_byte(char data)
    {
    unsigned char i, temp;
    for(i=0;i<8;i++)
       {
       temp=data>>1; //pobierz wartość zmiennej data , przesuń o jedno miejsce w prawo i zapisz do zmiennej temp
       temp &= 0x01;
       ow_write_bit(temp);
       
       }
    } 
    

    Pisząc krótko - w kółko(8-krotnie) wysyłasz na magistralę 1wire bit nr. 1 ze zmiennej data.

    Piotrek
  • REKLAMA
  • #6 2126994
    olo777
    Poziom 11  
    Posty: 7
    zumek masz racje :oops: powinno byc
    
    void ow_write_byte(char data)
    {
    unsigned char i, temp;
    for(i=0;i<8;i++)
       {
       temp=data>>i; 
       temp &= 0x01;
       ow_write_bit(temp);
       
       }
    } 
    


    w kodzie maialem poprawnie, skad cos takiego sie wkleilo na forum nie wiem 8O , musialem cos bezsensu pozmieniac

    inna sprawa ze dopiero nowa funkcja opozniajaca spowodowala poprawne dzialanie programu
  • #7 2127023
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    olo777 napisał:
    zumek masz racje :oops: powinno byc
    
    void ow_write_byte(char data)
    {
    unsigned char i, temp;
    for(i=0;i<8;i++)
       {
       temp=data>>i; 
       temp &= 0x01;
       ow_write_bit(temp);
       
       }
    } 
    


    w kodzie maialem poprawnie, skad cos takiego sie wkleilo na forum nie wiem 8O , musialem cos bezsensu pozmieniac

    inna sprawa ze dopiero nowa funkcja opozniajaca spowodowala poprawne dzialanie programu


    olo777 :!: Śpisz :?: To ten sam błędny kod ;)
  • #8 2127044
    olo777
    Poziom 11  
    Posty: 7
    zumek napisał:
    olo777 :!: Śpisz :?: To ten sam błędny kod ;)


    nie, teraz jest juz OK
  • REKLAMA
  • #9 2458631
    Fantomen
    Poziom 20  
    Posty: 482
    Pomógł: 3
    Ocena: 19
    Ma ktos moze bibloteki lcd.c ktore kolega olo777 uzyl w tym programie? Prosze o udostepnienie.
  • #10 3037635
    Konto nie istnieje
    Konto nie istnieje  
  • #11 5688345
    Dave_Masters
    Poziom 14  
    Posty: 74
    Pomógł: 1
    Ocena: 4
    Hmm, trochę to juz nieaktualne, ale może ktoś to jeszcze czyta (ja czytałem :P )

    Minimalna częstotliwość pracy AVR wg noty katalogowej Atmela do obsługi 1-wire to 2,17 MHz, czyli de facto 4MHz na wewnętrznym oscylatorze. Na 8 MHz za pomocą funkcji _delay_loop_1 i _delay_loop_2 bez problemu wyznaczysz odpowiednie całkowite wartości parametrów dla opóźnień.

    Dla zainteresowanych nota nazywa sie:
    AVR318: Dallas 1-Wire® master, nie wiem czy jeszcze jest na stronie Atmela, mam ją na dysku i mogę przesłać.
  • #12 5721493
    rozumek
    Poziom 10  
    Posty: 55
    Pomocy!!!

    powiedzcie mi co jest nie tak z moja funkcja resetująca DS18B20, juz nie wiem co robic.

    uchar init_DS18B20(void)
    {
     uchar presence = 0;
     
     _1_WIRE_DIR |= _BV(_1_WIRE);            //---ustawienie magistrali w trybie send
     _1_WIRE_OUT &= ~_BV(_1_WIRE);           //--- wysłanie sygnalu reset
     _delay_us(480);
     
     _1_WIRE_DIR &= ~_BV(_1_WIRE);          //--- ustawienie magistrali w trybie receive   
     
     _delay_us(70);                     //--- DS18B20 czeka do 60 us i ustawia 1_wire na 0 
      
     if(!(_1_WIRE_IN & _BV(_1_WIRE)))         //--- jesli wykryto ze 1_wire jest 0 zapala diode i ustawia sygnał "obecnosci"
     {
      PORTC &= ~_BV(PRESENCE);
      presence = 1;  //--- sprawdzenie czy wystapił sygnał dopstepnosci DS18B20
     }
     _delay_us(410);
     return presence;
    }


    funkcja zwraca obecnosc czujnika jesli presence = 0 i przy okazji zapala diode, nic juz nie działa. Mam zestaw uruchomieniowy ATmega8 zl2avr, czestotliwosc to 1MHz, (domyslnie, bo nie bawiłem sie fuse bitami). DS18B20 zasilam +5 V znajdujacego sie na plytce.

    dodam jeszcze ze mój rezystor podciagajacy ma wartosc: 3k3
  • #13 5728888
    Dave_Masters
    Poziom 14  
    Posty: 74
    Pomógł: 1
    Ocena: 4
    Dla jednego czujnika daj 4,7k i zmień częstotliwość procka. Jeśli chcesz się naocznie przekonać, że 1MHz to za mało to polecam zabawę z symulatorem i analizę czasów wykonania programu. Jak na talerzu będziesz miał podane, że czasy niestety są błędne.
    I polecam używanie makr / funkcji - kod będzie znacznie czytelniejszy i łatwiejszy do poprawki.

    P.S. Wysłałem Ci moją bibliotekę mailem wraz z notami aplikacyjnymi.
  • #14 5792964
    wer1207
    Poziom 13  
    Posty: 64
    Pomógł: 1
    Ocena: 4
    Podaję działający kod na atmega8. U mnie chodzi na kwarcu 8 MHz, ale zmieniałem na 1 MHz wewnętrzny rezonator i też działało.
    DS18B20 na PB2. rezystor (u mnie potencjometr) ma mniejszy opór niż 4k7, bo przy wyższych temperaturach świruje.
    LCD: DB4 - DB7 odpowiednio PB4 - PB7. Potrzebna obsługa LCD jest w kilku linijkach, co nie wygląda najlepiej, ale działa na pewno.
    Całość napisana w AVR Studio.

    #include <avr/io.h>
    #include <util/delay.h>
    #define LCD PORTD
    #define E 3
    #define RS 1
    
    #define SET_E   LCD |= _BV(E)
    #define CLR_E   LCD &= ~_BV(E)
    #define SET_RS  LCD |= _BV(RS)
    #define CLR_RS  LCD &= ~_BV(RS)
    
    void write_to_lcd(char x)    
    { SET_E; LCD = ((LCD & 0x0F) | (x & 0xF0)); CLR_E; SET_E; LCD = ((LCD & 0x0F) | ((x & 0x0F) << 4)); CLR_E; _delay_ms(2); }
    void write_command(char x)  {CLR_RS;  write_to_lcd(x);   }
    void write_char(char x)    {SET_RS;  write_to_lcd(x);   }
    void write_text(char * s)  {while(*s)    {  write_char(*s);  s++;   }}
    void lcd_init(void)   {_delay_ms(25);  CLR_E; CLR_RS; SET_E; LCD &= 0x3F; CLR_E; _delay_ms(5); SET_E; LCD &= 0x2E; CLR_E; 
       _delay_ms(2);  write_command(0x28);  write_command(0x08); write_command(0x01); write_command(0x06); write_command(0x0C); }
    
    
    #define DQ 2
    #define SET_DQ DDRB &= ~_BV(DQ)
    #define CLR_DQ DDRB |= _BV(DQ)
    #define IN_DQ PINB & _BV(DQ)
    
    void ow_reset(void)
    {
    	CLR_DQ;                                    // stan niski na linii 1wire
    	_delay_us(480);                            // opóźnienie 480us
    	SET_DQ;                                    // stan wysoki na linii 1wire
    	_delay_us(480);                            // opóźnienie 480 us
    }
    
    void ow_write_bit(char b)                  // procedura zapisu bitu na linię 1wire
    {
    	CLR_DQ;                                    // stan niski na linii 1wire
    	_delay_us(10);                             // opóźnienie 10us
    	if(b) SET_DQ;                              // jeśli b<>0 to ustaw stan wysoki na linii
    	_delay_us(100);                            // opóźnienie 100us
    	SET_DQ;                                    // stan wysoki na linii 1wire
    }
    
    char ow_read_bit(void)
    {
    	CLR_DQ;
    	_delay_us(2);
    	SET_DQ;
    	_delay_us(12);
    	if(IN_DQ) return 1; else return 0;
    }
    
    unsigned char ow_read_byte(void)
    {
    	unsigned char i;
    	unsigned char value = 0;
    	for (i=0;i<8;i++)
    	{
    		if(ow_read_bit()) value|=0x01<<i;
    		_delay_us(47);
    	}
    	return(value);
    }
    
    void ow_write_byte(char val)
    {
    	unsigned char i;
    	unsigned char temp;
    	for (i=0; i<8; i++)
    	{
    		temp = val >> i;
    		temp &= 0x01;
    		ow_write_bit(temp);
    	}
    }
    
    int main(void) 
    {
    	DDRD = 0xFE;
    	lcd_init();
    	char msb, lsb,  temp;
    	float celcius;
    	while(1)
    	{
    		ow_reset();
    		ow_write_byte(0xCC);
    		ow_write_byte(0x44);
    		for (uint8_t a = 0; a < 25; a++) _delay_ms(30);
    		ow_reset();
    		ow_write_byte(0xCC);
    		ow_write_byte(0xBE);
    		lsb = ow_read_byte();
    		msb = ow_read_byte();
    		celcius =  msb << 8 | lsb ;
    		celcius/=16;
    		temp = msb << 4| lsb >> 4;
    		write_command(0x01);
    		unsigned char tab[6];
    		write_text(dtostrf(celcius, 2, 4, tab));
    	}
    return 0;
    }
    


    Zmienna temp w main to całe stopnie. Nie są oddzielnie wyświetlane.

    Jeszcze tylko dla opóźnień trzeba
    #define F_CPU 8000000UL  // 8 MHz


    i działa.

Podsumowanie tematu

✨ Dyskusja dotyczy problemów z odczytem 64-bitowego kodu czujnika DS18B20 podłączonego do mikrokontrolera Atmega8 z wykorzystaniem protokołu 1-Wire i kompilatora WinAVR. Układ jest poprawnie złożony, a program działający w CodeVisionAVR poprawnie odczytuje temperaturę, jednak w WinAVR odczyt kodu zwraca osiem wartości 255. Główną przyczyną błędów okazały się niepoprawne funkcje opóźnień czasowych (delay_us), które są kluczowe dla prawidłowej komunikacji 1-Wire. Zastosowanie uproszczonej funkcji opóźniającej opartej na instrukcji asm("nop") oraz poprawienie funkcji zapisu bajtu (ow_write_byte) poprzez prawidłowe przesuwanie bitów rozwiązało problem. Dyskutowano także o minimalnej częstotliwości taktowania mikrokontrolera (minimum 2,17 MHz) dla poprawnej obsługi 1-Wire oraz o konieczności stosowania rezystora podciągającego 4,7 kΩ. Przykładowy działający kod dla Atmega8 z kwarcem 8 MHz oraz uwagi dotyczące konfiguracji LCD i obsługi magistrali 1-Wire zostały udostępnione. Wskazano również na dostępność not aplikacyjnych AVR318 dotyczących mastera 1-Wire dla AVR.
Wygenerowane przez model językowy.
REKLAMA