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

ATmega32 i czujnik temperatury DS18B20

mitny 04 Mar 2010 09:14 6681 18
  • #1 04 Mar 2010 09:14
    mitny
    Poziom 13  

    Witam,
    Od jakiegoś czasu w ramach wolnego czasu uczę się programowania mikrokontrolera ATmega32. Koduję w C (pracuję jako programista wiec ten język jest mi bliższy niż asembler), dotychczas nie miałem jakichś większych problemów, udało mi się nawet obsłużyć wyświetlacz LCD i sądziłem, że obsługa czujnika temperatury DS18B20 nie sprawi mi większych kłopotów - niestety myliłem się. Przeczytałem dziesiątki postów, kilkanaście artykułów i jak dotąd nie potrafię go obsłużyć - nie potrafię nawet wysłać prawidłowo sygnału reset.

    Mój schemat podłączenia wygląda tak:
    ATmega32 i czujnik temperatury DS18B20
    W samym mikrokontrolerze linia 1wire jest podpięta pod 3 pin w porcie D.
    Mikrokontroler podpięty jest pod zewnętrzny kwarc 12MHz a jest częstotliwość mam wprowadzoną w pliku makefile w F_CPU 12000000UL.
    Po wielu próbach podszedłem do tematu minimalistycznie - im mniej kodu tym mniej szans na błąd. Napisałem sobie poniższy skrypt:

    Code:

    #include <avr/io.h>
    #include <util/delay.h>

    #define PIN_1WIRE 2
    #define PORT_1WIRE PIND
    #define OUT_1WIRE_LOW PORT_1WIRE&=~(1<<PIN_1WIRE);
    #define OUT_1WIRE_HIGH PORT_1WIRE|=1<<PIN_1WIRE;
    #define DIR_1WIRE_IN DDRD&=~(1<<PIN_1WIRE);
    #define DIR_1WIRE_OUT DDRD|=1<<PIN_1WIRE;

    void delays(unsigned int ile)
    {
        unsigned long licznik=0;
        unsigned long koniec=ile*100;
        do
        {
            _delay_ms(10);
            licznik++;
        }while(licznik<koniec);
    }

    int main(void)
    {
        DDRA = 0xFF;
        PORTA = 0x00;
        delays(3);

        OUT_1WIRE_LOW; //ustaw port w stan niski
        DIR_1WIRE_OUT; //kierunek pinu na wyjście
        _delay_loop_2(1500); //odczekanie 500us
        DIR_1WIRE_IN; //kierunek pinu na wejście
        _delay_loop_2(135);//odczekanie 45us

        if (bit_is_clear(PORT_1WIRE, PIN_1WIRE)) //sprawdzenie poziomu linii
        {
            //odebrano bit PRESENCE
            PORTA = 0x02;
        }
        else
        {
            //stan nieaktywności
            PORTA = 0x01;
        }
        delays(50);
        return 0;
    }

    W powyższym przypadku stan sprawdzam po tym która dioda zaświeci mi się po wykonaniu programu - niestety zawsze świeci się nie ta co powinna.

    Być może błąd jest banalnie prosty i go nie zauważam niemniej proszę o jakieś rady abym mógł ruszyć z miejsca.

    0 18
  • Pomocny post
    #2 04 Mar 2010 10:01
    tmf
    Moderator Mikrokontrolery Projektowanie

    Twoje makro OUT_1WIRE_LOW jest bledne - dziala na PIND zamiast na PORTD, p[odobnie OUT_1WIRE_HIGH.

    0
  • #3 04 Mar 2010 18:51
    mitny
    Poziom 13  

    Dzięki. Faktycznie odczytywałem i zapisywałem za pomocą PIND.
    Niestety po poprawkach nadal nie działa.

    Code:
    #include <avr/io.h>
    
    #include <util/delay.h>

    #define NR_PIN PD2
    #define PORT_1WIRE PORTD
    #define PIN_1WIRE PIND
    #define DDR_1WIRE DDRD

    #define OUT_1WIRE_LOW     PORT_1WIRE&=~(1<<NR_PIN);
    #define OUT_1WIRE_HIGH    PORT_1WIRE|=1<<NR_PIN;
    #define DIR_1WIRE_IN      DDR_1WIRE&=~(1<<NR_PIN);
    #define DIR_1WIRE_OUT     DDR_1WIRE|=1<<NR_PIN;

    void delays(unsigned int ile)
    {
        unsigned long licznik=0;
        unsigned long koniec=ile*100;
        do
        {
            _delay_ms(10);
            licznik++;
        }while(licznik<koniec);
    }

    int main(void)
    {
        DDRA = 0xFF;
        PORTA = 0x00;
        delays(2);

       OUT_1WIRE_LOW; //ustaw port w stan niski
       DIR_1WIRE_OUT; //kierunek pinu na wyjście
        _delay_loop_2(1500); //odczekanie 500us

        DIR_1WIRE_IN; //kierunek pinu na wejście
        _delay_loop_2(135);//odczekanie 45us

        if (bit_is_clear(PIN_1WIRE, NR_PIN)) //sprawdzenie poziomu linii
        {
            //odebrano bit PRESENCE
            PORTA = 0x02;
        }
        else
        {
            //stan nieaktywności
            PORTA = 0x01;
        }
        delays(50);
        return 0;
    }

    Nie wiem czy przyczyny szukać gdzieś w kodzie czy może coś z moim podłączeniem jest nie tak - niemniej podłączone mam wg schematu.
    Dodam, że komunikacja pomiędzy mikroprocesorem a czujnikiem odbywa się po 20cm przewodzie ale to chyba nie powinno tutaj robić problemów.
    Zastanawiam się jeszcze nad tym czy aby na pewno mikrokontroler daje radę wysłać stan niski do czujnika - z obserwacji miernikiem wydawało mi się, że napięcie spadało z 5 do jakichś 3V jednak czasy są tak krótkie, że nie sposób być tego pewnym.

    0
  • #4 04 Mar 2010 22:18
    tmf
    Moderator Mikrokontrolery Projektowanie

    Dlaczego korzystasz z tak dziwacznych _delay_loop_2? Masz z biblioteki delay_us i z tej funkcji powinienes korzystac. Samo poprzedzenie funkcji znakiem "_" sugeruje, ze jest to wewnetrzna funkcja biblioteki i nie nalezy poza nia z niej korzystac. Dodatkowo optymalizacje masz ustawiona na -Os? Z O0 nie dzialaja funkcje z biblioteki delay.

    0
  • #5 04 Mar 2010 22:19
    mitny
    Poziom 13  

    Zmieniłem typ połączenia na:
    ATmega32 i czujnik temperatury DS18B20
    I w tym przypadku reset zadziałał - otrzymałem prawidłowy sygnał.
    Pytanie dlaczego w takim razie nie działa przy podłączeniu z zewnętrznego zasilania?
    Ponadto w takim przypadku chyba powinienem jeszcze coś połączyć bo z tego co wyczytałem to linia 1-wire może nie dać rady na czas konwersji którą musi wykonać czujnik? Jakieś rady?

    0
  • Pomocny post
    #6 04 Mar 2010 23:18
    slx
    Poziom 18  

    Tu masz za krótki czas

    Code:
        DIR_1WIRE_IN; //kierunek pinu na wejście 
    
        _delay_loop_2(135);//odczekanie 45us


    w dokumentacji wyraźnie napisane, że DS czeka od 15 do 60us po powrocie szyny do 1 i dopiero sciąga do zera.

    0
  • #7 04 Mar 2010 23:35
    mitny
    Poziom 13  

    Korzystam z funkcji _delay_loop_2 ponieważ chce mieć jak najbardziej dokładne opóźnienie. Funkcja _delay_loop_2 jest tak samo dostępna jak _delay_ms i obie są poprzedzone "_". Funkcja _delay_loop_2 wykonuje się dokładnie 4 cykle zegarowe * parametr. Znając częstotliwość mikroprocesora bez problemu można określić opóźnienia.
    Co do optymalizacji to mam ustawione na -O2 ale chyba efekt ten sam co z -Os.

    Jak wspomniałem wcześniej po przełączeniu czujnika na zasilanie pasożytnicze zaczyna zachowywać się poprawnie - a przynajmniej odpowiada na sygnał reset.
    I tutaj nie mogę dociec co może być powodem tego, że nie działa przy zasilaniu zewnętrznym?

    Dodano po 10 [minuty]:

    Poprawiłem czasy. Aktualnie kod wygląda tak:

    Code:
    #include <avr/io.h>
    
    #include <util/delay.h>

    #define NR_PIN PD2
    #define PIN_1WIRE PIND
    #define PORT_1WIRE PORTD
    #define DDR_1WIRE DDRD

    #define OUT_1WIRE_LOW     PORT_1WIRE&=~(1<<NR_PIN);
    #define OUT_1WIRE_HIGH    PORT_1WIRE|=1<<NR_PIN;
    #define DIR_1WIRE_IN      DDR_1WIRE&=~(1<<NR_PIN);
    #define DIR_1WIRE_OUT     DDR_1WIRE|=1<<NR_PIN;

    void delays(unsigned int ile)
    {
        unsigned long licznik=0;
        unsigned long koniec=ile*100;
        do
        {
            _delay_ms(10);
            licznik++;
        }while(licznik<koniec);
    }

    int main(void)
    {
        DDRA = 0xFF;
        PORTA = 0x00;
        delays(2);

       DIR_1WIRE_OUT; //kierunek pinu na wyjście
       OUT_1WIRE_LOW; //ustaw port w stan niski
       _delay_loop_2(1440);   //odczekanie 480us - przy 12MHz

        DIR_1WIRE_IN; //kierunek pinu na wejście
        _delay_loop_2(210);//odczekanie 70us

        if (bit_is_clear(PIN_1WIRE, NR_PIN)) //sprawdzenie poziomu linii
        {
            //odebrano bit PRESENCE
            PORTA = 0x02;
        }
        else
        {
            //stan nieaktywności
            PORTA = 0x01;
        }
        delays(5);

       if (bit_is_clear(PIN_1WIRE, NR_PIN)) //sprawdzenie poziomu linii
        {
            //odebrano bit PRESENCE
            PORTA = 0x02;
        }
        else
        {
            //stan nieaktywności
            PORTA = 0x01;
        }
       delays(50);
        return 0;
    }

    Przy zasilaniu pasożytniczym najpierw widać bit PRESENCE następnie po 5s już nie a więc zachowuje się tak jak trzeba jednak przy zasilaniu zewnętrznym wcale nie otrzymuję bitu PRESENCE.

    0
  • #8 05 Mar 2010 09:48
    tmf
    Moderator Mikrokontrolery Projektowanie

    Wydluz czas reset pulse. W tej chwili jest na granicy. No i twoje sprawdzenie czy jest presence pulse nie ma prawa dzialac. Po drugim if zawsze szyna bedzie w stanie 1.

    0
  • #9 05 Mar 2010 15:08
    mitny
    Poziom 13  

    Wydłużyłem czas reset pulse do 600us i nadal to samo. Przy zewnętrznym zasilaniu nie reaguje.
    Natomiast co do drugiego ifa to jest tylko tak dodatkowo - sprawdzam po prostu czy liniia wraca do stanu wysokiego.

    0
  • #10 05 Mar 2010 19:44
    zumek
    Poziom 39  

    Wszelkie znaki na niebie i na ziemi wskazuję, że masz "przekłamaną" częstotliwość taktowania rdzenia :D

    mitny napisał:

    Mikrokontroler podpięty jest pod zewnętrzny kwarc 12MHz a jest częstotliwość mam wprowadzoną w pliku makefile w F_CPU 12000000UL.

    To że masz podpięty kwarc pod mikrokontroler( a nie odwrotnie :D ), nie jest równoznaczne z tym, że mikrokontroler z niego korzysta - fusebity.
    Sprawdziłem Twój kod na M32 i funkcja delays(2) faktycznie "zajmuje" mikrokontroler na 2s, ale pod warunkiem, że jest on taktowany częstotliwością 1MHz, czyli ustawiona fabrycznie. Dla mnie jest oczywiste, że TO jest przyczyna Twoich kłopotów.

    0
  • #11 06 Mar 2010 08:34
    mitny
    Poziom 13  

    Zastąpiłem funkcję delays nową wersją:

    Code:

    void delays(unsigned int ile)
    {
        unsigned long licznik=0;
       unsigned long koniec=ile*50;
        do
        {
            _delay_loop_2(60000);
            licznik++;
        }while(licznik<koniec);
    }

    Tutaj użyłem funkcji _delay_loop_2 która zajmuje dokładnie 4 takty zegarowe * parametr. Gdy podam parametr 60000 przy 12Mhz funkcja zajmie procesor na 0.02s - mnożę to przez 50 i mam 1s. I ta funkcja również wykonuje się dokładnie tyle ile trzeba czyli 2s a więc taktowanie mam chyba jednak dobre.

    Mimo wszystko dorzucam moje ustawienia bo może faktycznie coś mam tu źle i dlatego nie działa.
    ATmega32 i czujnik temperatury DS18B20

    0
  • #12 06 Mar 2010 11:31
    inventco.eu

    Poziom 29  

    inventco.eu - proszę zapoznać się z regulaminem w szczególności punkt 14.

    0
  • #13 06 Mar 2010 13:34
    mitny
    Poziom 13  

    Pojawiła się sugestia, że przy 12MHz może nie działać - obniżyłem taktowanie używając wbudowanego do 4MHz (przestawiając odpowiednio czasy) i nadal to samo. Ciągle chodzi mi po głowie to czy atmega może wysłać 0 na szynę. Czy to prawidłowa sytuacja, że jeśli wystawię 0 przez dłuższy czas np. 1s to mikroprocesor resetuje się? Tak jakby nie mógł wystawić 0 i prąd który płynie przez szynę go resetował.

    0
  • #14 07 Mar 2010 00:12
    wojtek8-7
    Poziom 12  

    http://mikrokontrolery.net/avr_c_10.htm

    Nie znam się na C ale może to Tobie pomoże, mi troszkę pomogło jak się męczyłem z tym układem, też miałem z nim problemy, ale się udało :) Ja zapomniałem również o tym, że jeżeli port jest skonfigurowany jako wejście i jest w stanie niskim to rezystor podciąga magistrale do +. Trzeba o tym pamiętać

    0
  • #15 09 Mar 2010 22:15
    mitny
    Poziom 13  

    Chyba mam coś nie tak z podłączeniem. Atmega nie może ściągnąć linii do 0. Wygląda to tak, że gdy linia 1wire jest podłączona do mikrokontrolera i do rezystora 4.7k wówczas gdy ustawię pin jako wyjście i na port dam 0 to tak jest w rzeczywistości (sprawdzałem miernikiem - ustawiłem sobie czas na kilka sekund). Natomiast gdy do tej linii podłączę jeszcze linię DQ od czujnika wówczas w analogicznej sytuacji na linii mam 4.22V czyli tak jakby prąd płynący od linii Vdd w czujniku swobodnie przepływał na linię DQ. Nie wiem dlaczego tak jest - no chyba, ze tak być powinno ale wówczas jak wystawić 0 na 1wire?
    Czy ktoś może mnie poratować radą co mam nie tak?

    0
  • Pomocny post
    #16 09 Mar 2010 22:42
    tmf
    Moderator Mikrokontrolery Projektowanie

    Najpewniej koncowki GND i Vdd czujnika masz podlaczone na odwrot. Jestes pewien, ze dobrze rozpoznales piny?

    0
  • #17 09 Mar 2010 23:13
    mitny
    Poziom 13  

    Patrząc z góry mam tak:
    ATmega32 i czujnik temperatury DS18B20
    Wg. mnie tak jak mówi specyfikacja - chyba, że źle ją rozumiem.

    0
  • #19 10 Mar 2010 08:16
    mitny
    Poziom 13  

    Aż mi głupio za taki błąd :)
    Specyfikację miałem wydrukowaną a z racji małej ilości tuszu przycięło mi tekst (BOTTOM VIEW) i z tego powodu byłem przekonany, że widok jest od góry a nie od dołu. Czujnik nie miał prawa działać - teraz już nigdy nie zapomnę jak powinien być podłączony - dwa pójdą do kosza skoro były odwrotnie podłączone.
    Niemniej cała dyskusja bardzo mi się przydała i nauczyłem się kilku nowych rzeczy. Dziękuję wszystkim za pomoc i cierpliwość :)

    0
  Szukaj w 5mln produktów