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

ds18b20 - brak jednego bajtu

kosa3 30 Lip 2010 22:11 5002 59
  • #1 8349138
    kosa3
    Poziom 11  
    Witam wszystkich forumowiczów dobrej woli :).

    Mój układ składa się z atmegi16 i czujnika ds18b20, całość złożona na płytce stykowej. Problem polega na tym, że przy odczycie temperatury sczytywany jest LSB a zamiast MSB otrzymuję 0 (słownie "zero"). Czujnik testowany w temperaturze poniżej zera, w temperaturze pokojowej i we wrzątku i cały czas otrzymuję jedynie wartość LSB. Układ chyba reaguje prawidłowo ponieważ wartości LSB zmieniają się wraz z temperaturą od ok 49 w temp pokojowej poprzez 17 w temp ok zera i ponad 100 w temp ok 100 stopni. Wartość MSB cały czas jest równe 0.
    Poniżej zamieszczam kod.
    Tylko proszę o wyrozumiałość. Kod bardzo kombinowany i słabo czytelny ale wierzę, że doświadczeni programiści dadzą rade:).

    Dodam jeszcze, że próbowałem z dwoma czujnikami i cały czas to samo.
    Nie przetestuje w BASCOM ponieważ nigdy w nim nie programowałem;/.


    #define F_CPU 1000000L
    #include<avr/io.h>
    #include<stdlib.h>
    #include<util/delay.h>
    //#include "lcd.h"
    
    #define WE 7
    #define PORT_1Wire PIND
    #define SET_1Wire DDRD&=~_BV(WE)
    #define CLEAR_1Wire DDRD|=_BV(WE)
    
    #define LCD PORTA
    #define E  3
    #define RS  2
    #define SET_E   LCD |= _BV(E)
    #define CLR_E   LCD &= ~_BV(E)
    #define SET_RS  LCD |= _BV(RS)
    #define CLR_RS  LCD &= ~_BV(RS)
    
    char buf[8];
    
    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(1); 
    }
    
    
    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 lcdxy(short int w, short int k)
    {
    write_command((w*64+k)|128);
    }
    
    void lcd_init(void)
    {
    _delay_ms(15);
    CLR_E; 
    CLR_RS; 
    char i;
    for(i = 0; i < 3; i++) 
      {
      SET_E; 
      LCD &= 0x3F; 
      CLR_E; 
      _delay_ms(5); 
      }
    SET_E; 
    LCD &= 0x2E; 
    CLR_E; 
    _delay_ms(1); 
    write_command(0x28); 
    write_command(0x08); 
    write_command(0x01); 
    write_command(0x06); 
    write_command(0x0C); 
    }
    
    
    unsigned char RESET_PULSE(void)
    {
    unsigned char PRESENCE;
    CLEAR_1Wire;
    _delay_us(500);
    SET_1Wire;
    _delay_us(50);
    if(bit_is_clear(PORT_1Wire,WE))
    {
    PRESENCE=0;
    }
    else
    {
    PRESENCE=1;
    }
    _delay_us(410);
    if(bit_is_set(PORT_1Wire,WE))
    {
    PRESENCE=1;
    }
    else
    {
    PRESENCE=0;
    }
    return PRESENCE;
    }
    
    void send(char bit)
    {
    CLEAR_1Wire;
    _delay_us(1);
    if(bit==1)
    SET_1Wire;
    _delay_us(60);
    SET_1Wire;  
    }
    
    unsigned char read(void)
    {
    unsigned char PRESENCE=0;
    CLEAR_1Wire;
    _delay_us(1);
    SET_1Wire;
    _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;
    }
    
    void clear_lcd(void)
    {
    int i;
    for(i=0;i<100;i++)
    {
    write_char(32);
    }
    }
    
    int main()
    {
    DDRA = 0xFF;
    PORTA = 0xFF;
    unsigned char sprawdz;
    int i=0, temp1=0, temp2=0, pom1, pom2;
    lcd_init();
    write_text("1-wire");
    _delay_ms(1000);
    clear_lcd();
    while(1)
    {
    sprawdz=RESET_PULSE();
    if(sprawdz==1)
    {
    send_byte(0xcc); 
    send_byte(0x44); 
    _delay_ms(750);
    sprawdz=RESET_PULSE();
    send_byte(0xcc); 
    send_byte(0xbe); 
    temp1=read_byte(); 
    temp2=read_byte(); 
    sprawdz=RESET_PULSE();
    int temp=0;
    temp=(temp1+(temp2*256))/16;
    //temp=temp1|pom1;
    //pom2=pom1*0,0625;
    /*int d,f,i,e,r,t,y,u;
    i=temp1;
    d=i;
    if(d>100)
    {
    f=d/100+48;
    e=(d%100)/10+48;
    r=d%10+48;
    }
    else
    {
    f=32;
    e=d/10+48;
    r=d%10+48;
    }*/
    
    char bufor1[7];
    char bufor2[7];
    
    itoa(temp1,bufor1,10);
    itoa(temp2,bufor2,10);
    //r=d%10+48;
    
    //dtostrf(temp,1,1,buf);
    _delay_ms(200);
    clear_lcd();
    lcdxy(0,0.8);
    write_text(bufor1);
    lcdxy(1,0.8);
    write_text(bufor2);
    //write_char(temp1);
    /*lcdxy(0,0.8);
    write_text("temp1=");
    write_char(f);
    write_char(e);
    write_char(r);
    
    t=temp2*256;
    y=t+49;
    u=t;
    //e=(d%10);
    lcdxy(1,0.8);
    write_text("temp2=");
    write_char(t);
    write_char(y);*/
    }
    
    else
    {
    lcdxy(0,0.8);
    write_text("   kupa:)   ");
    }
    
    }
    //return 0;
    }
    


    Pozdrawiam
    Michał
  • #2 8349800
    ksarim
    Poziom 15  
    Rzuciłem okiem na Twój kod i nie zauważyłem żadnych pomyłek. Ja osobiście nie używam funkcji delay ze standardowej biblioteki bo jakoś nie ufam jej dokładności więc może tutaj jest błąd. Dołączam Ci sprawdzoną bibliotekę do 1-Wire. Podepnij ją do Twojego projektu, ustaw czasy opóźnień dla swojego zegara i zobacz czy będzie Ci cykać. Rezystor podciągający 4,7kΩ na linii 1-Wire oczywiście dałeś.
  • #3 8350359
    kosa3
    Poziom 11  
    Ale jeśli to byłaby wina opóźnień to nie powinno być w ogóle jakiegokolwiek odczytu temperatury. Skoro młodszy bajt jest odczytywany to starszy też powinien się odczytywać nie?
    A co do twojego pliku to skąd wziąłeś plik nagłówkowy "1-Wire.h"?
    Rezystor podciągający mam 3k bo w dokumentacji czujnika piszą o jakimś zwiększonym poborze prądu. Wcześniej miałem 4,7k i było identycznie.
  • #4 8350460
    ksarim
    Poziom 15  
    Czy potrafisz ocenić poprawność tego co się odczytuje? Bo wydaje mi się, że nie. To że się coś zmienia przy zmianie temperatury to za mało. Nie odczytujesz poprawnej informacji więc wynika z tego, że komunikacja po prostu nie działa. Na 99,99% jest błąd jest w kodzie ale przynajmniej ja go na pierwszy rzut oka nie widzę :)

    Pliki, które Ci podesłałem napisałem sam i są sprawdzone w 100%. Sam je wykorzystuje do komunikacji właśnie z czujnikami DS18B20. Spróbuj z nich skorzystać. Jak uda się poprawnie odczytać temperaturę to będziesz mógł potem bezpośrednio porównać to ze swoim kodem i zlokalizować błąd, chyba że już Ci się nie będzie chciało do niego wracać.

    Powodzenia.
  • #5 8350533
    kosa3
    Poziom 11  
    Ale kolego nie wrzuciłeś pliku 1-Wire.h.
  • #6 8352478
    ksarim
    Poziom 15  
    Sorki przeoczyłem.

    Jeszcze zauważyłem jedną rzecz. Używasz funkcji _delay_ms(750) po rozkazie konwersji temperatury. Niestety nie uzyskasz takiego opóźnienia bo zgodnie z tym co jest napisane w bibliotece "delay.h":
    Cytat:
    The maximal possible delay is 262.14 ms / F_CPU in MHz.

    Możliwe, że odczytujesz same zera bo czujnik nie zdążył jeszcze przetworzyć danych. Spróbuj rozbić to funkcję na 3 x _delay_ms(250). To tak na marginesie.
  • #7 8353475
    gaskoin
    Poziom 38  
    wyciąłeś zdanie z kontekstu jak dziennikarz

    Cytat:
    When the user request delay which exceed the maximum possible one,
    _delay_ms() provides a decreased resolution functionality. In this
    mode _delay_ms() will work with a resolution of 1/10 ms, providing
    delays up to 6.5535 seconds (independent from CPU frequency). The
    user will not be informed about decreased resolution.
  • #8 8353524
    ksarim
    Poziom 15  
    Wspomniałem o tym bo wydaje mi się, że ja kiedyś właśnie z tą funkcją miałem problem. Możliwe, że mi się coś pomyliło. Najlepiej, niech autor postu sam sprawdzi, czy funkcja _delay_ms() w przybliżeniu daje poprawne opóźnienie.
  • #9 8353640
    kosa3
    Poziom 11  
    Z tym opóźnieniem to kolega ma rację. Przy moim zegarze nie da się dać większego opóźnienia jak ~262 ms.
    Wracając do tematu to w miejscach gdzie jest opóźnienie 1 us zrobiłem wstawki z assemblera, zmieniłem wszystkie sporne opóźnienia i nadal jest to samo. Nie ma jednego bajtu z wartością temperatury:(. Normalnie to jest masakra z tym moim czujnikiem.
    Wstawiłem podrzucony wyżej kod i jest jeszcze lepiej a mianowicie obydwa bajty z wartościami temperatury są równe zero...:(.
    Macie jeszcze jakieś pomysły?

    Pozdrawiam
  • #10 8355433
    skritland
    Poziom 13  
    A fusy są dobrze ustawione (zewnętrzny kwarc)?
  • #11 8355993
    kosa3
    Poziom 11  
    Fusy są w porządku. Jadę na wewnętrznym oscylatorze. Juą normalnie nie mam pomysłu na to dziadostwo
  • #12 8356239
    flapo213
    Poziom 21  
    Witaj,

    Chyba wiem gdzie leży Twój problem.

    Kiedyś robiłem eksperyment z Atmegą pracującą z zegarem 1[MHz]. No chwilę rzeźbienia z oscyloskopem zeszło, nie mówiąc już o tym że te standardowe funkcje delay to kiepsko chodziły na tak niskim zegarze. Po wielkich bojach zrobiłem to na zegarze 1[MHz].

    Proponuję abyś na początek może odpalił procka z częstotliwością 8[MHz. Niestety zmiana z wewnętrznego 1[MHz] na 8[MHz] wewnętrzny wiąże sie z ze zmianą fusebitów.

    Jeszcze jedno przychodzi mi do głowy, skoro coś odbierasz z Ds-a i nie są to 0xFF-y to może oznaczać w miarę dobrą czasówkę. Wytłumaczenie jest proste jeśli byś miał złą czasówkę to z układu odebrałbyś jedynie 0xFF-y czyli tak naprawdę nic. Skoro DS zinterpretował komendę poprawnie to spróbuj się zastanowić może masz złą procedurę odczytu i składania bajtów z DS-a. Wysłać komendę to nie problem, prawdziwym problemem jest dobra implementacja i odbiór danych z DS-a.

    Pozdrawiam
  • #13 8356686
    kosa3
    Poziom 11  
    Najlepsze jest to, że gdy testowałem "działające" kody, które znalazłem gdzieś na necie, to właśnie dostawałem pełną temperaturę 0xffff. Gdy przerabiałem te kody jako LSB dostawałem jakąś wartość która nawet się zmieniała pod wpływem temperatury, a MSB jest ciągle zero.
    Kurde nie mam teraz oscylatora mniejszego niż 20 Mhz żeby przetestować atmegę na zewnętrznym zegarze.
    Składanie bajtów nie gra dla mnie w tej chwili roli. Odczytuję obydwa bajty osobno i je wyświetlam i cały czas jest ten sam problem.
    Jeszcze po raz setny sprawdzę procedury odczytu bitu i bajtu bo może tam, tak jak mówisz, leży problem.
    Czekam na dalsze pomysły.

    Pozdrawiam
  • #14 8356713
    flapo213
    Poziom 21  
    Ok to chyba już wiem co dalej trzeba zrobić.

    Ponieważ trudno wnioskować cokolwiek po odczycie temperatury 0xFFFF bo jest to kodowane w U2 i nic nie wymyślisz z tym więc spróbuj coś prostszego na początek odczytaj numer seryjny bodajże komenda 0x33 ale nie pamiętam dokładnie więc spójrz w dokumentację. Jeśli tym razem otrzymasz same 0xFF to oznaczać to będzie błąd transmisji bitowej najniższego poziomu innymi słowy czasówka do bani.

    Nie potrzebujesz zewnętrznego kwarca poprostu zmień fuse bity na wewnętrzny 8MHz to przecież można zrobić.

    Jeśli piszesz soft w Windowsie to zainstaluj sobie symulator avrstudio (softwarowy symulator) wyłącz wszelkie przerwania ustaw dobrze zegar w avrstudio bo domyślnie chyba jest 4MHz więc zmień go na 8MHz i sprawdzaj ile zajmują twoje funkcje opóźniające.

    Po odczycie wartość jakie podałeś wnioskuję jednak że masz błędną transmisję niskopoziomową.

    Pozdrawiam
  • #15 8356789
    kosa3
    Poziom 11  
    A więc kolego odczytałem numer seryjny i wyświetla mi, po konwersji wyniku na system dziesiętnym, 16. Czy to jest poprawnie czy znowu jakaś bzdura?
  • #16 8356868
    flapo213
    Poziom 21  
    Witaj,

    numer seryjny to jest 8 bajtów. Numer ten zawiera kod rodziny urządzenia, unikalny numer seryjny urządzenia i sumę kontrolną. Musisz odebrać 8 bajtów. Spójrz w dokumentację w sekcję dotyczącą składni numeru seryjnego.

    Wyświetl wszystkie 8 bajtów. Zobaczymy co z tym dalej.

    PS.

    Używając komendy 0x33 musi być podłączony tylko i wyłącznie jeden czujnik tylko jedno urządzenie one wire przy takim testowaniu.

    Nie wiem jaki jest stopień Twojej znajomości języka angielskiego ale w datasheet do tego układu naprawde jest wszystko bardzo jasno opisane.

    Pozdrawiam
  • #17 8356937
    kosa3
    Poziom 11  
    Kolego podziwiam twoją cierpliwość :).

    Więc otrzymałem takie oto liczby: 16, 197, 244, 1, 2, 8, 0, 128.
    Czy możesz to jakoś zweryfikować?
  • #18 8357525
    piti___
    Poziom 23  
    Czytasz jakieś śmieci. Jeden z bajtów powinien być równy 40 (0x28) jest to "DS18B20’s 1-Wire family code".
  • #19 8357946
    kosa3
    Poziom 11  
    Już nie wiem gdzie może być błąd. Jestem chyba ślepy. Koledzy pomóżcie znaleźć błąd. Pewnie gdzieś babol tkwi w funkcji resetu lub odczytu.
    Wrzucam funkcje które to realizują.
    
    unsigned char RESET_PULSE(void)
    {
    	unsigned char PRESENCE;
    	CLEAR_1Wire;
    	delayus(500);
    	SET_1Wire;
    	delayus(30);
    	if(bit_is_clear(PORT_1Wire, WE))
    	{
    		PRESENCE=1;
    	}
    	else
    	{
    		PRESENCE=0;
    	}
    	delayus(470);
    	if(bit_is_set(PORT_1Wire, WE))
    	{
    		PRESENCE=1;
    	}
    	else
    	{
    	PRESENCE=0;
    	}
    	return PRESENCE;
    }
    
    unsigned char read(void)
    {
    	unsigned char PRESENCE=0;
    	CLEAR_1Wire;
    	delayus(2);
    	SET_1Wire;
    	delayus(15);
    	if(bit_is_set(PORT_1Wire, WE))
    	{
    		PRESENCE=1;
    	}
    	else
    	{
    		PRESENCE=0;
    	}
    	return PRESENCE;
    }
    


    Dodam, że zmieniłem zegar na wewnętrzny 8 MHz.
  • #20 8358787
    gaskoin
    Poziom 38  
    sprawdź czy reset działa, jak urządzenie jest wykrywane, reset powinien zwrócić zero.

    Sprawdź to zapaleniem jakiejś diody, albo po RSie się podepnij.
  • #21 8358840
    kosa3
    Poziom 11  
    Kolego spójrz w kod resetu. Jeśli urządzenie jest wykrywane reset zwraca 1. Czujka jest wykrywana bo w funkcji głównej jest pętla warunkowa. Gdy reset zwraca 0, czyli czujnik nie jest wykrywany, na LCD wyświetlam napis.
    Nie mam teraz dojścia do oscyloskopu żeby sprawdzić jak działają timingi.

    Czekam na kolejne pomysły i dziękuje za dotychczasowe zainteresowanie:)

    Pozdrawiam
  • #22 8359251
    gaskoin
    Poziom 38  
    kosa3 napisał:
    Kolego spójrz w kod resetu. Jeśli urządzenie jest wykrywane reset zwraca 1. Czujka jest wykrywana bo w funkcji głównej jest pętla warunkowa. Gdy reset zwraca 0, czyli czujnik nie jest wykrywany


    if(bit_is_set(PORT_1Wire, WE))
       {
          PRESENCE=1;
       }
       else
       {
          PRESENCE=0;
       } 



    Cytat:
    During the initialization sequence the bus master transmits (TX) the reset pulse by pulling the 1-Wire bus
    low for a minimum of 480μs. The bus master then releases the bus and goes into receive mode (RX).
    When the bus is released, the 5kΩ pullup resistor pulls the 1-Wire bus high. When the DS18B20 detects
    this rising edge, it waits 15μs to 60μs and then transmits a presence pulse by pulling the 1-Wire bus low
    for 60μs to 240μs.


    Sprawdź to, o co prosiłem "Kolego" zamiast tworzyć nowe teorie :)


    //ehh źle spojrzałem, czas na sen, poza tym na odwrót napisałeś, zwracam należyty honor
  • #23 8359290
    kosa3
    Poziom 11  
    Po pierwsze primo nie wiem dlaczego piszesz "Kolego" (chodzi mi o cudzysłów) :).
    Po drugie wybaczam Ci ;).
    Już chciałem ripostować tym, że nie przejrzałeś całej dokumentacji. Ale ok. Jutro spróbuje napisać w bascomie żeby sprawdzić ten czujnik.
    Mam nadzieje, że ktoś znajdzie błąd w wyżej pokazanym kodzie.

    Pozdrawiam
  • Pomocny post
    #24 8359616
    flapo213
    Poziom 21  
    Witam,

    Wstrzymaj się z bascomem na razie.

    Wynik jaki otrzymałeś tzn:

    Cytat:
    16, 197, 244, 1, 2, 8, 0, 128.


    nie dokońca jest zły. Koledzy słusznie Ci napisali że pierwszy bajt jak powinieneś otrzymać powinien być 0x28 ale w przypadku użycia 12 bitowego czujnika DS18B20 a Ty odebrałeś 0x10 w Hexie więc ja wyrokuję że Ty poprostu korzystasz z czujnika DS18S20 którego family code jest 0x10 (16 dec).

    Z tego faktu jasno wynika że czasówki są dobre, bajt również interpretujesz poprawnie.

    No to w tym momencie powinieneś już mieć możliwość odczytu temperatury.

    Spróbuj podać teraz jakie są wartości z tego czujnika po konwersji. W dokumentacji jest napisane że po wydaniu komendy SCRACHPAD 0xBE musisz odebrać 9 bajtów gdzie 9 bajt jest bajtem sumy CRC. W przypadku odbioru jedynie 2 bajtów układ po wysłaniu jakiejkolwiek komendy może próbować jeszcze odsyłać Ci resztę bajtów informacyjnych komendy SCRACHPAD.

    Nie poddawaj się i powodzenia.
  • #25 8360183
    kosa3
    Poziom 11  
    Normalnie nie wiem co mam powiedzieć. Jestem idiotą bo te czujniki to rzeczywiście ds18s20. Nie spojrzałem na czujnik a jedynie na opakowanie i fakturę. Sprzedawca na fakturze napisał ds18b20, to samo na opakowaniu i takie czujniki właśnie zamawiałem. Zaraz do nich zadzwonię i ich zje...
    Ale rzecz jasna to moje przeoczenie. Mea culpa:(.

    Pozdrawiam i dziękuje za zainteresowanie moim problemem.
  • #26 8361732
    gaskoin
    Poziom 38  
    no tak, z tego co pamiętam to w tym czujniku drugi bajt jest bajtem znaku (nie ma tam temperatury) :)
  • #27 8361928
    kosa3
    Poziom 11  
    Tak dokładnie, drugi odczytywany bajt jest bajtem znaku. Aż dziw bierze, że cała procedura odczytu jest identyczna jak dla ds18b20 a w kodzie który zamieściłem u góry wystarczy jedynie zmienić:
    
    temp=(float)(temp1+(temp2*256))/16
    na
    temp=(float)(temp1+(temp2*256))/2
  • #29 8363732
    kosa3
    Poziom 11  
    Przy przesunięciu bitowym w lewo zgodzę się z Tobą. Jednak w wyniku przesunięcia bitowego w prawo (dzielenie) otrzymam liczbę całkowitą co zmniejsza dokładność czujnika.
  • #30 8365540
    gaskoin
    Poziom 38  
    i tak jest mało dokładny :)
REKLAMA