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.

M8 DS18B20 LED - DS18B20 format wyniku i podział na cyfry

Jarosław J 27 Gru 2012 18:39 3792 27
  • #1 27 Gru 2012 18:39
    Jarosław J
    Poziom 14  

    W ramach przygotowań do odczytu z DSa napisałem taki program testowy, aby sprawdzić poprawność wyświetlania. I tak w funkcji main, licznik zlicza zmienną "liczba" od 10 do -10
    za każdym razem liczba przekazywana jest do funkcji "oblicz" gdzie wykonywany jest podział na poszczególne cyfry.
    Myślałem, że nie będę miał problemów z obliczeniem poszczególnych cyfr, bawiłem się tym w bascomie, ale jednak poległem. Mój program oblicz wygląda tak:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Pozostaje jeszcze kwestia wyświetlenia minusa, ale myślę, że z tym sobie poradzę.

    Nie mam innego pomysłu na właściwe podzielenie liczby na cyfry. Cały program wygląda tak jak poniżej. Może gdzieś indziej tkwi problem? Program nie jest długi może ktoś spojrzy.

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0 27
  • #2 27 Gru 2012 19:04
    Krauser
    Poziom 26  

    Można tak:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Zwróć uwagę, że ta funkcja pobiera liczbę uint8_t, a powinna raczej pobierać int16_t

    0
  • #3 27 Gru 2012 19:15
    Jarosław J
    Poziom 14  

    No tak masz rację Ja na początku zadeklarowałem ją jako int8_t ale zapomniałem o zapisie w funkcji.
    Czy jednak nie wystarczy int8_t? z tego co wiem to liczy ona od 127 do -128 a ja zliczam na razie do 10.
    Twój przykład nie działa. Pokazuje same zera. Bo w sumie jak liczbę na przykład 5 podzielić na 10 żeby została reszta? Czytałem kiedyś o tym jak to uzyskać, ale za chiny nie mogę tego znaleźć.

    0
  • #4 27 Gru 2012 19:34
    tmf
    Moderator Mikrokontrolery Projektowanie

    Tak to nie zadziała z liczbami typu signed. One są zapisywane w kodzie U2 i modulo się na tym wykłada. Trzeba wyliczyć wartość bezwzględną liczby, a potem ją rozbijać na cyfry przy pomocy modulo i dzielenia przez 10. Możesz też po prostu użyć gotowca - ltoa, itoa itd.

    0
  • #6 27 Gru 2012 20:12
    Jarosław J
    Poziom 14  

    Z tego co widziałem, to funkcja z pliku ds18b20 zwraca temperaturę jako int16_t czyli ze znakiem. Nie wiem jak to przekonwertować ani co dostanę po konwersji (jak znam życie to wynik będzie odniesiony do 1024 i jeśli 22 stopnie w przypadku int16 będzie częścią 512 bitów tak po zmianie typu na uint16 będzie częścią z 1024)

    Co do wartości bezwzględnej nie byłoby to trudne bo wystarczyłoby odczytać najstarszy bit jeśli jest 1 to temperatura ujemna. Trzeba by było tylko zmodyfikować funkcję tmfa, żeby zwracał oddzielnie ten bit. oraz inaczej dzielił wynik bezwzględny ( a to jeszcze dla mnie kosmos)

    co do itoa i pokrewnych to konwersja typów pewnie zeżre dużo pamięci.

    Nie wiem co wybrać. Co będzie bardziej korzystne?

    0
  • #7 27 Gru 2012 20:18
    tmf
    Moderator Mikrokontrolery Projektowanie

    Te funkcje zwracają dane w U2, więc nie ma potrzeby aby osobno traktować bit znaku. itoa za dużo pamięci nie zeżre, prawie na pewno nie więcej niż jakieś domowe sposoby konwersji.

    0
  • #8 27 Gru 2012 20:26
    michalko12
    Specjalista - Mikrokontrolery

    Link - Poczytaj, masz tam też przykład

    0
  • #9 27 Gru 2012 20:46
    Jarosław J
    Poziom 14  

    Czyli pozostaję przy itoa....
    itoa konwertuje int do stringa ale stringa jak podzielić na cztery cyfry? bo chyba dzieleniem się nie uda?
    Chyba, żeby znowu stringa zamienić na integer ale co wówczas zyskamy?


    Powyższy link wiele wyjaśni. Zapomniałem o nim.

    0
  • #10 27 Gru 2012 20:59
    BlueDraco
    Specjalista - Mikrokontrolery

    Ech, macie problemy.. Oto cała konwersja z DS18B20 w wersji praktycznej (od -199 do + 199 stopni, czyli więcej, niż potrafi DS), szybko, bez bibliotek i bez dzielenia przez duże liczby:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • #11 27 Gru 2012 21:26
    Jarosław J
    Poziom 14  

    No nie źle BlueDraco :) Widzę tylko, że przygotowane jest to pod jakiś terminal?
    mimo to i tak nie wiem jak to zaaplikować pod 4 wyświetlacze LED.
    Co to są za wyrażenia z *p ? Jak to jest odczytywane?
    Co to za cyfry między apostrofami '1' jak tego używać ?

    0
  • #12 27 Gru 2012 21:41
    piotrva
    Moderator na urlopie...

    Moim zdaniem nie ma sensu konwertować tego na stringa, potem znowu na liczby.
    W moim sposobie wykorzystuję tylko jeden dodatkowy bajt na zapisanie znaku i nie mnożę żadnych buforów, stringów itp. (przynajmniej jeśli to ma być wyświetlane tylko na LED'ach.

    0
  • #13 27 Gru 2012 22:33
    michalko12
    Specjalista - Mikrokontrolery

    Jarosław J napisał:
    No nie źle BlueDraco :) Widzę tylko, że przygotowane jest to pod jakiś terminal?
    mimo to i tak nie wiem jak to zaaplikować pod 4 wyświetlacze LED.
    Co to są za wyrażenia z *p ? Jak to jest odczytywane?
    Co to za cyfry między apostrofami '1' jak tego używać ?


    W pierwszej kolejności zapoznaj się lepiej z C. To są podstawy. To jak przenieść wynik na LED to tak naprawdę zależy od ich podłączenia do układu i bez zewnętrznych dekoderów operacja typu LEDDIGIT[0] = 1; nie spowoduje wyświetlenia na LED wartości 1.

    BlueDraco napisał:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Czepię się... Trochę konsekwencji w składani zwłaszcza gdy coś przedstawiasz początkującemu. Chodzi mi o zapis *p++ i *p ++. W praktyce oczywiście żadnej różnicy nie ma, ale jak już, to bliżej prawdy jest * p++ a najbliżej *(p++). W tym przypadku i tak to nie ma znaczenia bo już *p jest zaporą.

    Dodano po 36 [minuty]:

    Wracając do problemu
    Przykład:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Poprawka:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • #14 27 Gru 2012 23:06
    Jarosław J
    Poziom 14  

    michalko12 napisał:
    operacja typu LEDDIGIT[0] = 1; nie spowoduje wyświetlenia na LED wartości 1.

    Mimo to w przykładzie oryginalnym tmfa wygląda to tak:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    i to działa, na poszczególnych wyświetlaczach pojawiają się kolejne cyfry.

    Ale Twój przykład
    Kod: c
    Zaloguj się, aby zobaczyć kod

    jest tym czego szukałem. Właśnie o to chodziło, żeby otrzymaną liczbę rozłożyć na wyświetlacze.
    Martwi mnie tylko to co napisał tmf, że liczby otrzymanej z funkcji do obsługi DS18b20 nie da się w ten sposób rozłożyć.

    W dalszym ciągu też nie rozumiem kodu przesłanego przez BlueDraco
    Wytłumaczcie mi proszę choćby pierwszą linijkę

    Kod: c
    Zaloguj się, aby zobaczyć kod


    rozumiem że p jest zmienną która jest inkrementowana
    Nie wiem jednak co oznacza gwiazdka przed p i dla czego porównana jest z jedynką w apostrofach. co ma na celu taki zapis?

    0
  • Pomocny post
    #15 27 Gru 2012 23:29
    BlueDraco
    Specjalista - Mikrokontrolery

    To jest konwersja na ASCII, np. do wyświetlenia na LCD lub terminalu. Pytania, które zadajesz, nie dotyczą mikrokontrolerów, a podstaw języka C. p jest zmienną wskaźnikową, czyli adresową. *p ++ = x - to zapis x pod adres zawarty w p i zwiększenie tego adresu, tak, aby p wskazywała następny bajt bufora. Jedynka w apostrofach - to znak ASCII - cyfra 1. Podobnie każdy inny symbol w apostrofach. Jeżeli temperaturę chcesz wyświetlać na wyświetlaczu LED, to raczej ma ona być wyrównana prawostronnie (ostatnia cyfra zawsze z prawej). Wtedy kod będzie nieco inny (prostszy) i nie ma sensu robić konwersji na ASCII, tylko od razu na obraz segmentów wyświetlacza - nie ma nawet potrzeby składowania wartości wyświetlanych cyfr w buforze. Byłoby to coś takiego:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Dlaczego pożyteczna jest dodatkowa zmienna val? Dlatego, że dla niemal każdego procesora dzielenie to mordęga, a im dłuższe dane, tym większa. val ma tylko 8 bitów i dzielenie powinno być znacznie szybsze niż wtedy, gdyby miała 16, tak, jak tval. AVR - to w końcu procesor 8-bitowy i operacje na danych 8-bitowych są w nim kilka razy szybsze, niż na 16-bitowych.

    0
  • #16 27 Gru 2012 23:39
    Jarosław J
    Poziom 14  

    Dzięki BlueDraco. Ja przygodę z C zaczynam właśnie od mikrokontrolerów i nie spotkałem wcześniej takiego zapisu. Faktycznie Twój zapis jest prostszy niż to co chciałem zrobić. Spróbuję to wszystko zespolić i wypróbować, nie wiem czy jeszcze dziś dam radę. Tymczasem dzięki - "Pomógł"

    0
  • #17 28 Gru 2012 11:52
    miszcz310
    Poziom 19  

    Ponieważ zauważyłem, że kolega ma problemy z zapisem wskaźnikowym dlatego ze swojej strony mogę polecić książkę:
    Książka: Head First C
    Żeby nie było, ta książka jest udostępniana ZA DARMO przez wydawcę na ich stronie. Moim zdaniem jak chcesz się szybko i w miarę bezstresowo przynajmniej zapoznać z C to to jest idealna książka. Ja na razie jestem trochę za połową i moim zdaniem super, bardzo fajnie przedstawione. Jako minusy to mogę powiedzieć, że zauważyłem dwa: czyli jest po angielsku (ale nie ma tam jakiś Bóg wie jakich konstrukcji, albo słownictwa), dwa to mocno wspierają linuxa (czy to w ogóle można uznać za wadę). Niektórym może nie odpowiadać szata graficzna, ale moim zdaniem dzięki temu ta książka nie jest sucha. Jeszcze raz polecam!

    0
  • #18 30 Gru 2012 18:40
    Jarosław J
    Poziom 14  

    Opanowałem całkowicie wyświetlanie na wyświetlaczu LED liczby w formie zmiennej uint16_t, z wyłączeniem wyświetlaczy na których jest "0" (oprócz cyfry jedności) oraz z wyświetlaniem segmentu "minus" w zależności od zmiennej znak.
    Poniżej funkcja "oblicz", może komuś się przyda, może też ktoś ją ulepszy. Zastosowałem tam dodatkową zmienną lokalną "liczba1". Jak na razie wszystko działa jak należy.

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Podsumowując muszę jeszcze:

    1. objąć atrybutem ATOMIC_BLOCK fragmenty odmierzające najkrótsze czasy
    2. wywołać funkcje z Main
    3. wykonać konwersję na zmienną uint16_t o nazwie liczba.

    Wywołanie funkcji wyobrażam sobie tak na razie na _delay_ms (750):
    Kod: c
    Zaloguj się, aby zobaczyć kod



    Natomiast funkcją Atomic Block tak aby nie zakłóciły jej przerwania, obejmę dwie funkcje:
    uint8_t OW_Read()
    void OW_Write(uint8_t byte)

    i funkcję
    uint8_t OWI_Search(uint8_t cmd, uint8_t *aID, uint8_t deviationpos)

    W nich są odmierzane te najkrótsze czasy 60 - 150 mikrosekund
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Wcześniej oczywiście dołączam bibliotekę util/atomic.h
    Czy w ten sposób będzie działać?

    Pozostaje jeszcze konwersja wyniku ale ją pozostawię na później.

    0
  • #19 30 Gru 2012 18:52
    BlueDraco
    Specjalista - Mikrokontrolery

    W taki sposób zepsujesz wyświetlanie. Najwygodniej zrobić Onewire na timerze z eleganckim automatem programowym, tylko że na biedniutkich AVR masz jednopoziomowy system przerwań, a tu aż się prosi o wyższy priorytet dla timingu Onewire i niższy dla wyświetlacza. Można oczywiście kombinować z przerywalnym przerwaniem wyświetlacza (SIGNAL czy jakoś tak - eksperci od AVR podpowiedzą). Można też udać, że zakłóceń wyświetlania nie ma. Można wreszcie tak sprytnie oprogramować wyświetlanie (b. krótka procedura), że nie będzie ono wpływało na zbyt duży rozjazd czasu Onewire.

    0
  • #20 30 Gru 2012 19:31
    tmf
    Moderator Mikrokontrolery Projektowanie

    Nie wszystkie AVRy mają jednopoziomowy system przerwań. Ale to i tak nic nie zmienia. Jeśli nawet wykorzystamy wielopoziomowy system to mamy wybór między dżumą a tyfusem. Albo wyższy priorytet będzie miał LED i będzie kaszanił transmisję OW, albo wyższy priorytet będzie miał OW, ale będzie kaszanił wyświetlanie LED. Albo zrobimy to jak należy i priorytety nie będą odgrywały roli.
    Z drugiej strony najłatwiej zrobić OW na USART, ale z tego co pamiętam autor już wybrał procka i teraz trzeba robić protezy, stąd problem. Przede wszystkim sprawdziłbym czas wykonywania przerwań wyświetlacza LCD. Jeśli jest on krótki to nie spowoduje rozjechania komunikacji na OW. W przeciwnym przypadku zacząłbym od delayów. Zakładając, że obsługa przerwania jest mimo wszystko krótka, to zamiast delay dałbym opóźnienie oparte o inny timer, liczący mikrosekundy. Wtedy przerwanie LED nie będzie bruździć, bo to co zeżre i tak delaya nie wydłuży. Potencjalnie krytyczne wtedy będą tylko krótkie (1-3us) impulsy ujemne sygnalizujące sloty OW. I tylko one będą musiały być w sekcjach krytycznych, a że sekce te będą trwały max kilka us, to wyświetlania nie zaburzą.

    0
  • #21 30 Gru 2012 20:49
    Jarosław J
    Poziom 14  

    Procesor mam taktowany oscylatorem wewnętrznym 8MHz, z preskalerem ustawionym na podział przez 8
    W obsłudze przerwania mam tyle co poniżej
    Policzy mi ktoś, co ile to przerwanie występuje i ile trwa?

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Jakby jeszcze ktoś spojrzał na to jak wtrąciłem Atomic_Block
    oraz na samo wywołanie funkcji konwersji i pomiaru (dwa posty wyżej)

    0
  • #22 30 Gru 2012 21:16
    miszcz310
    Poziom 19  

    Hmmm...
    No jak taktowanie 8MHz i preskaler ustawiony na 8 i przerwanie od przepełnienia bufora to będzie:
    f=8MHz/8/cnt, gdzie cnt to liczba zliczeń timera0, czyi chyba 256...

    f=8*10^6/2^10=7,8125 kHz

    T=1/f=0,128ms

    Tylko trochę nie rozumiem. Piszesz funkcje do obsługi wyświetlaczy i transmisji 1wire a czegoś takiego nie możesz policzyć.

    Pytasz ile trwa. Skompiluj sobie tylko tą funkcję, podejrzyj sobie kod w asm porównaj z tabelą rozkazów i będziesz znał ile cykli to zajmuje. Potem pomnóż sobie liczbę cykli przez odwrotność częstotliwości taktowania i gotowe.

    0
  • #23 30 Gru 2012 21:34
    Jarosław J
    Poziom 14  

    Sam też chyba coś pokręciłeś

    Piszesz f=8MHz/8/cnt
    a poniżej liczysz
    f=8*10^6/2^10=7,8125 kHz

    Jeśli podstawiać do wzoru
    to 8000000/8/256 =3906.25 czyli 3,90625 khz

    1/3,90625 khz = 0,000256s czyli co 256 mikrosekund.

    0
  • #24 30 Gru 2012 21:37
    BlueDraco
    Specjalista - Mikrokontrolery

    Jeśli zrobisz obsługę OW na przerwaniu timera baz głupotek typu delay, to nie będzie tam oczekiwań i nie będzie to zakłócało wyświetlania. Jeśli dobrze zrobisz wyświetlanie, to nie zakłóci to obsługi OW na przerwaniu timera. Więc pewne szanse powodzenia są.

    0
  • #25 30 Gru 2012 21:49
    Jarosław J
    Poziom 14  

    OK Blue_Draco Czyli mam do wykorzystania
    timer1 Atmegi8 Jest on 16 bitowy
    timer2 - 8 bitowy
    Przypuśćmy że wykorzystam
    timer2 bez prescalera to
    wykorzystując powyższy wzór bez ładowania żadnej wartości do timera mam na czytsto
    1/ 8000000 /256= przerwanie co 32 mikrosekundy to chyba za mało do odmierzenia kilku mikrosekund
    nie wiem jak będzie z timerem 16 bitowym

    0
  • #26 30 Gru 2012 21:59
    BlueDraco
    Specjalista - Mikrokontrolery

    Ależ nie. Timer do OW ma odmierzać odcinki czasu dla OW - zasadniczo dwa odcinki, odmierzające na ogół czasy jednego bitu. Przy inicjowaniu transmisji to są nieco inne odcinki. Taka obsługa OW nie jest łatwa do napisania - na LPC znajdziesz w sieci gotowce, nie wiem, jak na AVR, ale to praktycznie jedyne rozwiązanie nie wchodzące w konflikt z innymi fragmentami oprogramowania - w Twoim przypadku z obsługą wyświetlacza.

    0
  • #27 30 Gru 2012 22:42
    miszcz310
    Poziom 19  

    Nie to, że chcę robić komuś jakąkolwiek reklamę, ale Pan Mirosław Kardaś (na elektrodzie Mirekk36) opisał ten problem i sposób na jego rozwiązanie w swojej pierwszej książce. Sposób z tego co zrozumiałem na takiej zasadzie jaką opisuje kolega BlueDraco. Polecam na zapoznanie się z tą pozycją (np. jak masz dostęp do biblioteki z tą książką, albo masz kasę do wydania), ale wydaje mi się że jest to opowiedziane/opisane na jego blogu.


    P.S. Ogólnie jak chcesz wiedzieć ile czasu zajmuje dana część kodu to jeszcze są symulatory i tam są opcje do odmierzania czasu (chociażby ten z AVR Studio).

    0
  • #28 31 Gru 2012 12:09
    tmf
    Moderator Mikrokontrolery Projektowanie

    Jeśli taktujesz MCU zegarem 1 MHz (tak wychodzi z preskalera) to okres zegara wynosi 1us, tyle ile mniej więcej trwa impuls ujemny OW. Przy tak niskim taktownaiu można to zrobić, ale lepiej podnieść je do 4-8 MHz, co da więcej czasu. Tak jak pisałem, umieść w sekcji atomowej samo generowanie impulsu ujemnego, a delaye oprzyj o timer i będzie ok.
    BTW, czas wykonania funkcji bada się w symulatorze Atmel Studio - jak odpalisz symulację to masz tam licznik czasu, ustrawiasz breakpointa na wejściu i wyjściu, różnica to czas wykonania - sprawdź tylko ustawienie częstotliwości w symulatorze i ew. przelicz wynik.
    Impuls da się też generować timerem, ale wymagałoby to dodatkowego układu elektronicznego, gdyż ATMega nie ma możliwości konfiguracji wyjścia jako OC i trzeba to dorobić. Można to też zrobić programowo, ale nie przy taktowanu 1MHz.

    0
  Szukaj w 5mln produktów