Elektroda.pl
Elektroda.pl
X
Arrow Multisolution Day
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[Mega32] [C] Obsługa dwóch czujników DS18S20 1-wire

Gibol 24 Cze 2010 22:39 2546 13
  • #1 24 Cze 2010 22:39
    Gibol
    Poziom 17  

    Chciałem sobie złożyć do kupy zbiór procedur do obsługi 2 czujników DS'a, które później wykorzystam w programie. Znalazłem kod który wyrzucał dane na RS'a.
    Złożyłem, trochę przerobiłem. Pierwszy raz piszę coś poważnego w C dla AVR'a.

    Na razie nie mam jak sprawdzić czy to ruszy i prosiłbym szanownych forumowiczów o sprawdzenie poprawności tego kodu, czy ruszy tak jak jest i będzie to działało prawidłowo czy czegoś mi brakuje / jest źle.

    Code:


    #include <avr/io.h>
    #include <avr/interrupt.h>
    //#include <signal.h>
    #include <stdlib.h>
    #include <stdio.h>

    #define MATCH_ROM   0x55
    #define SKIP_ROM   0xCC
    #define   SEARCH_ROM   0xF0

    #define CONVERT_T   0x44      // DS1820 commands
    #define READ      0xBE
    #define WRITE      0x4E
    #define EE_WRITE   0x48
    #define EE_RECALL   0xB8

    #define   SEARCH_FIRST   0xFF      // start new search
    #define   PRESENCE_ERR   0xFF
    #define   DATA_ERR   0xFE
    #define LAST_DEVICE   0x00      // last device found
    //         0x01 ... 0x40: continue searching

    #define W1_PIN   PD6
    #define W1_IN   PIND
    #define W1_OUT   PORTD
    #define W1_DDR   DDRD

    //#define   XTAL      11059200L
    #define XTAL      16000000L

    #define uchar unsigned char
    #define uint unsigned int
    #define bit uchar
    #define idata
    #define code


    #define W1_PIN   PD6
    #define W1_IN   PIND
    #define W1_OUT   PORTD
    #define W1_DDR   DDRD

    #define DELAY_US(x)   ((uint)( (x) * 1e-6 * XTAL ))
    #define DELAY(x)   delay(x)

    //            Attention !!!
    // take care, that during delay no interrupt access a timer register
    // or restore TCNT1H


    void delay( int d )         // d = 0 ... 32000
    {
      d += TCNT1;            // not atomic !

      while( (TCNT1 - d) & 0x8000 );   // not atomic !
    }




    typedef struct
    {
       uint t1;
       uint t2;
       }wyniki;







    bit w1_reset(void)
    {
      bit err;

      W1_OUT &= ~(1<<W1_PIN);




      W1_DDR |= 1<<W1_PIN;
      DELAY( DELAY_US( 480 ));         // 480 us
      cli();
      W1_DDR &= ~(1<<W1_PIN);
      DELAY( DELAY_US( 66 ));
      err = W1_IN & (1<<W1_PIN);         // no presence detect
      sei();
      DELAY( DELAY_US( 480 - 66 ));
      if( (W1_IN & (1<<W1_PIN)) == 0 )      // short circuit
        err = 1;
      return err;
    }


    uchar w1_bit_io( bit b )
    {
      cli();
      W1_DDR |= 1<<W1_PIN;
      DELAY( DELAY_US( 1 ));
      if( b )
        W1_DDR &= ~(1<<W1_PIN);
      DELAY( DELAY_US( 15 - 1 ));
      if( (W1_IN & (1<<W1_PIN)) == 0 )
        b = 0;
      DELAY( DELAY_US( 60 - 15 ));
      W1_DDR &= ~(1<<W1_PIN);
      sei();
      return b;
    }


    uint w1_byte_wr( uchar b )
    {
      uchar i = 8, j;
      do{
        j = w1_bit_io( b & 1 );
        b >>= 1;
        if( j )
          b |= 0x80;
      }while( --i );
      return b;
    }


    uint w1_byte_rd( void )
    {
      return w1_byte_wr( 0xFF );
    }


    uchar w1_rom_search( uchar diff, uchar idata *id )
    {
      uchar i, j, next_diff;
      bit b;

      if( w1_reset() )
        return PRESENCE_ERR;         // error, no device found

      w1_byte_wr( SEARCH_ROM );         // ROM search command
      next_diff = LAST_DEVICE;         // unchanged on last device
     
      i = 8 * 8;               // 8 bytes
     
      do{
        j = 8;               // 8 bits
        do{
          b = w1_bit_io( 1 );         // read bit
          if( w1_bit_io( 1 ) ){         // read complement bit
       if( b )               // 11
         return DATA_ERR;         // data error
          }else{
       if( !b ){            // 00 = 2 devices
         if( diff > i || ((*id & 1) && diff != i) )
          {
             b = 1;            // now 1
             next_diff = i;         // next pass 0
          }
       }
          }
          w1_bit_io( b );              // write bit
          *id >>= 1;
          if( b )               // store bit
       *id |= 0x80;
          i--;
        }while( --j );
        id++;               // next byte
      }while( i );
      return next_diff;            // to continue search
    }



    void w1_command( uchar command, uchar idata *id )
    {
      uchar i;
      w1_reset();
      if( id ){
        w1_byte_wr( MATCH_ROM );         // to a single device
        i = 8;
        do{
          w1_byte_wr( *id );
          id++;
        }while( --i );
      }else{
        w1_byte_wr( SKIP_ROM );         // to all devices
      }
      w1_byte_wr( command );
    }

    void start_meas( void ){
      if( W1_IN & 1<< W1_PIN ){
        w1_command( CONVERT_T, NULL );
        W1_OUT |= 1<< W1_PIN;
       // W1_DDR |= 1<< W1_PIN;         // parasite power on

      }
    }


    wyniki read_meas( void )
    {
      uchar id[8], diff;
      wyniki i;
      uint temp;
     

      uchar j=0;

      for( diff = SEARCH_FIRST; diff != LAST_DEVICE; ){
        diff = w1_rom_search( diff, id );

        if( diff == PRESENCE_ERR ){
          //uputsnl( "No Sensor found" );
          break;
        }
        if( diff == DATA_ERR ){
          //uputsnl( "Bus Error" );
          break;
        }
       
          w1_byte_wr( READ );         // read command
          temp = w1_byte_rd();         // low byte
          temp |= (uint)w1_byte_rd() << 8;      // high byte
          if( id[0] == 0x10 )         // 9 -> 12 bit
            temp <<= 3;
         
         if(j == 1)
         i.t1 = temp;
         else
         i.t2 = temp;
         j++;
        }
       return i;
      }
     


      int main( void )
    {
       
    //init_timer();
     //uinit();
     sei();
     wyniki w;
     start_meas();
     w = read_meas();
    }


    Jak mówię kod jakby nie był do końca taki jak potrzebuję i stąd, niektóre rzeczy wrzucone w komentarz, które powodowały jakieś błędy czy nie wiedziałem po co one właściwie tu są.
    Używam AVRSTUDIO4 i WINAVR2010.

    0 13
  • Arrow Multisolution Day
  • #2 24 Cze 2010 23:03
    flapo213
    Poziom 21  

    Witaj Kolego,

    Nie wiem czego oczekujesz bo kod który zamieściłeś jak się domyślam nie jest twój, więc po co to sprawdzać, jak chcesz mogę Ci podesłać albo zamieścić coś napisane przez mnie procedury które obsługują wszystkie modele DS18x20 oraz konwersję U2 (ujemnych temperatur) oraz pracujące z ATM32 no i oczywiście konwersje do stringa oraz wartości DEC, HEX.

    Jeśli natomiast chcesz się czegoś nauczyć to nie cały kod tylko funkcja po funkcji bo tu się rozwiązuje problemy jak komuś coś nie działa lub nie wie co dalej bo coś w którymś momencie nie działa itd.

    Sprecyzuj bardziej co i jak, konkretnie która funkcja Cię najbardziej interesuje.

    Pozdrawiam

    0
  • #3 24 Cze 2010 23:39
    Gibol
    Poziom 17  

    No to już mówię jakie mam wątpliwości (kod nie jest mój, ale go zmodyfikowałem nieco do własnych potrzeb i nie mogę być pewnien czy zadziała (mogę nie rozumieć niektórych operacji itp.):

    Po 1. czy samo sei() wystarczy w mainie do tego żeby funkcja delay działała (jest użyte w niej TCNT1, ale nie widzę, żadnej konfiguracji do tego jak to było w bascomie.

    Po 2. czy funkcja read_meas() będzie działać poprawnie (zapisywać wartości pomiarów z obu czujników do struktury). Co do tego w jakiej postaci są te dane, nie wiem bo ich jeszcze nie widziałem ale raczej to nie jest temperatura, gdybyś mógł podać mi procedury, o których mówiłeś konwetujące ten wynik do wartości dziesiętnych (dotatnich/ujemnych).

    0
  • Arrow Multisolution Day
  • #4 25 Cze 2010 08:22
    flapo213
    Poziom 21  

    Witaj,

    widzę że trochę teori o DS18x20 Ci się przyda. Magistrala one wire której używają czujniki DS18x20 dziła tak że może być do tej magistrali podłączone wiele urządzeń ale odczyt z tych urządzeń nie jest jednoczesny tylko sekwencyjny to znaczy jeden po drugim. Ale nasuwa się pytanie skoro jest tylko jedna linia danych to jak zrobić odczyt sekwencyjny, odpowiedź jest prosta istnieją komendy które adresują urządzenia. Aby zaadresować urządzenie należy odpytać urządzenia na magistrali o numer seryjny (numer seryjny jest unikalnym numerem każdego urządzenia który np. mówi jakiego typu urządzenie jest itd.) Aby pozyskać numer seryjny urządzenia należy najpierw podłączyć i zasilić wszystkie urządzenia a następnie wysłać komendę poszukującą urządzeń i czytającą numery seryjne bądź podłączać pojedynczo czujnik po czujniku i odczytywać numery seryjne. Ja proponuję abyś odczytywał numer seryjny urządzeń pojedynczy i zapisywał do jakiejś pamięci ten numer. Taki sposób pozyskania numerów seryjnych jest prostszy i w przypadku czujników DS18x20 jak najbardziej słuszny dlaczego odpoweidź jest prosta gdybyś użył komendy przeszukaj wszystkie to nie wiedziałbyś który czujnik jest który itd. Jak mamy już numery seryjne to można adresować pojedyncze urządzenie poniżej zamieszczam Ci w punktach procedurę odczytu temperatury:

    1. Prześlij adres czujnika do odczytu

    2. Wyślij komendę konwersji temperatury

    3. Odczekać potrzebny czas na konwersję temperatury

    4. Wyślij komednę odczytu temperatury

    Co oznaczają terminy konwersja temperatury:

    DS18x20 jest czujnikiem temperatury z własną logiką, jest to czujnik cyfrowy a konwersja temperatury polega na tym iż układ ten pobiera temperaturę i przetwarza ją zgodnie z dokładnością modelu 12bit, 11bit, 10bit, 9bit. Naturalnie najdłużej trwa konwersja temperatury przy 12bitowej dokłądności około 750ms.

    Następnie wynik konwersji jest przechowywany w specjalnych rejestrach i tą właśnie wartość odczytujesz.

    Wnioski:

    Nie potrzeba extra procedur do odczytu z wielu czujników DS18x20.

    Zalecenia:

    Aby dobrze i sprawnie podejść do tematu potrzeba kawałek hardware + oscyloskop.

    W swoim czasie jak poodpalasz wszystkie części związane z nikopoziomową obsługą tego czujnika podeślę Ci procedury. Gdybym Ci je teraz dał to Ci się to totalnie wymiesza.

    Będę śledził ten wątek,

    Pozdrawiam

    0
  • #5 25 Cze 2010 14:16
    gaskoin
    Poziom 38  

    w datasheecie dallasa te procedury są już napisane tak, że wystarczy je skopiować

    0
  • #6 25 Cze 2010 15:11
    Gibol
    Poziom 17  

    gaskoin napisał:
    w datasheecie dallasa te procedury są już napisane tak, że wystarczy je skopiować


    W którym? Ani w B ani w S nie ma.

    0
  • #8 25 Cze 2010 22:53
    Gibol
    Poziom 17  

    Ok, zmieniłem kod na którym bazowałem na inny, tamten był mało czytelny.
    W tym nie było procedur do odczytu temperatury, więc dopisałem ale oba bajty zwraca mi 255.

    Odczytywanie ID działa w porządku, wyrzuca na ekran 8 wartości 3-cyfrowych jak chciałem.

    Nie wiem co dalej z odczytem pomiarów z czujnika. Dodam że czujniki są pasite-power.

    Code:

    #include <avr/io.h>
    #include <avr/delay.h>
    #include <hd44780.c>
    #include <hd44780.h>


    #define F_CPU 16000000UL


    #define CONVERT_T   0x44      // DS1820 commands
    #define READ      0xBE



    #define uchar unsigned char
    #define uint unsigned int



    /**************************************************************************************************************
    Konfiguracja pinu DQ
    **************************************************************************************************************/
    #define DDR_DQ DDRB
    #define PORT_DQ PORTB
    #define PIN_DQ PINB
    #define DQ 0

    /**************************************************************************************************************
    Ilość urządzeń 1wire
    **************************************************************************************************************/
    #define AMOUNT 2

    /**********************************************************************************
    Tablica kodów ROM
    **********************************************************************************/
    unsigned char rom[AMOUNT][8];

    /**********************************************************************************
    Wysyła sygnał RESET i odbiera sygnał PRESENCE PULSE. Jeśli powodzenie zwraca 0.
    **********************************************************************************/
    unsigned char owire_reset(void)
    {
       unsigned char ret;
       
       PORT_DQ &= ~(1<<DQ);
       DDR_DQ |= (1<<DQ);
       
       _delay_ms(0.6);   
       
       DDR_DQ &= ~(1<<DQ);
       
       _delay_us(70);
       
       ret = (PIN_DQ & (1<<DQ));
       
       _delay_ms(0.6);
       
       return ret;
    }

    /**********************************************************************************
    Wysyła pojedynczy bit na linię
    **********************************************************************************/
    void owire_send_bit(unsigned char data)
    {
       PORT_DQ &= ~(1<<DQ);
       DDR_DQ |= (1<<DQ);
       
       _delay_us(10);
       
       if(data&0x01)
          DDR_DQ &= ~(1<<DQ);
         
       _delay_us(70);
       
       DDR_DQ &= ~(1<<DQ);
       
       _delay_us(10);
    }

    /**********************************************************************************
    Odbiera pojedynczy bit z linii
    **********************************************************************************/
    unsigned char owire_get_bit(void)   
    {
       unsigned char data;
       
       PORT_DQ &= ~(1<<DQ);
       DDR_DQ |= (1<<DQ);
       
       _delay_us(5);
       
       DDR_DQ &= ~(1<<DQ);
       
       _delay_us(5);
       
       data=(0x01 & (PIN_DQ>>DQ));

       _delay_us(80);
       
       return data;
    }

    /**********************************************************************************
    Wysyła bajt danych
    **********************************************************************************/
    void owire_send_byte(unsigned char data)
    {
       unsigned char licznik;
       
       for(licznik=0;licznik != 8;licznik++)
          owire_send_bit(data>>licznik);
       
       _delay_us(40);
    }

    /**********************************************************************************
    Zczytuje kody ROM wszystkich urządzeń na linii 1wire
    **********************************************************************************/
    void owire_search_rom(void)
    {
       unsigned char last_zero, a, b, tmp, discrp = 0, last_bit = 0, dscnt = 0;

       do
       {
       
          while(owire_reset());
          owire_send_byte(0xf0);
         
          last_zero = 0;
          for(a = 0; a < 64; a++)
          {
         
             b = owire_get_bit();
             tmp = owire_get_bit();
             
             if((b == 0)&&(tmp == 0))
             {
                if(a == discrp)
                   b = 1;
                else if(a > discrp)
                   b = 0;
                else
                   b = last_bit;
                if(b == 0)
                   last_zero = a;
             }
             
             owire_send_bit(b);
             rom[dscnt][(a / 8)]|=(b << (a % 8));
             last_bit = b;
          }
         
          dscnt++;
          discrp = last_zero;
       } while((dscnt < AMOUNT)&&(discrp > 0));
       
    }


    //////////////////////////////////////////////////////////////////////////////
    // READ_BYTE - reads a byte from the one-wire bus.
    //
    unsigned char owire_read_byte(void)
    {
    unsigned char i;
    unsigned char value = 0;
    for (i=0;i<8;i++)
    {

    if(owire_get_bit()) value|=0x01<<i; // reads byte in, one byte at a time and then
    // shifts it left
    _delay_us(6); // wait for rest of timeslot
    }
    return value;
    }

    void LCD_Byte(char b1)
    {
    unsigned char b1a, b1b, b1c;

    b1a = b1 / 100;
    b1b = (b1 - (b1a*100))/10;
    b1c = b1 - (b1a*100) - (b1b*10);

    b1a += 48;
    b1b += 48;
    b1c += 48;




    LCD_WriteData(b1a);
    _delay_ms(10);
    LCD_WriteData(b1b);
    _delay_ms(10);
    LCD_WriteData(b1c);
    }


    int main(void)
    {

       
       
       LCD_Initalize();
       _delay_ms(700);
       LCD_Clear();
       _delay_ms(700);
       LCD_WriteText("a");
       _delay_ms(300);
       LCD_WriteText("b");
       _delay_ms(300);
       LCD_WriteText("c");

       _delay_ms(300);

       owire_search_rom();

       LCD_Clear();
       _delay_ms(700);

       LCD_GoTo(0,0);

    for(int i=0; i<9; i++)
       {
       LCD_Byte(rom[1][i]);
       LCD_WriteText(",");
       if(i==4)
       LCD_GoTo(0,1);

       _delay_ms(300);
       }
       
       _delay_ms(4000);
       LCD_Clear();

    for(int j=0; j<9; j++)
       {
       LCD_Byte(rom[0][j]);
       LCD_WriteText(",");
       if(j==4)
       LCD_GoTo(0,1);
       _delay_ms(300);
       }
       
    _delay_ms(1000);


    for(char a=0; a<8; a++)
    {
       owire_send_byte(rom[1][a]);
       }

       owire_send_byte(CONVERT_T);
       _delay_ms(750);
       owire_send_byte(READ);

    char b1, b2;

    b2= owire_read_byte();
    b1= owire_read_byte();

    }

    0
  • #9 28 Cze 2010 09:22
    flapo213
    Poziom 21  

    Wita,

    Z tego co czytam z Twojego postu próbujesz zasilić czujnik w tzw. trybie parasite power czyli zasilanie twojego czujnika będzie odbywać się poprzez linię DO. To nie jest tak prosto jak by się wydawało że jest w funkcji "one_wire_send_byte" czy jakoś tak masz w tym momencie błąd tzn. nie do końca błąd bo część komend będzie działać takich jak odczyt numeru seryjnego. Komenda konwersji temperatury jest komendą która wymaga większej ilości energi. Jeśli nie dostarczysz do czujnika odpowiednio dużo eneregi to po wydaniu komedny convert to czujnik się zresetuje poprostu i otrzymasz wynik pomiaru temperatury 85C za każdym razem. W datasheet jest opisane że masz około kilkuset mikrosekund aby na linie DO podłączyć źródło o wyższej wydajności prądowej niż podciąganie. W praktyce oznacza to że po wydaniu komendy 'Convert' powinienś przełączyć pin Atmegi w tryb output source i tu mocna uwaga przekonfigurowanie portu powinno być w odpowiedniej kolejności tzn DDRx przed PORTx lub odwrotnie ja już nie pamiętam dokłądnie ale te ukłdy są na to wyczulone i to bardzo. Jeśli nie chesz się z tym męczyć to mogę Ci podesłać gotowe procedury obsługi w Atmega16 z DS18x20 i wszelkimi możliwymi kowersjami przelicznikami itd. Przyznam się że chwilę się nad tym namęczyłem ale się opłacało bo okazało się że połączenie czujnika w trybie 3-przewodowym niestety wprowadzało nagrzewanie czujnika i wprowadziło niedokładność pomiaru temperaruty. W trybie parasite power czujnik jest zasilany tylko na chwilkę. Warto również zastosować w szerego pomiędzy pinem procesora a pinem DO czujnika rezystor rzędu 100Ohmów celem zabezpieczenia nóżki procesora przed przypadkowym przywarciem do masy np. na kablu.

    Gdybś sobie nie poradził to daj znać to podeślę Ci procedury, a być może zamieszczę na Twoim poście całe gotowe rozwiązanie żeby inni nie mieli już tego typu problemów z tym związanych.

    Pozdrawiam

    0
  • #10 28 Cze 2010 15:40
    Gibol
    Poziom 17  

    Byłbym wdzięczny, bo jak do tej pory podłączałem 2 czujniki i programowałem pod BASCOM, nie było problemu, że one są parasite power, wszystko szło ok, bez dodatkowego zasilania. Zrobił bym je "Normalnie" ale czuniki które posiadam są tu próbki z Dallasa i ten model akurat pracuje tylko w trybie parasite power (DS18S20PAR).
    Jednym słowem, byłbym wdzięczny za działające procedury :) Mogą być w temacie, będzie łatwiej dla przyszłych szukających. Dzięki wielkie z góry!

    0
  • #11 28 Cze 2010 19:04
    gaskoin
    Poziom 38  

    no przecież dałem Ci linki wyżej (działające, kody też działają) kod wystarczy jedynie minimalnie zmienić

    0
  • #12 29 Cze 2010 22:34
    flapo213
    Poziom 21  

    Witaj,

    Przepraszam że tak późno ale zamieszczam obiecane procedury, ufam że będziesz wiedział gdzie i co i jak zmienić.

    W pliku *.H zmień port mikrokontrolera zgodnie ze swiom schematem.

    Zmień również czy raczej dostosuj czasówkę głównie chodzi mi o timer.

    Jakby coś daj znać,

    PS. do kolegi 'gaskoin' te tak zwane minimalne przeróbki ustalałem z atmelem przez tydzień i wcale nie było to oczywiste chodzi mi o połączenie atmeg z DS18x20 a raczej przekonfigurowywanie portów. Co i jak zapraszam do przestudiowania plików.

    Pliki do pobrania bez punktów

    W razie niejasności pytaj zdaję sobie sprawę że może być coś niejasne.

    Pozdrawiam

    0
  • #13 29 Cze 2010 23:08
    gaskoin
    Poziom 38  

    flapo213 napisał:

    PS. do kolegi 'gaskoin' te tak zwane minimalne przeróbki ustalałem z atmelem przez tydzień i wcale nie było to oczywiste chodzi mi o połączenie atmeg z DS18x20 a raczej przekonfigurowywanie portów


    połączenie jest jasno opisane w datasheecie - nie wiem o co Ci chodzi z tym "przekonfigurowywaniem" portów. Zmian naprawdę jest niewiele, żeby to odpalić.

    Może inaczej - zmian jest wiele, ale dotyczą tego samego.

    tu macie jeszcze jednego pdfa, nie ma natomiast w nim jak połączyć kilka term w szeregu www.teslabs.com/openplayer/docs/docs/other/ds18b20_pre1.pdf

    0
  • #14 30 Cze 2010 08:52
    flapo213
    Poziom 21  

    Nie no jasne hardwarowo to raczej nie ma się co przyczepić ale spróbuj oprogramować atmegę do współpracy z czujnikiem DS18x20 w trybie parasite power nie patrząc na moje przykłady to się przekonasz że tak wcale oczywiste nie jest i chwilę trzeba posiedzieć i przestudiować one wire-a.

    Pozdrawiam

    0