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

Operacje na 32biotwych wartościach w WinAVR.

06 Sie 2006 12:51 1033 4
  • Poziom 29  
    Probuję oprogramować kość DDS (AD9835); częstość generowanego sygnału programuje się wysyłając cztery razy 16bitowe słowa, w każdym słowie jest 8 bitów tworzących 32 bitowe słowo określające bezpośrednio częstość. W układzie jest już ATtiny2313, który ma tylko 2 kB na kod programu, więc operacja w WinAVR na 32bitowych intach odpada :( Czy macie pomysł jak oprogramować obsługę tego 32bitowego słowa, tj. wyświetlić na LCD, zmienić o +/- 10? Mam już kod rozbijający 32bitowe słowo na 4x8bitów i wysyłające do DDS. A może trzymać wewnętrznie w 4 bajtach?
  • Specjalista techniki cyfrowej
    me_super napisał:
    Probuję oprogramować kość DDS (AD9835); częstość generowanego sygnału programuje się wysyłając cztery razy 16bitowe słowa, w każdym słowie jest 8 bitów tworzących 32 bitowe słowo określające bezpośrednio częstość.

    Tego za bardzo nie rozumiem. 16-bitowe słowo, które ma 8 bitów? Czy chodzi o to, że poza danymi o częstotliwości wysyłane jest coś jeszcze, a wszystko to pakowane w 16-bitowe "ramki"?

    me_super napisał:
    W układzie jest już ATtiny2313, który ma tylko 2 kB na kod programu, więc operacja w WinAVR na 32bitowych intach odpada

    A czemu? kod operujący na 32 bitach wcale nie jest taki duży, gorzej gdyby to były liczby zmiennoprzecinkowe.
    na przykład coś takiego:
    Code:
    unsigned long int jakas_zmienna_32_bitowa;
    
    /* wyslanie po kolei calej zmiennej przez RS-232 (mocno uproszczone, działać nie będzie, chodzi o samą ideę)
    Zmienna wysyłana jest począwszy od najbardziej znaczącego bajtu*/
    UDR = (unsigned char) jakas_zmienna_32_bitowa >> 24;
    UDR = (unsigned char) jakas_zmienna_32_bitowa >> 16;
    UDR = (unsigned char) jakas_zmienna_32_bitowa >> 8;
    UDR = (unsigned char) jakas_zmienna_32_bitowa;

    Po włączeniu optymalizacji w takim kodzie nie zostanie wykonana żadna operacja 32-bitowa.
    Kompilator rozpoznaje takie konstrukcje i zamiast wykonywać operacje przesunięcia logicznego o wielokrotność ośmiu, będzie po prostu pobierał kolejne bajty z obszaru w którym zapisana jest jakas_zmienna_32_bitowa

    me_super napisał:
    A może trzymać wewnętrznie w 4 bajtach?

    Przecież właśnie tak przechowywane są wartości 32-bitowe...
  • Poziom 29  
    shg napisał:
    Tego za bardzo nie rozumiem. 16-bitowe słowo, które ma 8 bitów? Czy chodzi o to, że poza danymi o częstotliwości wysyłane jest coś jeszcze, a wszystko to pakowane w 16-bitowe "ramki"?


    Dokładnie tak jest. Aby zaprogramować jedną częstość należy wysłać
    cztery 16bitowe ramki. Jedna ramka zawiera 8 bitów danych + adresy itp.

    shg napisał:

    A czemu? kod operujący na 32 bitach wcale nie jest taki duży, gorzej gdyby to były liczby zmiennoprzecinkowe.


    Chciałbym wyświetlić 32bitową wartość na LCD, i zmieniać ją poprzez klawiaturę. Mając uint32_t wiem jak zmienić wartość o np. 10. Jeśli mam 4x uint8_t to musiałbym samemu realizować dodawanie itp. Do tego, na wyświetlaczu chciałbym mieć w Hz wartość 32bitową a co innego wysyła się do kości DDS; z grubsza rejestr wysyłanego do DDS ma wartość częstość*107.3741824, gdzie częstość jest w Hz. Jeśli mam rejestr 32bitowy a chcę poznać częstość to muszę pomnożyć 32bitową liczbę.

    Na razie w uint16_t trzymam częstość w kHz a do DDS wysyłam częstość*107374, ale tam mam rozdzielczość 1 kHz.
  • Poziom 39  
    Ja zaproponuję unię(nie Europejską) :D
    Code:

    #include <avr/io.h>
    enum {ONE,TWO,THREE,FOUR};
    typedef union
    {
    unsigned int _int;
    unsigned char _ibyte[2];
    }SixTeen;

    typedef union
    {
    unsigned long _long;
    unsigned char _lbyte[4];
    }ThirtyTwo;

    ThirtyTwo tt;
    SixTeen st[4];

    void Transmit(int x)
    {
    //wysłanie 16-bajtów gdzieśtam ;)
    }
    void readtt(void)
    {
    char i;
    for(i=0;i<4;i++) st[(int)i]._ibyte[1]=tt._lbyte[(int)i];
    }
    int main(void)
    {
    char i;
    tt._long=0x11223344;
    while (tt._long!=0)
       {
       tt._long+=10;
       readtt();
       for(i=0;i<4;i++) Transmit(st[(int)i]._int);
       }
    return 0;
    }

    Dopasuj tylko kolejność i pozycję poszczególnych bajtów z long , w int.

    Piotrek

    PS
    Jeśli coś nie jasne - pytać :D
  • Specjalista techniki cyfrowej
    Właśnie rozwiązałem u siebie jeden z Twoich problemów (tak myślę), więc się podziele.
    Potrzebowałem obliczyć wartość, którą muszę przesłać do DDSa mając daną częstotliwość w Hz. Wzorek taki:
    fw = czestotliwosc * 2^24 / 50000000
    akumulator fazy ma rozmiar 24 bity, zegar DDSa to 50MHz.

    Moje pierwsze podejście wyglądało tak, że wykonałem tą operację dokładnie jak powyżej. Wymusiło to operacje na 64bitach, a to z kolei oznaczało duuuuuże zapotrzebowanie na pamięć programu.

    Ale wpadłem na podły pomysł, to co powyżej inaczej można zapisać tak:
    fw = czestotliwosc * 2^24 * 2 / 100000000
    czyli:
    fw = czestotliwosc * 2^25 / 100000000
    no i co my tu mamy?
    25 razy przesunąć bity w lewo i 8 razy podzielić przez 10.

    Trick polega na tym, żeby operacje te wykonywać na przemian, tak że zawsze wynik będzie mieścił się w 32 bitach.

    No i wyszło z tego coś takiego:
    Code:
    uint32_t freq;
    
    uint8_t shiftcnt, divcnt;

    /* w zmiennej freq mamy częstotliwość w Hz */
    /* temu panu już podziękujemy : */
    /*         freq = (uint32_t) (((uint64_t) freq << 24) / 50000000ULL);*/
             shiftcnt = 25;
             divcnt = 8;
             while(divcnt != 0) {
                while((shiftcnt != 0) && ((freq & 0x80000000) == 0)) {
                   freq <<= 1;
                   shiftcnt--;
                }
                freq /= 10;
                divcnt--;
             }
    /* w zmiennej freq znajduje się wartość gotowa do wysłania do DDSa */


    U Ciebie będzie to wyglądało podobnie, z tym że długość akumulatora fazy wynosi 32 bity, a częstotliwość zegara 40MHz (tak przynajmniej obliczyłem/zgadłem z podanych przez Ciebie informacji)

    więc wartość którą należy wpisać do DDSa wyglada tak:
    fw = czestotliwosc * 2^32 / 40000000
    co po przeksztalceniu daje:
    fw = czestotliwosc * 2^30 / 10000000

    A cały kod będzie wyglądał tak:

    Code:
    uint32_t freq;
    
    uint8_t shiftcnt, divcnt;

    /* w zmiennej freq mamy częstotliwość w Hz */
             shiftcnt = 30;
             divcnt = 7;
             while(divcnt != 0) {
                while((shiftcnt != 0) && ((freq & 0x80000000) == 0)) {
                   freq <<= 1;
                   shiftcnt--;
                }
                freq /= 10;
                divcnt--;
             }
    /* w zmiennej freq znajduje się wartość gotowa do wysłania do DDSa */


    U mnie taka operacja pozwoliła na zmniejszenie rozmiaru programu do 2600 bajtow, poza tym mam tam jeszcze obsługę UARTa, a w zasadzie kompletny, choć mocno uproszczony, terminal, konwersje long na string i odwrotnie, no i oczywiście składanie ramek dla DDSa i całą transmisję. W zasadzie najwięcej miejsca zajmuje właśnie obsługa terminala i konwersja z ASCII na long int

    Ponieważ wykorzystane jest tutaj 32 bitowe dzielenie automatycznie staje się dostępna funkcja 32 bitowego mnożenia, którą możesz wykorzystać bez powiększania rozmiaru kodu. Procedury mnożenia i dzielenia 32 bitowego zajmują razem poniżej 200 bajtów.
    Dodawaniem i odejmowaniem nie masz co się martwić, bo te operacje zajmują tyle co nic.

    Czyli podsumowując.
    Trzymasz wartość częstotliwości w hercach w zmiennej 32 bitowej, po naciśnięciu przycisku dodajesz/odejmujesz sobie do/od niej ile tam chcesz.
    Do wyświetlania używasz funkcji ltoa(), z printf() nie masz nawet co się bawić, za duża jest.
    Przed wysłaniem do DDSa przeliczasz powyższym algorytmem.