Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

AT90S2313 8MHz plus 18B20

smajlas 16 Jun 2007 19:50 1689 10
  • #1
    smajlas
    Level 12  
    Witam!
    Przeczytałem już chyba wszystko w temacie, niestety nie daję rady.
    Chcę wyświetlić temperaturę (na razie tylko dodatnią) na dwóch wyświetlaczach siedmiosegmentowych (wspólne anody, przełączane z portu D3 i D4), no i niestety, same głupoty są wyświetlane (dziwne, bo w tablicy znaków nie ma odpowiedników dla takich cudów, które są u mnie... czyżby d1, lub d2 były takimi wielkimi liczbami, że wyszedłem poza obszar tablicy??)
    Z tego co mi wiadomo, bardzo ważną sprawą są odpowiednie czasy w których próbkujemy/podajemy stany dla DS-a. Sprawdziłem to oscyloskopem, i wiem, że w moim przypadku pętla "delay(1)" będzie trwać 600ns, a "waitms(1)" 420mikrosekund.
    Listing:
    Code:
    //Timingi niby poprawione :/
    

    #include <avr/io.h>

    #include <avr/interrupt.h>


    #define DQ 0   //numer portu, który służy jako 1-wire
    #define OSC 8    //częstotliwość zegara w MHz
    #define SET_DQ DDRD &= ~_BV(DQ)   //ustawienie portu D0 w stan niski, czyli jako WE,
                            //co zwalnia linię 1-w, rezystor podciąga do 1,
                            //wymagany brak podciągnięcia do 1
    #define CLR_DQ DDRD |= _BV(DQ)  //ustawienie portu D0 w stan wysoki, czyli jako WY,
                            //czyli 1-w będzie niski
                            //(nie zapomnij zdefiniować PORTD PIN0 jako bez podciągnięcia)
                            //np. PORTD &=~_BV(0);

    #define IN_DQ PIND & _BV(DQ)

    char znaki[] = {192,249,164,176,153,146,130,248,128,144,136,131,198,161,134,142,220,158,157,188,181,179,241,185};
    //dłuższa tablica, dla dodatkowych znaków, może tu trafiamy...

    char wysw = 0;
    unsigned char d1, d2;
    // procedura obsługi przerwania od timera TC0
    SIGNAL (SIG_OVERFLOW0)
    {
    TCNT0 = 250;       // załadowanie do licznika wartości początkowej
    PORTD &= ~_BV(4);    // wygaszenie wszystkich cyfr wyświetlacza
    PORTD &= ~_BV(3);
    PORTB = 0xFF;
    wysw++;          // zwiększenie o 1 zmiennej wysw

    switch(wysw)      // w zależności od wartości wysw włącz odpowiednią cyfrę na wyświetlaczu
    {
       case 1:
       {
          PORTD |= _BV(3);       // włącz pierwszą cyfrę wyświetlcza (tu są jednostki - wyświetlacz bardziej na prawo)
          PORTB = *(znaki + d2); // na wyświetlaczu wyświetl odpowiedni znak pobrany z tablicy znaków
          break;               // opuść instrukcję switch
       }
       case 2:
       {
          PORTD |= _BV(4);       // włącz drugą cyfrę wyświetlcza (tu są dziesiątki - wyświetlacz bardziej na lewo)
          PORTB = *(znaki + d1); // na wyświetlaczu wyświetl odpowiedni znak pobrany z tablicy znaków
          wysw = 0;
          break;             // opuść instrukcję switch
       }
    }
    }
    //procedura opóźniająca o 420us, gdy p==1 i OSC=8
    void waitms(unsigned char p)
    {
    unsigned char a,b; //zmienne licznikowe
    for(;p>0;--p) //ta pętla do wykonania p razy
       for(b=10;b>0;--b) //ta 10 razy
          for(a=10*OSC;a>0;--a) //ta petla 100 razy
          __asm("nop"); //dodatkowe opóźnienie o 1 cykl
    }

    // procedura opóźniająca o (5*t)* 0,125 us dla 8MHz kwarcu, tutaj jest 600ns (mierzone oscyloskopem)
    void delay(unsigned char t)
    {
    do
       {
       asm("nop");
       }
       while(--t);
    }
    // procedura reset
    void ow_reset(void)
    {
    CLR_DQ; // stan niski na linii 1wire
    delay(200);   //120us
    delay(200);   //120us
    delay(200);   //120us
    delay(250);   //140us razem ok 500us lub wiecej
    SET_DQ;// stan wysoki na linii 1wire
    delay(200); //
    delay(200);
    delay(200);   //120us
    delay(250);   //140us, razem ok 500us
    }



    void ow_write_bit(char b)      // procedura zapisu bitu na linię 1wire
    {
       cli();       // zablokowanie przerwań
       CLR_DQ;    // MASTER robi stan niski na linii 1wire
       delay(2);    // opóźnienie 1.2us - inicjalizacja slotu zapisu
       if(b) SET_DQ; // jeśli parametr jest niezerowy to ustaw stan wysoki na linii
       delay(108);    // opóźnienie 64us
       SET_DQ;    // MASTER odpuszcza, więc stan wysoki na linii 1wire
       delay(2);   //recovery -co najmniej 1us, mam 1.2us
       sei();       // odblokowanie przerwań
    }
    void ow_write_byte(char val)    //zapis całego bajta do DS18B20
    {
       unsigned char i;
       unsigned char zm;
       for (i=0; i<8; i++)      //bajt = 8xbit
       {
          zm = val >> i;
          zm &= 0x01;
          ow_write_bit(zm);
       }
       //delay(4);
    }
    char ow_read_bit(void)      //odczyt bita informacji z DS-a
    {
       cli();
       CLR_DQ;     //1-w stan niski
       delay(2);   //nie mniej niż 1us, mamy 1.2us
       SET_DQ;      //port 1-w jest wejściem, można czytać, co DS dał na linię
                //(przy okazji mamy zwolnienie linii, więc stan wysoki po skończeniu slotu)
       delay(20);      //czekaj 12us
       if(IN_DQ) return 1; else return 0; //odczyt
       delay(80);   //48us (cały slot co najmniej 60us, mam 61us)
       delay(2);  //recovery - 1us
       sei();
       
    }

    unsigned char ow_read_byte(void)   //odczyt całego bajta
    {
       unsigned char i;
       unsigned char value = 0;
       for (i=0;i<8;i++)
       {
          if(ow_read_bit())   //jak zczytana jedynka
          value|=0x01<<i;      //to wpisuj jedynkę i przesuwaj w lewo całość
          // a co jak odczytane zero??
       }
       return(value);
    }


    //---------------------------------------------------------------------------
    int main(void)
    {
    char msb, lsb, temp;
    TCCR0 = 0x04;      //preskaler 100, czyli
    TCNT0 = 250;      // wpisanie wartości początkowej
    TIMSK = 0x02;      // odblokowanie przerwania od licznika
    sei();      // globalne odblokowanie przerwań

    DDRB = 0xFF;   //wszystkie B WY
    DDRD |= _BV(6);      //port D6 WY (kontrola stan niski-wysoki)
    DDRD |= _BV(4);      //port D4 wyjście (wyświetlacz 1 wspólna Anoda +)
    DDRD |= _BV(3);      //port D3 wyjście (wyświetlacz 2 wspólna Anoda +)
    PORTD |= _BV(6);   //D6 stan wysoki
    PORTD &= ~_BV(5);   //d5 stan niski
    PORTD &= ~_BV(0);   //D0 niepodciągnięty (1-wire)

    while(1)
    {
    ow_reset();
    ow_write_byte(0xCC);      //CC - przeskocz skanowanie ROM, wysyłanie seriala itd
    ow_write_byte(0x44);      //44 - konwertuj temperaturę
    waitms(250);   //105ms na konwersję potrzeba ok 750ms, kiedy max rozdzielczość
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);



    ow_reset();
    ow_write_byte(0xCC);
    ow_write_byte(0xBE);
    lsb = ow_read_byte();
    msb = ow_read_byte();
    //                                        |---MSB---|---LSB---|
    //                                        |znak  |calk   |części
    //organizacja 12-bitowej temperatury: np. +0.5stopnia 0000 0000 0000 1000
    //                              np. +10.125 st  0000 0000 1010 0010
    //                             np. +25.0625 st 0000 0001 1001 0001

    lsb = lsb >> 4;      //przesuń lsb o 4 miejsca w prawo (lsb - bajt; normalnie 4bity temp całkowite stopnie i 4 po przecinku)
    msb &= 0x07;       //z-anduj msb i 0111 i przypisz do msb (msb - bajt; 5 starszych bitów - znak plus 3 bity liczby całkowitej)
    msb = msb << 4;      //przesuń msb o 4 miejsca w lewo
    temp = lsb | msb;
    d2 = temp / 10;         //podziel temp na 10 i umieść w d2
    d1 = temp % 10;         //resztę z dzielenia daj do d1
    }
    return 0;
    }


    Gdzie popełniłem błąd? Zaznaczam, że czasy zapis/odczyt/reset pochodzą z datasheet-a dla DS18B20 Dallas.

    Inna sprawa:
    Wielu zaawansowanych użytkowników proponuje zrobić wszystkie czasy za pomocą przerwań. Chętnie to zrobiłbym, ale jak to zrealizować? Co ma robić procek w czasie pomiędzy przerwaniami??
  • #2
    zumek
    Level 39  
    smajlas wrote:
    Gdzie popełniłem błąd?

    Ba , żeby tylko jeden ;) Twój kod jest naszpikowany błędami , "jak dobra kasza skwarkami" :D
    Code:

    // procedura reset
    void ow_reset(void)
    {
    CLR_DQ; // stan niski na linii 1wire
    delay(200);   //120us
    delay(200);   //120us
    delay(200);   //120us
    delay(250);   //140us razem ok 500us lub wiecej
    SET_DQ;// stan wysoki na linii 1wire
    delay(200); //
    delay(200);
    delay(200);   //120us
    delay(250);   //140us, razem ok 500us
    }

    Dlaczego ta procedura , nie zwraca wartości PRESENCEPULSE :?:
    Zakładasz , że Twój DS zawsze odpowie :?:

    Code:

    char ow_read_bit(void)      //odczyt bita informacji z DS-a
    {
       cli();
       CLR_DQ;     //1-w stan niski
       delay(2);   //nie mniej niż 1us, mamy 1.2us
       SET_DQ;      //port 1-w jest wejściem, można czytać, co DS dał na linię
                //(przy okazji mamy zwolnienie linii, więc stan wysoki po skończeniu slotu)
       delay(20);      //czekaj 12us
    /*------------------------------------------------------------*/
       if(IN_DQ) return 1; else return 0;//odczyt

    Koniec procedury , a przerwania wyłączone :D
    Code:

       delay(80);   //48us (cały slot co najmniej 60us, mam 61us)
       delay(2);  //recovery - 1us
       sei();
    }

    Powyższe , w tej postaci zbędne , bo się nigdy nie wykona :(
    Narazie tyle ;)
    ... jeszcze jedno - dlaczego odświeżasz wyświetlacze > 6kHz :?:
    120Hz dla dwóch , spoko wystarczy.

    Piotrek
  • #3
    smajlas
    Level 12  
    Dobra poprawiłem to odświeżanie (fiu, rzeczywiście, ale to dla spokoju oczu, mniej mruga, mniej się zużywają, hihi) ;)
    teraz
    Code:
    TCNT0 = 1;      // wpisanie wartości początkowej

    i wywaliłem zbędne
    Code:
       delay(108);    // opóźnienie 64us
    
       SET_DQ;    // MASTER odpuszcza, więc stan wysoki na linii 1wire
       delay(2);   //recovery -co najmniej 1us, mam 1.2us
    z kawałka
    Code:
    void ow_write_bit(char b)      // procedura zapisu bitu na linię 1wire
    
    {
       cli();       // zablokowanie przerwań
       CLR_DQ;    // MASTER robi stan niski na linii 1wire
       delay(2);    // opóźnienie 1.2us - inicjalizacja slotu zapisu
       if(b) SET_DQ; // jeśli parametr jest niezerowy to ustaw stan wysoki na linii
       delay(108);    // opóźnienie 64us
       SET_DQ;    // MASTER odpuszcza, więc stan wysoki na linii 1wire
       delay(2);   //recovery -co najmniej 1us, mam 1.2us
       sei();       // odblokowanie przerwań
    }

    Niestety, skwarki okazały się być zbyt małe, żeby zaspokoić głód...
    śmieci jak były, tak są. Jutro dopracuję kwestię presence pulse.
    Z drugiej strony, byłem ciekaw, czy rzeczywiście DS odpowiada, dlatego zaaplikowałem mu (po wcześniejszych niepowodzeniach) taki kawałek:
    Code:

    // port D0 jest portem 1-wire, do niego podpięty został czujnik temperatury DS18B20
    #include <avr/io.h>
    #include <avr/interrupt.h>

    char z,a,b;

    SIGNAL(SIG_OVERFLOW0)
    {
       
       z=z+1;
       switch (z)
       {
          case 1:            //tu będzie już 15us (na wszelki wypadek, niech się wszystko uspokoi)
          {
             PORTD &=0b01000110;         //wymuszenie stanu niskiego na linii 1-wire (D0)
             TCNT0=0x05;               //od nowa 250us
             break;
          }
          case 2:         //od ściągnięcia 1-wire ułynęło 250us
          {
             TCNT0=0x05;         //250us
             if(PIND&1)         //warunek spełniony jak 1-wire ma stan różny od zera (wysoki)
             PORTB|=0x01;      //jak 1-wire wysoki to zaświeć diodę na B0 (dioda 1)(zgaszona 28maj07)
             break;
          }
          case 3:         //od ściągnięcia 1-wire upłynęło już 500 us
          {               //ZMIENIAMY KIERUNEK DZIAŁANIA PORTU 1-WIRE
             DDRD=0x7E;         //port D0 jest teraz WEjściem niepodciągniętym, czyli na 1-wire pojawi się 1, bo jest zewnętrznie podciągnięta rezystorem
             PORTD=0b01000110;         //nie chcemy podciągnięcia wewnętrznego 0100 0110,  a wartość 7e to 0111 1110
             TCNT0=0xF5;         //teraz załaduj EF, żeby licznik odmierzył 10us (FF-0A)
             
             break;
          }
          case 4:         //od zmiany na 1-wire upłynęło 10us, teraz rezystor podciąga linię 1-wire do stanu wysokiego
          {
             if(PIND&1)         //jeśli 1-wire ma stan wysoki
             PORTB|=0x02;      //zaświeć diodę na B1 (dioda 2) (swieci-28maj07)
             TCNT0=0xE1;         //nastepne przerwanie za 30us
             break;
          }
          case 5:            //od zmiany na 1-wire upłynęło już 40us
          {
             if(PIND&1)         //jeśli 1-wire ma stan wysoki
             PORTB|=0x04;      //zaświeć diodę na B2 (dioda 3) (zgaszona-28maj)
             TCNT0=0xE6;         //następne przerwanie za 25us
             break;
          }
          case 6:               //od zmiany 65us
          {
             if(PIND&1)         //jeśli 1-wire ma stan wysoki
             PORTB|=0x08;      //zaświeć diodę na B3 (dioda 4) (zgaszona-28maj)
             TCNT0=0xE6;         //następne przerwanie za 25us
             break;
          }
          case 7:               //od zmiany 90us
          {
             if(PIND&1)         //jeśli 1-wire ma stan wysoki
             PORTB|=0x10;      //zaświeć diodę na B4 (dioda 5) (zgaszona-28maj)
             TCNT0=0xCD;         //następne przerwanie za 50us
             break;
          }
          case 8:               //od zmiany 140us
          {
             if(PIND&1)         //jeśli 1-wire ma stan wysoki
             PORTB|=0x20;      //zaświeć diodę na B5 (dioda 6) (swieci-28maj)
             TCNT0=0x4B;         //następne przerwanie za 180us
             break;
          }
          case 9:            //od zmiany 320us
          {
             if(PIND&1)         //jeśli 1-wire ma stan wysoki
             PORTB|=0x40;      //zaświeć diodę na B6 (dioda 7) (swieci-28maj)
             TCCR0=0x00;         //zatrzymaj licznik TC0
             z=0;
             break;
          }
       }
    }

    int main(void)
    {
    DDRB = 0xFF;   //wszystko WY
    DDRD = 0x7F;   //wszystko WY
    PORTB=0x00;      //wszystko do 0 podciągnięte
    PORTD=0b01000111;    // d6, D2 i D1 D0 do jedynek, reszta OC
    TCCR0=0x02;         //0x02 to 010, czyli CK/8 co nam daje flagę TOV0 co 256us jak licznik liczy od zera
    TCNT0=0xF0;         //na początek do licznika wpisujemy 0xF0 (czyli 240), co daje nam przepełnienie licznika po 15us
    z=0;
    sei();            //WAŻNE!!!!!!!!!!
    TIMSK=0x02;       //odblokowanie przerwania lokalnego licznika TC0  WAŻNE!!!!!!!
    while(1)
    {
       if(bit_is_clear(PIND,PD1))      //do D1 podpięty jest przycisk, jak zostanie naciśnięty, to
       {
       TCCR0=0x02;                  //wystartuj od nowa licznik TC0
       TCNT0=0x10;                  //niech odlicza 240us
       DDRD=0x7F;                  // D0 jest WYjściem (0111 1111)
       PORTD = 0b01000110;            //ustaw D6 w stan wysoki
       PORTB=0x00;
       z=0;
       }
       
       
    }
    return (0);
    }
    /*
    28 maja 2007 po pomiarze oscyloskopem okazało się, że timing jest następujący
           ______                ____________________
    ______|      |______________|               
          |-30us-|-----100us----|
       B0  B1      B2  B3  B4     B5            B6   - to są czasy, kiedy które testowanie się odbywa

    No i odpowiedział jak należy... :)
  • #4
    zumek
    Level 39  
    smajlas wrote:
    Dobra poprawiłem to odświeżanie (fiu, rzeczywiście, ale to dla spokoju oczu, mniej mruga, mniej się zużywają, hihi) ;)
    teraz
    Code:
    TCNT0 = 1;      // wpisanie wartości początkowej

    A po co przeładowywać licznik :?:
    8MHz/256(preskaler)/256(pojemność licznika)=~122 Hz i to wystarczy.
    smajlas wrote:

    i wywaliłem zbędne
    Code:
       delay(108);    // opóźnienie 64us
    
       SET_DQ;    // MASTER odpuszcza, więc stan wysoki na linii 1wire
       delay(2);   //recovery -co najmniej 1us, mam 1.2us

    Ja pisałem o funkcji char ow_read_bit() i nie napisałem że to wogóle jest zbędne , tylko że w takiej postaci jak Ty to napisałeś ;)
    Myślałem o czymś takim:
    Code:

    char ow_read_bit(void)      //odczyt bita informacji z DS-a
    {
       char return_bit;
       cli();
       CLR_DQ;     //1-w stan niski
       delay(2);   //nie mniej niż 1us, mamy 1.2us
       SET_DQ;      //port 1-w jest wejściem, można czytać, co DS dał na linię
                //(przy okazji mamy zwolnienie linii, więc stan wysoki po skończeniu slotu)
       delay(20);      //czekaj 12us
       /*   if(IN_DQ) return 1; else return 0; //odczyt  */
       return_bit=IN_DQ;
       delay(80);   //48us (cały slot co najmniej 60us, mam 61us)
       delay(2);  //recovery - 1us
       sei();
       return return_bit;   //gdy odczytany bit =0 zwraca 0 , a dla 1 zwraca !=0
    }


    Piotrek
  • #5
    smajlas
    Level 12  
    No tak,
    Code:
    return return_bit
    jest znacznie bardziej elegancki.
    Przestałem przeładowywać licznik (ale to wpisywanie wartości początkowej, to po prostu brak doświadczenia - myślałem, że zawsze 3eba)
    Poza tym zrealizowałem funkcję presence:
    Code:
    // procedura reset
    
    char ow_reset(void)
    {
    char presence;
    CLR_DQ; // stan niski na linii 1wire
    delay(200);   //120us
    delay(200);   //120us
    delay(200);   //120us
    delay(250);   //140us razem ok 500us lub wiecej
    SET_DQ;// stan wysoki na linii 1wire

    delay(100); //po zwolnieniu linii czekaj 60us
    presence=~IN_DQ; //tu ma być stan niski, jak DS obecny, więc trzeba zrobić jedynkę
    delay(200);
    delay(200);   //120us
    delay(250);   //140us, razem ok 500us
    return presence;
    }

    a w main()
    Code:
    while(1)
    
    {
    PORTD &=~_BV(6);   //zresetowanie stanu portu D6
    if(ow_reset())
    {
    PORTD |=_BV(6); //jeśli wystąpił presence pulse pokaż na diodach
    ow_write_byte(0xCC);      //CC - przeskocz skanowanie ROM, wysyłanie seriala itd
    ow_write_byte(0x44);      //44 - konwertuj temperaturę

    waitms(250);   //105ms na konwersję potrzeba ok 750ms, kiedy max rozdzielczość
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    }


    if(ow_reset())
    {
    ow_write_byte(0xCC);
    ow_write_byte(0xBE);
    }
    lsb = ow_read_byte();
    msb = ow_read_byte();


    Teraz co mam:
    presence pulse obecny - na diodach krótkie mrugnięcia uspakajają mnie co do DSa.
    Jestem już w pół drogi: mam pokazaną temperaturę w zakresie od 33 do 39 stopni. Powyżej pokazuje mi np: dla 40st - 0A, 41st - 06, 42 -08, niestety, powyżej na jednym wyśw zero, na drugim: śmieć. Poniżej 32 stopni 0 i coś co wygląda jak J...

    Coś jednak nie tak: Mam przełącznik do odłączania DS-a od portu D0, po przełączeniu w OFF, diodka od potwierdzania obecności dalej mruga.... :(, ale na wyświetlaczach 00 (więc tu chyba ok)
  • #6
    zumek
    Level 39  
    smajlas wrote:
    ...Coś jednak nie tak: Mam przełącznik do odłączania DS-a od portu D0, po przełączeniu w OFF, diodka od potwierdzania obecności dalej mruga.... :(, ale na wyświetlaczach 00 (więc tu chyba ok)

    A czy ten przełącznik , nie odłącza również rezystora podciągającego 1wire od VCC :?: Po odłączeniu od magistrali DS-a , a pozostawieniu zewnętrznego pull-up'a , powinno odczytywać się z magistrali wyłącznie jedynki , więc skąd 00 na wyświetlaczu :?:
    Dziwne :|

    Piotrek

    PS
    Sposób w jaki obliczasz odczytaną temperaturę , też mi sie wielce nie podoba , bo wykonujesz przesunięcie w prawo na typie signed char , a to nie to samo co na unsigned char .Poza tym ,"obcinasz" o 1 bit za dużo z msb :(
  • #7
    smajlas
    Level 12  
    Ach, słuszne zauważenie. Odłączałem również rezystor, było bez podciągnięcia, więc zera :) No i diodka mrugała, teraz wskazuje prawidłowo :)
    Dzięki zumek!
    Jednak nadal pozostaje kwestia tych dziwactw... dlaczego zamiast 40st mam 0A ? czyżby to dzielenie temp/10 i temp%10 było nieakuratne?

    Jak to za dużo obcinam? Czy nie jest tak, że 4 bity z lsb to "niższa" część temperatury, a 3 z msb to "wyższa" część?

    A jeśli inaczej liczyć, to jak? Zaraz nad tym pomyślę...
  • Helpful post
    #8
    zumek
    Level 39  
    smajlas wrote:
    ...Jednak nadal pozostaje kwestia tych dziwactw... dlaczego zamiast 40st mam 0A ? czyżby to dzielenie temp/10 i temp%10 było nieakuratne?

    W ostatnim poście napisałem:
    Quote:

    PS
    Sposób w jaki obliczasz odczytaną temperaturę , też mi sie wielce nie podoba , bo wykonujesz przesunięcie w prawo na typie signed char , a to nie to samo co na unsigned char ... .

    Skoro "nie dotarło" :D , to podaję rozwiązanie
    Jest:
    Code:

    char msb, lsb, temp;
    /* .... */
    lsb = lsb >> 4;      //przesuń lsb o 4 miejsca w prawo (lsb - bajt; normalnie 4bity temp całkowite stopnie i 4 po przecinku)
    //...

    Powinno/ma być:
    Code:

    unsigned char msb, lsb, temp;
    /* ... */
    lsb = lsb >> 4;      //przesuń lsb o 4 miejsca w prawo (lsb - bajt; normalnie 4bity temp całkowite stopnie i 4 po przecinku)
    //...

    A Ty zastanów się , dlaczego tak ;)

    Piotrek
  • #9
    smajlas
    Level 12  
    YYess!!
    Działa, i do tego dobrze działa! Mogę otwierać szampana :D

    Dzięki zumek!! Dzięki!!

    Problem zrozumiałem. Używanie signed char jest sporym błędem, bo najbardziej znaczący bit robi liczbę ujemną i to o wartości zupełnie innej :) (uzupełnienie do 2, teraz już jestem mądrzejszy.. hihi)
    Tylko żeby do tego dojść, trzeba było poszukać - a nie chciało mi się... Lenistwo nie popłaca...
  • #10
    me_super
    Level 29  
    smajlas wrote:
    YYess!!
    Działa, i do tego dobrze działa! Mogę otwierać szampana :D



    Zamieść działające źródło tutaj -- sam walczyłem z tym kodem i nie mogłem sobie poradzić :-)
  • #11
    smajlas
    Level 12  
    Quote:
    Zamieść działające źródło tutaj -- sam walczyłem z tym kodem i nie mogłem sobie poradzić

    Voila!
    Code:
    //Timingi poprawione, błędy usunięte
    

    #include <avr/io.h>

    #include <avr/interrupt.h>


    #define DQ 0   //numer portu, który służy jako 1-wire
    #define OSC 8    //częstotliwość zegara w MHz
    #define SET_DQ DDRD &= ~_BV(DQ)   //ustawienie portu D0 w stan niski, czyli jako WE,
                            //co zwalnia linię 1-w, rezystor podciąga do 1,
                            //wymagany brak podciągnięcia do 1
    #define CLR_DQ DDRD |= _BV(DQ)  //ustawienie portu D0 w stan wysoki, czyli jako WY,
                            //czyli 1-w będzie niski
                            //(nie zapomnij zdefiniować PORTD PIN0 jako bez podciągnięcia)
                            //np. PORTD &=~_BV(0);

    #define IN_DQ PIND & _BV(DQ)

    char znaki[] = {192,249,164,176,153,146,130,248,128,144,136,131,198,161,134,142,220,158,157,188,181,179,241,185};
    //dłuższa tablica, dla dodatkowych znaków, może tu trafiamy...

    char wysw = 0;
    unsigned char d1, d2;
    // procedura obsługi przerwania od timera TC0
    SIGNAL (SIG_OVERFLOW0)
    {
    //TCNT0 = 1;       // załadowanie do licznika wartości początkowej
    PORTD &= ~_BV(4);    // wygaszenie wszystkich cyfr wyświetlacza
    PORTD &= ~_BV(3);
    PORTB = 0xFF;
    wysw++;          // zwiększenie o 1 zmiennej wysw

    switch(wysw)      // w zależności od wartości wysw włącz odpowiednią cyfrę na wyświetlaczu
    {
       case 1:
       {
          PORTD |= _BV(3);       // włącz pierwszą cyfrę wyświetlcza (tu są jednostki - wyświetlacz bardziej na prawo)
          PORTB = *(znaki + d2); // na wyświetlaczu wyświetl odpowiedni znak pobrany z tablicy znaków
          break;               // opuść instrukcję switch
       }
       case 2:
       {
          PORTD |= _BV(4);       // włącz drugą cyfrę wyświetlcza (tu są dziesiątki - wyświetlacz bardziej na lewo)
          PORTB = *(znaki + d1); // na wyświetlaczu wyświetl odpowiedni znak pobrany z tablicy znaków
          wysw = 0;
          break;             // opuść instrukcję switch
       }
    }
    }
    //procedura opóźniająca o 420us, gdy p==1 i OSC=8
    void waitms(unsigned char p)
    {
    unsigned char a,b; //zmienne licznikowe
    for(;p>0;--p) //ta pętla do wykonania p razy
       for(b=10;b>0;--b) //ta 10 razy
          for(a=10*OSC;a>0;--a) //ta petla 100 razy
          __asm("nop"); //dodatkowe opóźnienie o 1 cykl
    }

    // procedura opóźniająca o (5*t)* 0,125 us dla 8MHz kwarcu, tutaj jest 600ns (mierzone oscyloskopem)
    void delay(unsigned char t)
    {
    do
       {
       asm("nop");
       }
       while(--t);
    }
    // procedura reset
    char ow_reset(void)
    {
    char presence;
    CLR_DQ; // stan niski na linii 1wire
    delay(200);   //120us
    delay(200);   //120us
    delay(200);   //120us
    delay(250);   //140us razem ok 500us lub wiecej
    SET_DQ;// stan wysoki na linii 1wire

    delay(100); //po zwolnieniu linii czekaj 60us
    presence=~IN_DQ; //tu ma być stan niski, jak DS obecny
    delay(200);
    delay(200);   //120us
    delay(250);   //140us, razem ok 500us
    return presence;
    }

    void ow_write_bit(char b)      // procedura zapisu bitu na linię 1wire
    {
       cli();       // zablokowanie przerwań
       CLR_DQ;    // MASTER robi stan niski na linii 1wire
       delay(2);    // opóźnienie 1.2us - inicjalizacja slotu zapisu
       if(b) SET_DQ; // jeśli parametr jest niezerowy to ustaw stan wysoki na linii
       delay(108);    // opóźnienie 64us
       SET_DQ;    // MASTER odpuszcza, więc stan wysoki na linii 1wire
       delay(2);   //recovery -co najmniej 1us, mam 1.2us
       sei();       // odblokowanie przerwań
    }
    void ow_write_byte(char val)    //zapis całego bajta do DS18B20
    {
       unsigned char i;
       unsigned char zm;
       for (i=0; i<8; i++)      //bajt = 8xbit
       {
          zm = val >> i;
          zm &= 0x01;
          ow_write_bit(zm);
       }
       //delay(4);
    }
    char ow_read_bit(void)      //odczyt bita informacji z DS-a
    {
       char return_bit;
       cli();
       CLR_DQ;     //1-w stan niski
       delay(2);   //nie mniej niż 1us, mamy 1.2us
       SET_DQ;      //port 1-w jest wejściem, można czytać, co DS dał na linię
                //(przy okazji mamy zwolnienie linii, więc stan wysoki po skończeniu slotu)
       delay(20);      //czekaj 12us

       //if(IN_DQ) return 1; else return 0; //odczyt
       return_bit=IN_DQ;   //przypisanie aktualnego stanu portu do zmiennej return_bit
       delay(80);   //48us, cały slot = 61us (minimum to 60)
       delay(2); //recovery
       sei();      //odblokowanie przerwań
       return return_bit;   
    }

    unsigned char ow_read_byte(void)   //odczyt całego bajta
    {
       unsigned char i;
       unsigned char value = 0;
       for (i=0;i<8;i++)
       {
          if(ow_read_bit())   //jak zczytana jedynka
          value|=0x01<<i;      //to wpisuj jedynkę i przesuwaj w lewo całość
          // a co jak odczytane zero??
       }
       return(value);
    }


    //---------------------------------------------------------------------------
    int main(void)
    {
    unsigned char msb, lsb, temp;
    TCCR0 = 0x04;      //preskaler 100, czyli
    //TCNT0 = 1;      // wpisanie wartości początkowej
    TIMSK = 0x02;      // odblokowanie przerwania od licznika
    sei();      // globalne odblokowanie przerwań

    DDRB = 0xFF;   //wszystkie B WY
    DDRD |= _BV(6);      //port D6 WY (kontrola stan niski-wysoki)
    DDRD |= _BV(4);      //port D4 wyjście (wyświetlacz 1 wspólna Anoda +)
    DDRD |= _BV(3);      //port D3 wyjście (wyświetlacz 2 wspólna Anoda +)
    PORTD &= ~_BV(6);   //D6 stan niski (diodka-czerwona-niski, zielona-wysoki)
    PORTD &= ~_BV(5);   //d5 stan niski
    PORTD &= ~_BV(0);   //D0 niepodciągnięty (1-wire)

    while(1)
    {
    PORTD &=~_BV(6);   //zresetowanie stanu portu D6
    if(ow_reset())
    {
    PORTD |=_BV(6); //jeśli wystąpił presence pulse pokaż na diodach
    ow_write_byte(0xCC);      //CC - przeskocz skanowanie ROM, wysyłanie seriala itd
    ow_write_byte(0x44);      //44 - konwertuj temperaturę

    waitms(250);   //105ms na konwersję potrzeba ok 750ms, kiedy max rozdzielczość
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    waitms(250);
    }


    if(ow_reset())
    {
    ow_write_byte(0xCC);
    ow_write_byte(0xBE);
    }
    lsb = ow_read_byte();
    msb = ow_read_byte();
    //                                        |---MSB---|---LSB---|
    //                                        |znak  |calk   |części
    //organizacja 12-bitowej temperatury: np. +0.5stopnia 0000 0000 0000 1000
    //                              np. +10.125 st  0000 0000 1010 0010
    //                             np. +25.0625 st 0000 0001 1001 0001

    lsb = lsb >> 4;      //przesuń lsb o 4 miejsca w prawo (lsb - bajt; normalnie 4bity temp całkowite stopnie i 4 po przecinku)
    msb &= 0x0F;       //z-anduj msb i 0111 i przypisz do msb (msb - bajt; 5 starszych bitów - znak plus 3 bity liczby całkowitej)
    msb = msb << 4;      //przesuń msb o 4 miejsca w lewo
    temp = lsb | msb;
    d2 = temp / 10;         //podziel temp na 10 i umieść w d2
    d1 = temp % 10;         //resztę z dzielenia daj do d1
    }
    return 0;
    }


    Działający, taram!!
    Procek, AT90S2313, kwarc 8MHz, 1-wire to port D0 z podciągniętym DS18B20 przez rezystor 4K7 do plusa. Do tego 2 7-segmentowe wyświetlacze, wspólna anoda, oraz diodka kontrolna na D6, przy stanie wysokim zgaszona (żeby było widać króciutkie błyśnięcia).
    Życzę powodzenia,
    Michał[/code]