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

[atmega8][C] - Komunikacja ATmegi8 z termometrem cyfrowym DS18b20.

09 Paź 2015 21:57 1197 15
  • Poziom 13  
    Szanowni Forumowicze,
    od pewnego czasu interesuję się programowaniem mikroprocesorów. Wykonałem już kilka bardzo prostych projektów jak woltomierz cyfrowy czy termometr analogowy (mcp9700) z wyświetlaniem wyniku na LCD (HD44780). Problem mam jednak ze zrozumieniem sterowania elementami cyfrowymi. O ile w przypadku np. termometru mcp9700 sprawa jest prosta (bo temperatura jest funkcją napięcia na wyjściu to sobie to bez problemu przeliczę wykorzystując ADC i notę katalogową) to w przypadku termometru cyfrowego Dallas DS18b20 nie wszystko jest dla mnie takie jasne. Wiele rzeczy rozjaśnił mi fantastyczny artykuł znaleziony na elektrodzie: www.elektroda.pl/rtvforum/download.php?id=454850

    Przechodzę do konkretów - mam kilka pytań odnośnie kodu z powyższego artykułu:

    1)
    Kod: C
    Zaloguj się, aby zobaczyć kod
    - dobrze rozumiem, że funkcja 'bit_is_set(port, pin)' jest zdefiniowana w pliku nagłówkowym io.h i sprawdza czy na danym pinie mamy stan wysoki?

    2) na stronie 22. w listingu kodu z komentarzem "Funkcja odbiera bajt z magistrali" w piątej linijce od dołu jest użyta zmienna 'iCounter', która to nie jest nigdzie wcześniej w kodzie zadeklarowana. To jest jakaś zmienna z pliku io.h czy to błąd/literówka w artykule?

    3) i chyba dla mnie najważniejsze - na stronie 23. w funkcji głównej programu w pętli głównej programu. W kilku miejscach jako argumenty funkcji podaje się adresy hex podane w dokumentacji termometru odpowiedzialne za konkretnie funkcje. Rozumiem ideę - wysyłam odpowiednie bity konfiguracyjne do termometru żeby on robił co chcę. Ale czy podając do funkcji argument w postaci adresu hex mikroprocesor wie w które miejsce w termometrze wysłać dany bit? I czy adres hex jest zgodny z typem danych char?
    Właśnie tego najbardziej nie rozumiem - jak to się dzieje, że mikroprocesor wie w jakie miejsce wysłać dane nawet jeśli ma jego adres skoro mikroprocesor na wyjściu może jedynie ustawiać stan wysoki/niski?

    Proszę o wskazówki i pozdrawiam :)
  • Pomocny post
    Specjalista - Mikrokontrolery
    Co do 3: Przeczytaj jeszcze raz dokumentację układu. Chyba bardzo czegoś nei zrozumiałeś. Do termometru wysyła się ciąg bitów, z których pierwsze 8 stanowi polecenie, a nestępne - jego ew. argumenty. W układzie DS18B20 nie ma żadnych "adresów" ani "miejsc do wysyłania bitów".

    Ani mikrokontroler, ani DS18B2 nie wiedzą, co to jest "hex". W prgramie zapisuje się stałą w postaci szesnastkowej, bo tak jest wygodniej dla programisty. Dwucyfrowa stała szesnastkowa ma 8 bitów, czyli tyle, ile zmienna typu uint8_t. Odradzam stosowanie typu char, który na ogół jest traktowany jako liczba ze znakiem. uint8_t to to samo, co unsigned char.
  • Pomocny post
    Poziom 27  
    1. Makro "bit_is_set" jest zdefiniowane w pliku nagłówkowym sfr_defs.h. Sprawdza ono czy dany bit w rejestrze mikrokontrolera jest ustawiony. Nie koniecznie musi to być pin (nóżka). Polecam wpisanie w goolach "avr bit_is_set" - w pierwszym czy drugim linku znajdziesz kod tego makra i dokładny opis wspomnianego pliku nagłówkowego.
    2. Literówka. Powinno być ucCounter.
    3. Te wielkości w hex to nie adresy. To są komendy wysyłane do termometru. Np. wysłanie do termometru rozkazu (wartości) 0x44 rozpoczyna pomiar temperatury.
  • Poziom 13  
    Masz rację BlueDraco - bardzo czegoś nie zrozumiałem :) właśnie tak czułem, że mikrokontroler ani termometr nie wiedzą co to jest hex, stąd wyszedł mi mały mętlik.
    szczywronek - dziękuję za rozwianie niepewności :)

    Czyli funkcje 'v1Wire_SendByte' i 'uv1Wire_ReadByte' służą tylko do tego, żeby wysłać odpowiedni rozkaz do termometru?
    I dlaczego do zmiennych 'cTemperatureL/H' przypisuję tę samą funkcję? Zmienne nie będą miały wtedy tych samych wartości?
  • Pomocny post
    Poziom 27  
    dawid.barracuda napisał:
    Czyli funkcje 'v1Wire_SendByte' i 'uv1Wire_ReadByte' służą tylko do tego, żeby wysłać odpowiedni rozkaz do termometru?
    Te funkcje, jak sama nazwa wskazuje, wysyłają i odczytują bajt danych z magistrali 1wire. W przypadku ds18b20 ten bajt danych to może tym rozkaz dla termometru (np. wspomniany już start pomiaru) lub dane (np. zmierzona wartość temperatury). Przesyłanie rozkazów i danych odbywa się tak samo i z wykorzystaniem tych samych funkcji!

    dawid.barracuda napisał:
    dlaczego do zmiennych 'cTemperatureL/H' przypisuję tę samą funkcję?
    Kłaniają się podstawy języka C :) Do zmiennych nie przypisujesz funkcji tylko wartość zwracaną przez funkcję. W tym wypadku jest to wartość odebrana z magistrali 1wire (bo tak działa funkcja ..._ReadByte). Termometr działa w ten sposób, że po odebraniu rozkazu 0xBE zaczyna odsyłać zawartość swojej wewnętrznej pamięci. Znajduje się w niej m.in. wartość temperatury. Kilkukrotne wywołanie funkcji cośtam_ReadByte() powoduje odczytywanie kolejnych bajtów wysyłanych przez dallasa. Każde wywołanie funkcji to odczytanie jednego bajtu. W tym wypadku odczytywane są tylko 2B - to one zawierają temperaturę.
  • Poziom 13  
    Rozumiem :)
    Nie rozumiem teraz innej rzeczy. Mianowicie, w artykule jest napisane:
    Wysłanie bitu polega na ustawieniu magistrali w stan niski, następnie jeśli po czasie min. 1μs zostanie ona ustawiona w stan wysoki, równoznaczne jest to z wysłaniem jedynki. Jeśli z kolei po wyczyszczeniu magistrali nie nastąpi w określonym czasie podciągnięcie jej do jedynki to uznaje się to za wysłanie zera logicznego. Funkcja:

    Kod: C
    Zaloguj się, aby zobaczyć kod


    No to tak - najpierw ustawiamy magistralę w stan niski i czekamy - pierwsze dwie linijki powyższej funkcji. Jeśli argumentem funkcji jest 1 to wykonujemy to, co znajduje się w funkcji if. I tutaj mam problem - czy w tym miejscu nie powinno być zakończenia wykonywania funkcji i czy nie powinno być dodane wyrażenie else przed opóźnieniem 80us powodującym wysłanie zera?

    [/code]
  • Pomocny post
    Moderator Mikrokontrolery Projektowanie
    Nie, jest ok. Jeśli wysyłasz 0 to powinno być 0 przez 80 us, jeśli 1 to powinno być 1 przez 80 us. Jak widzisz w obu przypadkach kod opóźnienia jest taki sam, stąd też nie ma potrzeby wykonywania kodu alternatywnego (else).
  • Poziom 13  
    Czyli wyczyszczenie magistrali następuje w pierwszej linijce tej funkcji, tak?

    Mam cały program i coś jest nie tak. Temperaturę z MCP9700 odczytuje prawidłowo, a z dallasa mam ciągle "cisza". Poza tym wszystko działa bardzo wolno i jak wyjmę kondensator filtrujący spomiędzy pinów MCP9700 to nie reaguje na zmianę napięcia. Procesor to ATmega8 i jest ustawiony na zewnętrzny kwarc 16MHz, start-up time: 16CK + 64ms.
    Do kodu musiałem zlinkować "hd44780.c" bo inaczej program się nie kompilował i nie wiem co zrobić, żeby program działał bez tego - wiem, że nie linkuje się plików *.c
    Oto kod programu:
    Kod: C
    Zaloguj się, aby zobaczyć kod


    Proszę o wskazówki co popsułem i pozdrawiam.
  • Poziom 27  
    dawid.barracuda napisał:
    Czyli wyczyszczenie magistrali następuje w pierwszej linijce tej funkcji, tak?
    Nie :) Magistrala 1w jest domyślnie w stanie wysokim. Stan ten jest wymuszany przez rezystor podciągający ją do "plusa".

    Wysłanie bitu polega na ustawieniu magistrali w stan niski, następnie jeśli po czasie min. 1µs zostanie ona ustawiona w stan wysoki, równoznaczne jest to z wysłaniem jedynki. Jeśli z kolei po wyczyszczeniu magistrali nie nastąpi w określonym czasie podciągnięcie jej do jedynki to uznaje się to za wysłanie zera logicznego.

    Zgodnie z opisem: aby wysłać bit należy najpierw wymusić na magistrali stan niski na min 1µs - robią to dwie pierwsze linijki funkcji.
    Teraz: jeśli wysyłamy 1 to magistrala ma wrócić do stanu wysokiego - to robi warunek z if. I ma w tym stanie pozostać jakiś tam czas -> za to odpowiada delay. Jeżeli wysyłany jest bit 0 to warunek if nie będzie spełniony -> magistrala dalej będzie w stanie niskim przez cały okres delaya.
    Na końcu funkcji przywracany jest stan podstawowy magistrali - stan wysoki. Jeżeli wysyłany bit był 1 to nic się nie zmienia, bo magistrala została zwolniona już w warunku if; jeżeli wysyłane było 0 to if nie był spełniony i dopiero teraz magistrala wraca do stanu wysokiego.
  • Poziom 13  
    No dobra, tę część rozumiem :)
    Ale nie wiem czemu program nie chce w ogóle zacząć odczytu temperatury - jak napisałem w poprzednim poście ciągle mam "cisza", czyli nawet impuls RESET nie chce zostać wysłany jak należy i nie wiem w czym tkwi problem.
  • Poziom 27  
    Co do kodu:
    1. Na początek zrezygnuj z przerwań. Obsługa 1wire wymaga dużej precyzji "czasowej" - opóźnienia liczą się w µs. Jeżeli w czasie trwania komunikacji 1wire pojawi się przerwanie to zależności czasowe się rozjadą. Jak już będzie działać to możesz wrócić do przerwań, ale wyłączać je na czas wysyłania/odbierania bitów z 1w. Lub jeszcze lepiej - obsługiwać 1wire z wykorzystaniem uartu czy timera.
    2. Co to znaczy "cisza"? (nie było pytania :))
    3. Zamiast linkować *.c rozwiń "program się nie kompilował" - pokaż log z kompilacji.
    4. F_CPU nie powinno być zdefiniowane w kodzie programu, tylko w opcjach projektu (klik)
    5. Nie musisz inkludować "ręcznie" sfr_defs.h -> już jest inkludowany z io.h
    6. Wszystko działa wolno, bo w pętli głównej masz ogromnego delaya (750ms) i operacje na floatach.
  • Poziom 13  
    Wyrzuciłem z programu obsługę mcp9700 (więc i przerwania) i dalej "cisza".
    Lub jeszcze lepiej - obsługiwać 1wire z wykorzystaniem uartu czy timera.
    Czy chodzi Ci o to, żeby zamiast funkcji delay używać sprzętowego timera do pomiaru opóźnień?[/b][/i]

    Znalazłem jeszcze takie coś w reference na stronie atmela: The maximal possible delay is 768 us / F_CPU in MHz.
    W moim przypadku 768/16=48 us. W przypadku przekroczenia tej wartości funkcja delay_us przechodzi w delay_ms. Czy to może mieć znaczenie w moim przypadku?
  • Moderator Mikrokontrolery Projektowanie
    Tak, jeśli masz taką możliwość to najlepiej użyć sprzętu. W stopce z moich postów masz linki do książek - a na ich stronach znajdziesz darmowe przykłady, m.in. do realizacji obsługi 1-wire przy pomocy UART, a także tak jak to robisz do tej pory (tyle, że przykłady działają :) ). Popróbuj. Zapewne twój problem leży w niedokłądnych czasach, na to 1-wire jest czułe.
    Jeśli nie działa ci nawet odpowiedź na reset pulse to sprawdź połączenia i czy masz zewnętrzne podciąganie do Vcc magistrali OW.
  • Moderator Mikrokontrolery Projektowanie
    Nie. Użycie timerów w ten sposób niewiele zmienia w stosunku do zwykłej pętli. Albo OW robi się sprzętowo z wykorzystaniem UART i opóźnienia generuje moduł UART, albo wykorzystuje się timer i jego przerwanie do zmiany stanu IO. Ew. można wykorzystać możliwość bezpośredniego sterowania pinem IO przez timer. To mocno zależy od użytego procka.
  • Poziom 13  
    Odnośnie sposobu nr 2 - czyli chodzi o takie
    wykorzystanie:
    http://mikrokontrolery.blogspot.com/2011/03/led-sterowany-przez-timer.html?m=1 ostatni listing? I wtedy przerwanie, a w ramach obsługi przerwania po prostu odpowiedni interwał czasowy?

    Nie do końca wiem jak mam użyć przerwań. Póki co, to o ile się orientuje, to aby uzyskać zliczanie impulsów timera co 1us to mogę np. jako źródło taktowania ustawić wewnętrzny oscylator RC na 8MHz i preskaler na 8. Wtedy będę bardzo zbliżony do 1us. Ale jak zrobić, żeby timer włączał się i liczył tylko w tych miejscach w kodzie, gdzie potrzebuję opóźnienie? W przykładzie z linku wyżej mam timer liczący cały czas a w pętli głównej programu nic nie ma, ale u mnie te opóźnienia użyte są w funkcjach. Jak mogę to zrobić żeby było prawidłowo?

    Dodam, że czujnik działa, bo sprawdziłem go za pomocą Arduino UNO R3 i gotowych bibliotek do obsługi tego termometru.