logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Atmega8 + LCD 2x16 jak wyświetlić liczby zmiennoprzecinkowe?

bartek_duszynski 05 Sty 2010 11:08 6516 14
REKLAMA
  • #1 7487735
    bartek_duszynski
    Poziom 11  
    Witam, jestem w trakcie tworzenia pierwszego projektu na mikrokontrolerze więc proszę o wyrozumiałość. Buduje kalkulator na zaliczenie laboratorium z mikroprocesorów. Ze wszystkim jakoś dawałem rade szukając samemu informacji w sieci jednak mam problem z wyświetlaniem liczb zmiennoprzecinkowych na LCD 2x16. Czy ktoś mógłby mi pokazać/podpowiedzieć jak to zrobić. Pytanie jest pewnie banalne ale nie mogę sobie dać samemu rady z tym problemem. Z góry dzięki za odpowiedź

    Dodam że korzystam z biblioteki h44780 do obsługi wyświetlacza
  • REKLAMA
  • Pomocny post
    #2 7487949
    wladziu22
    Poziom 17  
    Hey.
    Zrob tak:

    Pierwszy sposób:
    
    #include <stdlib.h> 
    ....
    float Wynik = 1.123;
    ....
    dtostrf(tmp,6,3,pomiar);         //zamiana float na string
    wypiszNaLCD(pomiar);            // twoja funkcja wypisująca ciąg znaków
    


    Drugi sposób (użycie printf-a):
    W pliku Makefile dodajesz:
    
    # If this is left blank, then it will use the Standard printf version.
    #PRINTF_LIB = 
    #PRINTF_LIB = $(PRINTF_LIB_MIN)
    PRINTF_LIB = $(PRINTF_LIB_FLOAT)
    ....
    ....
    MATH_LIB = -lm (<- to w zasadzie powinno już być ale sprawdź)
    


    Następnie w pliku main.c dodajesz:
    
    #include <avr/pgmspace.h>
    #include <stdio.h>
    

    określasz standardowe wyjście (ew. wejście):
    
    FILE LCD=FDEV_SETUP_STREAM(WypiszCiagZnakow,NULL,_FDEV_SETUP_WRITE);
    

    gdzie WypiszCiagZnakow() to twoja funkcja jak sama nazwa wskazuje wypisuje string na LCD.
    i na koncu uzywasz za każdym razem wypisujac jakis wynik np:
    
    fprintf_P(&LCD,PSTR("U = %5.3fV"), tmp_nap * (5.0 / 1024));
    

    działanie: tmp_nap * (5.0 / 1024) oczywiście w moim przykładzie da liczbę zmiennoprzecinkowa.

    Myślę, ze się nie pomyliłem :)
    A jeśli tak to pewnie zaraz ktoś na mnie nakrzyczy ;-)
    aha..dodam ze przecinek sporo pamięci zżera co sam się przekonałem. A powyższe przykłady jeszcze więcej :)

    Ja np aby uszczuplić kod używam działań bez przecinka, a następnie wyświetlam kolejno każdy wynik dodając "." w odpowiednim miejscu. Np:
    
    #include <stdlib.h> 
    ....
    char szPomiar[16];
    int iWynikPomiaru = 1234;
    .....
    .....
    wypiszNaLCD("Wynik=");  //twoja funkcja
    itoa(iWynikPomiaru,szPomiar,10); // zamiana int na string,wywolanie tej funkcji wymaga #include <stdlib.h> 
    ...
    sendsign(szPomiar[0]); //twoja funkcja
    sendsign('.');
    sendsign(szPomiar[1]);
    sendsign(szPomiar[2]);
    sendsign(szPomiar[3]);
    

    Powyższe 5 krotne wywołanie sendsign() może wiązać się z pustym znakiem wiec to tez trzeba ew. przewidzieć. Bez przecinka będzie to oczywiście prościej wypisać :) :
    
    wypiszNaLCD(szPomiar);
    


    Widzę ze masz Atmege8 więc raczej powinieneś mieć możliwość użycia pierwszego sposobu. Zresztą skompilujesz i będziesz widział ile pamięci potrzebujesz by wgrać całego .hexa. A jak będziesz to ignorował i miał za mało pamięci to procek będzie się resetował/ zawieszał :)
    Pozwodzenia
  • REKLAMA
  • Pomocny post
    #3 7487978
    elektronik12z
    Poziom 13  
    Witam

    U mnie ta funkcja dtostrf zabiera sporo pamięci, napisałem własną, która zabiera 5 razy pamięci programu:


    //Konwersja float to str
    void ToString(char *dest, float value)
    {
     //format A.BB	 
     long A=0;
     long B=0;
    
     A=value;
     B=value * 100;
     B=B-A*100;
    
     char sA[3];
     char sB[6];
    
     itoa(A,sA,10);  
     itoa(B,sB,10); 
    
     
     strcat(dest,sA);
     strcat(dest,".");
     strcat(dest,sB);
    
    
    }


    Działa, oczywiście nie jest ładna i pewnie można to napisać 100 razy lepiej, ale zadowala mnie w 100%.

    Pozdrawiam
  • REKLAMA
  • #4 7488131
    wladziu22
    Poziom 17  
    jest jedno ale....
    Z góry masz określoną wielkość string-a. No ale faktycznie pamięci o wiele mniej będzie zużytej :)
    Jeszcze jedna uwaga którą napotkałem przy użyciu funkcji itoa(): wewnątrz jest wywołanie funkcji odwracającej string, lecz nie działała i strong byl pisany od tylu :)
  • #5 7488227
    elektronik12z
    Poziom 13  
    Witam

    Mi nie odwraca :) Ciekawe od czego to zależy.

    Funkcja służy mi do wyświetlenia wyników z ADC, mierze napięcie do 5.00V, więc rozmiar 3 i 6 to tak dużo, a funkcja itoa załatwia resztę.

    Pozdrawiam
  • #6 7488297
    wladziu22
    Poziom 17  
    Nie mam pojęcia, nie zaglądałem dokładniej w składnię funkcji odwracającej: strrev() czy jakoś tak. Jedynie na żywca ja sprawdziłem i faktycznie nie odwracała. Ale ciekawostka..w starczej wersji bibliotek było wszystko ok. To tak tylko na marginesie. Nie miałem zbytnio czasu by się to zagłębiać. Wynik miał być tylko poglądowy na szybciocha zrobione.

    Cytat:
    ...więc rozmiar 3 i 6 to tak dużo
    chyba "mało" chciałeś napisać :)
  • #7 7488627
    elektronik12z
    Poziom 13  
    Witam,

    Przecież 3 i 6 to:

    999.999999 jak dla mnie to full wypas :)

    Pozdrawiam
  • #9 7491837
    bartek_duszynski
    Poziom 11  
    wladziu22 sprawdziłem Twój pierwszy sposób ale naprawdę sporo pamięci zabiera. Musze wymyślić coś co zajmuje mniej pamięci bo mi się to nie zmieści na tym kontrolerze :)
  • #10 7492772
    wladziu22
    Poziom 17  
    a nie mówiłem...drugi zajmie jeszcze więcej :) Drugi pomysł używałem na Atmega16, lecz nie wywoływałem fprintf_P(); tylko kilka razy. Jeśli wiesz jak będzie wyglądał wynik tzn ile będzie miał miejsc po przecinku i w sumie ile cyfr to zawsze możesz użyć pomysłu kolegi: elektronik12z
    Możesz też bawić się w dzielenie, mnożenie, odejmowanie i modulo (czyli uzyskać resztę z dzielenia) ale wszytko to przechowywać w zmiennych int. Wówczas również musisz sam dołożyć znak '.' na LCD w odpowiednim miejscu.
    Tak na szybko, np.:
    
    float liczba = 11.22;
    unsigned char przecinek = 0;
    
    if(liczba <= 0.1 && liczba > 0.9)
    {
       przecinek = 1;
    }
    else if(liczba <= 0.01 && liczba > 0.09)
    {
       przecinek = 2;
    }
    itd....
    w podobny sposób sprawdzisz ile jest cyfr przed przecinkiem...
    
    //po tym będziesz wiedział ile razy możesz mnożyć przez 10 Twoja liczbę, by pozbyć się przecinka lecz nie stracić żadnych liczb po przecinku ani nie zyskać dodatkowego zera, czyli:
    
    int mnożnik = przecinek*10;
    liczba = liczba * mnożnik; // tu pozbywamy się przecinka
    int liczba2 = (int)liczba;   // a tu powinniśmy miec już int zamiast float (1122)
    //*
    
    //używając modulo uzyskasz liczbę która była wcześniej po przecinku:
    int temp = liczba2 % mnożnik; // temp = 1122%100=22;
    //a teraz wrócimy do liczby całkowitej:
    liczba2 = (liczba2-temp)/mnożnik; // liczba2 = 1122-22=11
    

    Wyświetlasz liczba2 i temp na LCD wiedząc w którym miejscu masz dodać przecinek '.'
    Oczywiście prościej jest użyć itos() zaraz po '*' (zaznaczyłem gwiazdką w kodzie owe miejsce)
    A jak będziesz miał liczby ujemne to dopiero się zacznie ;-)
    Powodzenia

    P.S. Nie kompilowałem tego kodu wiec bez urazy jak gdzieś się potknąłem.
  • #11 7498279
    elektronik12z
    Poziom 13  
    Witam

    Małe sprostowanie:

    //Konwersja float to str
    void ToString(char *dest, float value)
    {
     //format A.BB   
     long A=0;
     long B=0;
    
     A=value;
     B=value * 100;
     B=B-A*100;
    
     char sA[3];
     char sB[6];
    
     itoa(A,sA,10); 
     itoa(B,sB,10);
    
     
     strcat(dest,sA);
     strcat(dest,".");
    
     //NOWE bo np. przy wartości 4.02 byłoby 4.2!!!!!
     if(B<10)  strcat(dest,"0"); 
     //
    
     strcat(dest,sB);
    
    
    }
    

    Pozdrawiam
  • #12 9745589
    mopeqq
    Poziom 2  
    Odświeżę starego kotleta, bo mam dziwny problem.

    Otóż robię kalkulator na atmedze i wyświetlaczu LCD. Jestem na ukończeniu, ale niestety nie wiedzieć czemu procesor nie oblicza mi poprawnych wartości wyników (korzystam z WinAVR). Przepisałem część programu do Deva i działa:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Oczywiście do wyświetlania wyniku zamiast printfa korzystam z funkcji wyświetlania stringa na lcd. W powyższym listingu floaty są stałe. W moim właściwym programie wyłuskuję je za pomocą funkcji:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    a po wykonaniu działania korzystam ze sprintfa.

    I teraz uwaga :P:

    Jeżeli wpiszę np.: wynik1=wynik1+2.8

    Wynik na wyświetlaczu lcd jest ok.

    Jeżeli wyświetlę sam wynik1 lub wynik2 (bez dodawania) również widzę poprawną wartość argumentów.

    Niestety jeśli wpiszę: wynik1=wynik1+wynik2

    Widzę albo wartość pierwszego argumentu, albo jakieś wartości z kosmosu (liczbowe, nie ma krzaków).

    PS. Póki co nie chcę przesyłać całego listingu, gdyż jestem przeciętnym programistą i kod jest bardzo nieoptymalny, chaotyczny i mało czytelny :D. Mam nadzieję, że ktoś na bazie powyższych wskazówek zlokalizuje błąd.

    Dodatkowo dołączam tabelkę "śmieciawych" wyników. Może ktoś znajdzie w nich jakąś proporcjonalność :P.

    4.75+19.5 = 24.25 (ok)
    19.999+1.5 = 20.499 (powinno być 21.499)
    4+1.5 = 4.5 (powinno być 5.5)

    5*5=0 (powinno być 25)
    2.5*4.5=1.25 (powinno być 11.25)
    40.89*2.1=4.089 (powinno być 85.869)

    Bardzo proszę o pomoc i wskazówki.
  • REKLAMA
  • #14 9746415
    mirekk36
    Poziom 42  
    Ja tak zawsze w głowę zachodzę - jak można się tak męczyć z tymi operacjami na zmiennych float, jak można marnować hektary pamięci flash na te wszystkie funkcje dtostrf, sprintf , włączanie specjalnych opcji w makefile - na takie mega proste działania do których NIE TRZEBA NAWET JEDNEJ ZMIENNEJ TYPU FLOAT UŻYĆ.

    Mam nadzieję, że jak podam ci poniżej prosty przykład to zrozumiesz jak sobie radzić bez float w takich przypadkach. I jak to zrobisz to się zdziwisz jak mało pamięci i jakim niewielkim nakładem pracy można sobie z tym poradzić.

    twój nie działający albo źle działający przykład działania:

    Cytat:
    19.999+1.5 = 20.499 (powinno być 21.499)


    Zrób to sobie tak:

    1. pomnóż powyższe liczby przez tysiąc, czyli:

    uint16_t a = 19999
    uint16_b = 1500

    uint16_c = a+b;

    w wyniku tego dostaniesz w zmiennej 'c' wartość 21499 prawda ?

    no i patrz cały czas nie użyłem ani jednego floata a mam już wynik, który teraz tylko wystarczy zamienić na dwie części: to co przed przecinkiem i to co po przecinku. Skoro mnożyłeś wyżej liczby x1000 to teraz wystarczy podzielić i już masz 21 oraz 499

    wystarczy sobie napisać prostą procedurkę/funkcję - dokonującą tego - która zajmie kilka przysłowiowych bajtów. Czy coś jeszcze więcej trzeba ????

    A dokładność będziesz miał idealną ;)

    Ja tam do ADC czy do pomiarów temperatury nigdy nie korzystam z typów float i takich tam zabaw jakie opisane są na początku tego tematu. Tzn można ale jak się ma procka ze 128kB Flash i dużo wolnej pamięci no i co ważnie - nie jest istotny czas wykonywania tych działań.

    Bo kolejną zaletą tego co ja ci tu opisałem - jest szybkość.
  • #15 9747180
    janbernat
    Poziom 38  
    Mirek- ale w tym wypadku chyba nie o to chodzi.

    mopeqq napisał:
    wynik1=atof(stra);
    wynik2=atof(strb);

    mopeqq napisał:
    wynik1=wynik1+wynik2;
REKLAMA