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

[stm32] operacje dsp. Operacje na ułamkach. Kompilator C.

30 Maj 2011 23:17 3363 16
  • Poziom 19  
    Witam,
    chciałbym zastosować STM32F103RCT6 do sterowania przekształtnikiem napięcia zasilającym silnik indukcyjny. Program chcę pisać w C w Keilu.
    Stąd pewne pytania:
    Ponieważ będę operował na ułamkach chcę wydzielić np 10 lub więcej bitów na część ułamkową. Nie wiem jak w C załatwić kontrolę nad przepełnieniem czy konieczne są wstawki asm (chciałbym korzystać przy mnożeniu i dzieleniu głównie z przesuwania ponieważ jest szybsze niż operacja dzielenia, która może zająć nawet do 12 cykli)? Z tego co przejrzałem w Definitive Guide To The ARM Cortex-M3 STM ma rejestry R0 - R12 (ogólnego użytku), ale nie ma wydzielonego akumulatora.
    Jednostka posiada operację pseudo DSP (pseudo ponieważ wykonywane w kilku cyklach) np. SMLAL, ale czy można jakoś kompilator C zmusić do korzystania z tych rozkazów? Jeśli ktoś z Was wykonywał jakieś operację DSP i podzieliłby się radami będę wdzięczny.
    pozdrawiam
  • Computer Controls
  • Użytkownik usunął konto  
  • Computer Controls
  • Moderator Mikrokontrolery Projektowanie
    W C nie masz żadnej kontroli nad przepełnieniem. Nie wiem czy Keil obsługuje arytmetykę stałopozycyjną, jeśli tak to masz do dyspozycji różne typy i coś dopasujesz do wymaganej ilości bitów po przecinku. Niewykluczone nawet, że kompilator takie operacje optymalizuje stosownie do możliwości procesora. Nawet nie tyle kompilator co jego biblioteka standardowa. W gcc jakbyś chciał spróbować te funkcje mieszczą się w libgcc, jeśli decydujesz się na wstawki to może lepiej jest od razu umieścić je w bibliotece zawierającej podstawowe operacje (libgcc dla gcc, Kail pewnie ma coś podobnego), dzięki czemu w programie odwołujesz się normalnie jak to w C, a kompilator sobie sam wybierze twoją implementację.
  • Poziom 35  
    Keil nie obsługuje natywnie arytmetyki stałoprzecinkowej.
    Cortex-M3 ma instrukcje nasycające rejestr do n-tego bitu, ale żeby z nich korzystać, musisz pisać w asemblerze lub skorzystać z gotowych funkcji CM3_intrinsics.
    Cortex-M3 nie ma instrukcji natywnie pracujące w jakimś stałoprzecinkowym formacie (np. Q15, jak dsPIC) - te operacje musisz robić na piechotę.

    Ja bardzo często korzystam z formatu 24b(int)+8b(fract). Arytmetyka jest bardzo prosta - takie liczby dzielą się, mnożą, dodają, odejmują i modulują bez problemu. Najważniejsze jest tutaj pilnowanie typu i formatu zmiennych, na których przeprowadzasz operacje, np.

    u32 zmiennaStaloprzecinkowa1;
    //Chcemy wpisać liczbę 17.0
    zmiennaStaloprzecinkowa=17; //ŹLE, to zapisuje tylko 17/256 (=0.664)
    zmiennaStałoprzecinkowa=(17<<8); //DOBRZE
    //dodajemy stałą 9
    zmiennaStałoprzecinkowa+=9; //ŹLE
    zmiennaStałoprzecinkowa+=(9<<8); //DOBRZE
    //Odejmujemy 7.5
    zmiennaStaloprzecinkowa-=(7<<8)|0x80; //7bit=1, część ułamkowa=128

    Jak chcesz mnożyć razy sam 'ułamek', musisz dzielić...

    Mam nadzieję, że pokazałem analogię. Powyższe dobrze sprawdza się w różnego rodzaju PIDach czy innych pętlach kontroli...
  • Poziom 19  
    Tak tez robiłem do tej pory, ale teraz chcę odnieść wszystkie wielkości do wielkości rzeczywistych (napięcie, prąd, prędkość silnika). Ponieważ np. stała całkowania będzie wynosić TP = 100e-6s stąd na część ułamkową chcę przeznaczyć 16 b z czego wynika że na część całkowitą również zostaje 16b. Wtedy TP = 100e-6*65536 = 6 , a np 2A = (2<<16) = 131072. Niestety wielkości napięcia będą znacznie większe i będą praktycznie całkowicie zapełniać 32b. Przeglądając instrukcje asm widziałem np rozkazy SMULL lub SMLAL , które moim zdaniem mógłbym wykorzystać np przy całkowaniu. Wyniki tych operacji znajdują się na 2 rejestrach Keil ich chyba nie wykorzystuje stąd też pozostaje wstawka asm pobierająca potrzebne argumenty np. okres całkowania lub od razu KI (ale to przy regulatorze) i zwracająca całkę. Przed zwrotem argumentu trzeba by wykonać kilka działań, ponieważ po mnożeniu należy przesunąć wynik o część ułamkową. Dzięki dwóm rejestrom nie stracił bym rządnych wartości , a kontrolę przepełnienia stosował bym już na jednym rejestrze zwracającym wartość całki. To jeszcze jest wizja ponieważ zabiorę się do pisania za ok 2 tygodnie.
    Trochę martwi mnie, że czas wykonywania powyższych rozkazów trwa od 3 do 7 cykli zegara.

    Przy wszystkich operacjach chcę wykorzystywać w miarę możliwości przesunięcia bitowe ponieważ działają szybciej niż dzielenie.
  • Poziom 35  
    Ten rdzeń ma potok, więc też nie panikuj z tymi cyklami zegara. To naprawdę jest dosyć szybki procek (roznymi rzeczami go katowałem, malloc w przerwaniu tez robil...)
    A w razie czego zawsze możesz podnieść zegar...
  • Poziom 35  
    Oczywiście, że ma. Po to one są.
  • Pomocny post
    Poziom 35  
    Operacje na zmiennych 64bitowych nawet na piechotę nie są koszmarnie wolne na 32bit prockach. Ja bym sie nie przejmowal tymi kilkoma instrukcjami...

    Typ 64bitowej zmiennej to (unsigned) __int64. Long long mozna wpisać, ale zmienna i tak ma 32 bity...
  • Poziom 35  
    No ok
    A teraz zainstaluj Keila i sprawdź. U mnie nie dziala long long (ani unsigned long long), za to działa (un)signed __int64.

    Typ long long przekręca się po 0x7FFFFFFF, ciekawe czemu?...
  • Użytkownik usunął konto  
  • Moderator Mikrokontrolery Projektowanie
    W dodatku te typy intXX_t to przecież tylko aliasy do typów standardowych, najczęściej definiowane tak:
    #define uint64_t unsigned long long
    Więc raczej to nie ma prawa niedziałać. Nie będę się spierał o Keila, bo go nie znam, ale generalnie kompilatory rozpoznają tylko podstawowe typy, a rozszerzenia są definiowane w plikach nagłówkowych, a porem przez preprocesor (w końcu to tylko define) są wstawiane jako typy podstawowe.
  • Poziom 35  
    To ja nie wiem czemu u mnie unsigned long long przekrecil sie z 0xFFFFFFFF na 0...