Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

ATmega8 - zmienne i ich dzielenie

1ceman 02 Jun 2006 01:01 2031 15
  • #1
    1ceman
    Level 12  
    Witam
    Piszę program, w którym zliczam impulsy z Timera0 (ATmega8). Ilość tych impulsów jest następnie dzielona przed określoną liczbę. Jak mozna się domyślić czasem wartość wynikowa będzie ułamkiem czyli pojawi się przecinek.
    Problem w tym, że nie mogę ustawić zmiennej wynikowej jako liczby z przecinkiem.

    Code:
    Dim N As Byte
    
       N = Tcnt0 / 36
       N = N * 60

    W takim wypadku wszystko działa, ale wynik nie ma nic wspólnego z rzeczywistością ponieważ zmienna N jest 'okrojona' do liczby całkowitej.
    Jeśli ustawię N jako Single pojawia się błąd, ponieważ (tak mi się wydaje) w jednym równaniu są zmienne różnych typów.



    Chciałbym żeby po dzieleniu i kolejnych operacjach można było na LCD przedstawić wynik w postaci liczby z przecinkiem. Teoretycznie mogłoby to tak wyglądać (w praktyce oczywiście nie działa):
    Code:
    Dim N As Single
    
       N = Tcnt0 / 36
       N = N * 60
       Cls
       Lcd "V=" ; Fusing(N , "#.##")

    Na pewno są jakieś sposoby na obejście tego. Niestety jestem zbyt początkujący i nigdy nie robiłem tego typu operacji.
    Dzieki za pomoc.

    Dodano po 41 [minuty]:

    Poradziłem sobie z tym, ale mam inne pytanie.
    Jak rozwiązać to żeby w przypadku gdy wynik jest liczbą całkowitą program nie narzucał mu postaci #.##? Inaczej mówiąc - jak sprawdzić czy wynik jest liczbą całkowitą czy jest w postaci liczby ułamkowej?
    Kamery 3D Time of Flight - zastosowania w przemyśle. Darmowe szkolenie 16.12.2021r. g. 10.00 Zarejestruj się
  • Helpful post
    #2
    Andy74
    Level 25  
    Witam.
    Co mi przyszło do głowy:

    Code:
    Dim N As Single , Temp As Single , Temp2 As Byte
    

       Temp2 = Tcnt0
       Temp = Temp2
       N = Temp / 36
       N = N * 60
       Cls
       Lcd "V=" ; Fusing(N , "#.##")


    Nie można przepisać bezpośrednio wartości rejestru Tcnt0 do zmiennej typu Single, ale można do Byte, a tą do Single i wtedy podzielić. Chyba o to Ci chodziło? Ten kod nie wyrzuca błędu przy kompilacji, ale musisz sprawdzić, czy działa prawidłowo. Tyle tylko, że to dwie dodatkowe zmienne i dwa działania...

    Pozdrawiam
    Andy

    edit: Uprzedziłeś mnie... A tak z ciekawości: Jak to rozwiązałeś?
  • #3
    1ceman
    Level 12  
    Rozwiązałem to dokładnie tak jak napisałeś, tyle że z jedną dodatkową zmienną:
    Code:
    Dim X As Byte , N As Single
    
       X = Tcnt0
       N = X / 36
       N = N * 60

    Wiesz może jak rozwiązać problem, który dodałem do pierwszej wiadomości?
    Quote:
    Jak rozwiązać to żeby w przypadku gdy wynik jest liczbą całkowitą program nie narzucał mu postaci #.##? Inaczej mówiąc - jak sprawdzić czy wynik jest liczbą całkowitą czy jest w postaci liczby ułamkowej?
  • Helpful post
    #4
    Andy74
    Level 25  
    No właśnie myślę...
    Fusing wymaga przynajmniej jednego znaku "#" lub "&" po kropce, więc zawsze wyświetli tam przynajmniej jedno zero...
    A może by tak za pomocą STR() zamienić Single na tekst i poeksperymentować z instrukcją Format()?

    Można też inaczej, np:
    Code:
    IF INT(N) = N Then
    
    Liczba jest całkowita i wyświetl bez formatowania
    Else
    Liczba ułamkowa i wyświetl z formatowaniem
    End If


    Pozdrawiam
    Andy

    edit: Sprawdziłem, nie kompiluje się, ale można tak:

    Code:
    Dim N As Single , Temp As Single
    

       Temp = Int(n)

       If Temp = N Then
          'Liczba jest całkowita i wyświetl bez formatowania
       Else
          'Liczba ułamkowa i wyświetl z formatowaniem
       End If
  • Helpful post
    #5
    zumek
    Level 39  
    Jeśli możesz dopuścić błąd przy zaokrągleniu , to skompiluj dla
    const _single=0 , jeśli nie , to const _single=1 :
    Code:

    $regfile = "m8def.dat"
    Const _single = 1

    #if _single
    Dim N As Single , Temp As Single , Temp2 As Word
       Temp2 = Tcnt0 * 10
       N = Temp2 / 6
       Temp = N - Int(n)
       If Temp = 0 Then Temp2 = N
       Cls
       Lcd "V=" ;
       If Temp <> 0 Then Lcd Fusing(n , "#.##") Else Lcd Temp2
    #else
       Dim N As Long , Reszta As Word , Liczba As String * 10
       N = Tcnt0 * 1000
       N = N / 6
       Reszta = N Mod 100
       If Reszta = 0 Then N = N / 100
       Liczba = Str(n)
       If Reszta = 0 Then
          Liczba = Format(liczba , "  ")
       Else
          Liczba = Format(liczba , "  .00")
       End If
       Cls
       Lcd "V=" ; Liczba
    #endif
    End

    Sprawdź wielkość kodu wynikowego , dla obu warunków :D

    Piotrek
  • #6
    1ceman
    Level 12  
    Andy74 podany przez Ciebie sposób niby działa, ale...
    po zamianie zmiennej single na int i przypisaniu jej wartości znowu do zmiennej single wyświetlana wartość będzie w postaci np. 5.0.
    Zeby się pozbyć tego zera po przecinku zmienna do której przypisujemy musi być typu integer, np.
    Code:
    Dim A As Single , B As Integer 
    
    A = 20 / 4
    Lcd "n1= " ; A
    Lowerline
    B = Int(a)
    Lcd "n2= " ; B

    Wtedy wynik będzie w postaci:
    Code:
    n1= 5.0
    
    n2= 5


    Dziękuję za pomoc!
    Pozdrawiam.

    Ps. zumek nie obraź się że nie wykorzystałem Twojego kodu, ale jakiś taki zbyt skomplikowany wygląda do tego zadania ;)

    Przy okazji - gdzie się sprawdza wielkość kodu?
  • Helpful post
    #7
    zumek
    Level 39  
    1ceman wrote:

    ...
    Ps. zumek nie obraź się że nie wykorzystałem Twojego kodu, ale jakiś taki zbyt skomplikowany wygląda do tego zadania ;)

    Co tam jest skomplikowane :?:
    To są 2 oddzielne programy , z których 1-szy działa na Single(między #if ,#else) i 2-gi na Long (między #else,#endif).Każdy z nich wykonuje Twoje założenia i nic poza tym :D
    Różnica pomiędzy nimi , to wielkość kodu wynikowego , gdzie 1-szy "konsumuje" blisko 2700 B , a 2-gi tylko(?) niecałe 1400 B , a wynik ich działania jest identyczny , z wyjątkiem dokładności(brak zaokrągleń w drugim)
    To tyle
    1ceman wrote:

    Przy okazji - gdzie się sprawdza wielkość kodu?

    W raporcie (Ikonka w postaci łapki , trzymającej karteczkę :D )

    Piotrek
    PS
    Pokaż swój :D
  • #8
    1ceman
    Level 12  
    Nie wiem czy mój kod jest prostszy czy nie, ale dla mnie łatwiejszy :)
    Code:
    $regfile = "m8def.dat"
    
    $crystal = 8000000
    Config Lcd = 16 * 2
    Dim A As Single , Temp As Single , B As Integer
    Cls

    A = 20 / 3
    Lcd "n1= " ; A
    Temp = Int(a)
    B = Int(a)
    Lowerline
    If Temp = A Then
       Lcd "Cala: " ; B
    Else
       Lcd "Ulam=" ; Fusing(a , "#.##")
    End If
    End

    Można wkleić do bascoma i sprawdzić. W tej chwili wynik będzie z przecinkiem. Zeby zmienić wystarczy w równaniu A=20/3 podstawić np. 4 zamiast 3.

    Nie wiem dokładnie gdzie jest rozmiar kodu, wkleje raport :D
    Quote:
    Report : 2
    Date : 06-02-2006
    Time : 17:39:22

    Compiler : BASCOM-AVR LIBRARY V 1.11.8.1, DEMO Edition
    Processor : M8
    SRAM : 400 hex
    EEPROM : 200 hex
    ROMSIZE : 2000 hex

    ROMIMAGE : 7E6 hex -> Will fit into ROM
    ROMIMAGE : 2022 dec
    FLASH USED : 24 %
    BAUD : 9600 Baud
    XTAL : 8000000 Hz
    BAUD error : 0.16%

    Stack start : 45F hex
    Stack size : 20 hex
    S-Stacksize : 8 hex
    S-Stackstart : 440 hex
    Framesize : 18 hex
    Framestart : 427 hex
    Space left : 951 dec

    Gdzie to się sprawdza dokładnie i co znaczy hex? :]
  • Helpful post
    #9
    Andy74
    Level 25  
    Witam
    Mój kod nie miał być idealny, chodziło mi tylko o to by Ci pokazać na przykładzie ideę z użyciem Int() :)

    Co do Twojego pytania:

    Quote:
    ROMSIZE : 2000 hex

    Całkowity rozmiar pamięci programu w formacie szesnastkowym (2000 hex = 8192 dec, czyli Twój kontroler ma 8192 bajty na program).

    Quote:
    ROMIMAGE : 7E6 hex -> Will fit into ROM

    Twój program zajmuje 7E6 bajtów pamięci (format szesnastkowy) i mieści się w ROM-ie kontrolera

    Quote:
    ROMIMAGE : 2022 dec

    To samo, tylko dziesiętnie. To jest rozmiar kodu po kompilacji.

    Quote:
    FLASH USED : 24 %

    2022 bajty programu, to jest 24% całej pamięci programu procesora.

    Więc podsumowując "hex" znaczy tyle co hexadecymalnie, czyli szesnastkowo. Przeliczyć na dziesiętny i odwrotnie można łatwo choćby w kalkulatorze windows w widoku "naukowym".

    Pozdrawiam
    Andy
  • #10
    1ceman
    Level 12  
    Dziekuje za dokładne wyjaśnienie :)
    Przy okazji dowiedziałem się do czego są literki A-F w kalkulatorze windowsa :P
  • Helpful post
    #11
    Holy

    Level 14  
    Brrr, Basic. ;) Przerzuć się na C lub asemblera. Nowe AVRStudio można zintegrować z WinGCC i działa to bez zarzutów. A w C deklarujesz sobie od razu, czy masz zmienną całkowitą (char, int), czy zmiennoprzecinkową (float) i o dziwo artytmetyka zmiennoprzecinkowa nie zajmuje ogromnych ilości flasha. :)
  • Helpful post
    #12
    zumek
    Level 39  
    Holy wrote:
    Brrr, Basic. ;) Przerzuć się na C lub asemblera. Nowe AVRStudio można zintegrować z WinGCC i działa to bez zarzutów. A w C deklarujesz sobie od razu, czy masz zmienną całkowitą (char, int), czy zmiennoprzecinkową (float) i o dziwo artytmetyka zmiennoprzecinkowa nie zajmuje ogromnych ilości flasha. :)

    Ciekawie prawisz , tylko wytłumacz mi dlaczego ten prosty test ...
    Code:

    #include <avr/io.h> //ATmega8
    #include <stdlib.h>

    float x;
    char buf[10];

    int main(void)
    {
       x=TCNT0; //lub x=(float)TCNT0;
       x/=36;
       x*=60;
       dtostrf(x,5,2,buf);
       while(1);
    return(0);
    }

    ... zajmuje na "dzieńdobry" 4186B(dla -Os 4070B) i my mamy dopiero string'a , a jeszcze trzeba dołożyć funkcje dla LCD :?:
    Może ja o czymś nie wiem , lub mój WinAvr jest popsuty :?:

    Pozdrawiam
    Piotrek
  • Helpful post
    #13
    Holy

    Level 14  
    W moim AVRStudio zajmuje tyle samo, co jest raczej całkiem normalne. :) Faktycznie biorąc pod uwagę ATMega8 to taki programik zajmuje ponad połowę flasha. Głównym problemem jest to funkcja:
    Code:
    dtostrf(x,5,2,buf); 

    To ona zajmuje większość flasha, bo przy jej okazji pakowana jest cała arytmetyka zmiennoprzecinkowa. Sposobem na to jest operowanie w C na int'ach i zastosowanie ciekawego algorytmu do konwersji na BCD. Algorytm w asemblerze zamieszczam poniżej, można go sobie przerobić na wstawkę do C.

    Code:

    ;****************************************************
    ;      KONWERSJA HEX-->DEC
    ;   R25:R24 - HEX
    ;   R23:R22 - DEC
    ;   R21:R16 - cache
    ;****************************************************
    HEX2DEC:
       ldi   R16, 8      ;czy 8 cykl?
       ldi   R17, 3      ;ile trzeba dodać do tetrady
       ldi   R21, 16      ;licznik cykli
       clr   R22
       clr   R23
       mov   R20, R25
    hex5:   clc
       rol   R20
       rol   R22      ;przesuwamy wynik
       rol   R23
       dec   R21
       breq   hexend
    hex0:   mov   R19, R22   ;sprawdzanie 1 tetrady
       andi   R19, 0x0F
       subi   R19, 5
       brlt   hex1
       add   R22, R17   ;dodaj 0x03
    hex1:   mov   R19, R22   ;sprawdzanie 2 tetrady
       andi   R19, 0xF0
       swap   R19
       subi   R19, 5
       brlt   hex2
       swap   R17
       add   R22, R17   ;dodaj 0x30
       swap   R17      
    hex2:   mov   R19, R23   ;sprawdzanie 3 tetrady
       andi   R19, 0x0F
       subi   R19, 5
       brlt   hex3
       add   R23, R17   ;dodaj 0x03
    hex3:   mov   R19, R23   ;sprawdzanie 4 tetrady
       andi   R19, 0xF0
       swap   R19
       subi   R19, 5
       brlt   hex4
       swap   R17
       add   R23, R17   ;dodaj 0x30
       swap   R17      
    hex4:   cpse   R21, R16   ;czy 8 cykl?
       rjmp   hex5      ;NIE
       mov   R20, R24   ;TAK - zmiana rejestru
       rjmp   hex5
    hexend:   ret


    Sposób reprezentacji wyników jest prosty. Najpierw mnożymy, potem dzielimy. W dodatku mnożnik zwiększamy sztucznie np. 10 razy i wtedy przesuwamy wynik, tak aby w miejscu jedności przy normalnym mnożeniu mieć dziesiąte części. A separator dziesiętny wstawiamy tylko na wyświetlaczu. I w ten sposób jest po problemie. :) Szybko, skutecznie i zajmuje mało kodu.
  • Helpful post
    #14
    zumek
    Level 39  
    Holy wrote:
    W moim AVRStudio zajmuje tyle samo, co jest raczej całkiem normalne. :)
    A w Basicu już nie jest "całkiem normalne" :?:
    Wybacz ten "prztyczek w nos" - za
    Quote:
    Brrr, Basic.

    Ale dziękuję za krótki wykład i przykładzik w asm - na pewno komuś się przyda :D

    Pozdrawiam
    Piotrek
  • Helpful post
    #15
    Holy

    Level 14  
    Po to się tutaj produkujemy, żeby wymieniać ciekawe informacje i spostrzeżenia. :) Poniżej zamieszczam kod tej samej procedury co powyżej, tyle, że napisanej w C. Jak ktoś lubi Basic'a, to może sobie przerobić.
    Procedura przekształca zwykłego int'a w kod BCD, oczywiście zakres liczb jest od 0 do 9999.

    Code:

    unsigned int hex2dec(unsigned int x)
    {
      unsigned char i;
      unsigned int y=0;
      for (i=0;i<15;i++)
          {
          y<<=1;
          y+=(x/0x8000);
          if ((y & 0x000F) >= 0x0005) y+=0x0003;
          if ((y & 0x00F0) >= 0x0050) y+=0x0030;
          if ((y & 0x0F00) >= 0x0500) y+=0x0300;
          if ((y & 0xF000) >= 0x5000) y+=0x3000;
          x<<=1;
          }
          y<<=1;
          y+=(x/0x8000);
    return y;
    }
  • Helpful post
    #16
    zumek
    Level 39  
    Holy wrote:

    ... Jak ktoś lubi Basic'a, to może sobie przerobić.
    Procedura przekształca zwykłego int'a w kod BCD, oczywiście zakres liczb jest od 0 do 9999.

    W Basicu z Bascoma , jest do tego gotowa funkcja x=Makebcd(x) , choć operuje tylko na Byte(unsigned char).
    Piotrek
pcbway logo