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

[Rozwiązano] [ATMega8] [ATMega8][avr-gcc] - Optymalizacja konwersji uint64 na tablicę dziesiętną

saper_2 28 Lip 2013 01:58 3627 17
  • #1 12568473
    saper_2
    Poziom 18  
    Witam,

    Mam 5 bajtową tablicę i muszę ją zamienić na wartość dziesiętną w formie tablicy gdzie każdy element to jedna liczba.
    I tu się zaczynają schody, przerobienie z uint8_t arr[5] na uint64 jest proste i bezbolesne. Ale zamiana uint64 na dziesiętną wartość w tablicy z pojedynczymi liczbami zajmuje ponad 7kb flash'a.... Docelowo chcę użyć m8 i już mam zajęte 4,6k więc...

    Taki mam kod: (to, że liczby lądują w odwrotnej kolejności to nawet nie jest problem...)
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Samo modulo i dzielenie uint64 kosztuje mnie 7k pamięci flash (czyli w moim przypadku program zajmie 11,8k :( )...
    Trochę już szukałem i nie znalazłem żadnego rozwiązania, ażeby to tyle nie zajmowało miejsca (i chodziło znośnie szybko).

    Wiem, że unsigned long long na avr to jak postawienie atmegi na strzelnicy ;), ale nie mam innego pomysłu jak zamienić tablicę arr na tablicę liczb decymalnych...
  • #3 12568507
    saper_2
    Poziom 18  
    Witam,

    Pytanie retoryczne ? :) Oczywiście, że mam.
    Poziom S (jakby go nie zwać).
  • #5 12569233
    BlueDraco
    Specjalista - Mikrokontrolery
    Kłania się tzw. "arytmetyka resztowa". Zwróć uwagę, że zawsze dzielisz przez 10. Spróbuj to rozpisać na bajty albo np. słowa 16-bitowe, to może coś zauważysz. Dla liczby 16-bitowej i dwóch bajtów wygląda to tak (x0 i x1 to dwa bajty liczby):
    x = 256 * x1 + x0
    x / 10 = 25 * x1 + (6 * x1 + x0) / 10

    W przykładzie 16-bitowym istotne jest to, że redukujemy dzielenie 16-bitowe do dzielenia 11-bitowego.

    W podobny sposób da się zapewne zredukować dzielenie 40-bitowe do dzielenia nie dłuższego niż 32-bitowe, a wtedy możesz zapomnieć o uint64_t.

    Na pierwszy rzut oka spróbowałbym podzielić tę liczbę na trzy kawałki po 16 bitów.
  • Pomocny post
    #6 12570865
    PDT
    Poziom 24  
    Witam,

    Na wejściu mamy 40-bitową liczbę binarną, potrzebujemy ją skonwertować na zapis przy podstawie 10. Chcemy tego dokonać bez angażowania 'dużej biblioteki'. Najlepiej jest tego dokonać metodą kompensacyjną.



    Kod: text
    Zaloguj się, aby zobaczyć kod


    Przykładowa funkcja 'odejmowania warunkowego' dla 51 i 4-bajtów:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    Na pewno zajmie to niewiele pamięci, czas wykonania też nie jest zły (zwłaszcza w porównaniu z programowym dzieleniem).

    Pzdr
  • #7 12574355
    saper_2
    Poziom 18  
    BlueDraco - dzięki ale zagiąłeś mnie tym :/

    PDT, wielkie dzięki! Działa z moimi modyfikacjami :) i w samym C - czas nie jest dla mnie krytycznym czynnikiem, a aż takie oszczędzanie ażeby sięgać do ASM też mi nie potrzebne. Obsługa wedle twojego pomysłu zajmuje ok ~700bajtów więc nie mam problemu. Wklejam kod z DevC++ gdzie testowałem wraz z wersjami dla AVRa co używam. I dołączam program testowy wraz z kodem źródłowym (napisany pod DevC++ MinGW GCC 4.7.1 GNU-C99) :)

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


    [i]Temat zablokuję za parę dni :), może ktoś coś chciałby dodać od siebie :)
  • #9 12574587
    tmf
    VIP Zasłużony dla elektroda
    U mnie skompilowanie kodu z postu #1 wydłuża program o jakieś 480 bajtów, z tego ok. 200 jest związanych z 64-bitowym dzieleniem i modulo. Także coś musi być z ustawieniami kompilatora. Warto zajrzeć do pliku lss, operacje modulo i dzielenia są realizowane przez INSNS o nazwie __umoddi3 i __udivdi3. Obie z wywołaniami funkcji pomocniczych zajmują ok. 180 bajtów. Warto sprawdzić czy gcc czasem zamiast korzystać z gotowych funkcji, nie kompiluje sobie tego do własnego radosnego kodu. BTW, jaka wersja kompilatora i skąd wzięta?
  • #10 12574735
    saper_2
    Poziom 18  
    avr-gcc 4.3.3 , WinAVR 2010-01-10, avr-libc 1.6.7cvs , procesor to ATMega8.
    O właśnie jak piszesz o kompilatorze, zapomniałem napisać, używam WinAVR 2010 (wiem stary) ale ostatni jaki jest, a avrstudio mi kompletnie sie nie podoba ze względu na engine VS MSa. A poza tym bardzo polubiłem Programmers Notepad, nieskomplikowany, nawet prosty, ma kolorowanie i drzewo plików projektu, łatwo dodać własne narzędzia z dużą ilością opcji, no i jest lekki ,a nie taki gniot jak VS...

    Wiem :), nie mam bardzo pozytywnej opinii o VS. Chociaż odkąd mam nowy komputer (4x2,3GHz/8Gb/hd7670) to opinia trochę o VS poszła u mnie w górę ;) - piszę w VSC# . Poprzednio miałem c2d t7600 2x2,2GHz/4GB/GF9600M ale VS chodził na tym trochę ślamazarnie czasami...

    Czego używasz tmf?
    Miałbym do porównania (kompilator/avrlibc/mcu)?

    dondu, masz rację, poczekam, aż elka sama się przypomni ;)
  • #11 12574817
    BlueDraco
    Specjalista - Mikrokontrolery
    No to może tak:

    krok pierwszy - dzielimy liczbę 40-bitową przez 10000, używając wyłącznie dzielenia 32-bitowego:

    uint32_t x43 = ((uint32_t)b[4] << 8) + b[3]; // 16 bardziej znaczących bitów
    uint32_t x210 = ((uint32_t)b[2] << 16) + (b[1] << 8) + b[0]; // 24 mniej znaczące bity
    uint32_t xh = x43 * 1677 + (x43 * 7216 + x210) / 10000;
    uint16_t xl = (x43 * 7216 + x210) % 10000;
    - uzyskujemy iloraz i resztę z dzielenia - iloraz nie przekracza 27 bitów, reszta - 14 bitów.

    Następnie wypisujemy obie liczby obok siebie np. tak:
    printf("%lu%05u\n", hh, xl);
    - pierwsza bez zer wiodących, druga z zerami wiodącymi. Trochę lepiej byłoby sprawdzić, czy pierwsza jest zerem i w takim przypadku wypisać tylko drugą, bez zer wiodących.
  • #12 12574820
    tmf
    VIP Zasłużony dla elektroda
    No to używasz zabytkowego kompilatora, który akurat jeśli chodzi o operacje 64 bitowe nie jest specjalnie zoptymalizowany. Nowsze mają oddzielne biblioteki pod AVR i stąd różnica, jak widzisz spora. Ja używam 4.7.2/4.8.1. 4.7.2 możesz pobrać ze strony Atmela - wystarczy pobrać sam toolchain, nie trzeba pobierać IDE. Wtedy masz z grubsza to samo co w WinAVR.
  • #13 12574842
    saper_2
    Poziom 18  
    OOOO nawet nie wiedziałem, że jest sam toolchain. Wiedziałem tylko o całym środowisku. Zaraz będę kombinował ;)

    Ale się przynajmniej czegoś ciekawego nauczyłem :)

    4.8.1 ? Sam kompilujesz? Czy jest dostępny do pobrania pod win avr-gcc w całkiem nowej wersji?
  • #14 12580345
    Milek79
    Poziom 15  
    4.8.1 trzeba chyba pobrać osobno. Ale nie ma potrzeby tak nowego, ten w pełni wystarczy: http://www.atmel.com/tools/ATMELAVRTOOLCHAINFORWINDOWS.aspx Zawiera w sobie avr-gcc 4.7.2
    A co do środowiska to według mnie VS2010 to jakiś potworek przy którym programiści się chyba specjalnie natrudzili żeby był tak ociężały :P Ja używam trochę już starego ale lekkiego AVR Studio 4.19.
  • #15 12580830
    saper_2
    Poziom 18  
    Dzięki, znalazłem już i przetestowałem, choć trochę napsuł mi ten nowy atmelowski avrgcc nerwów głównie przez to, że makefile mi nie działał co używałem z winavr :/ (brakowało sh.exe (który przekopiowałem z winavr) i avr-size który nie działa wywoływany z makefile - a ręcznie tak :/ ).

    Co do optymalizacji kodu, po tym jak wyciąłem niepotrzebne funkcje z obsługi usarta, uzyskałem na winavr2010 5,036k, na nowym toolchain'nie (gcc4.7.2/avrlibc1.8.0) 4,26k!!!
  • #16 18722488
    kamil12239
    Poziom 17  
    Szanowni Panowie,
    Odkopałem temat i mam problem z dzieleniem int16_t . Prosta arytmetyka zmienna dzielę przez dwa.
    W programie mam:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Jeśli dając zmienną:
    czas_stan.EEsek = 15 ;
    czas_stan.EEmin = 17;

    no to wtedy sec = 55020;
    natomiast srodek to powinno być 27510‬;
    Wynik jest natomiast zadziwiający i wynosi 60278;
    Dlaczego tak się dzieje?

    Miałem już na int32_t ale to samo.
    Procesor attiny1614.
REKLAMA