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.

[AVR][C][Proteus] Generacja przebiegów (DDS) - zmiana f

29 Lip 2009 21:29 2775 6
  • Poziom 20  
    Witam !

    Znalazłem w sieci projekt uniwersalnego generatora przebiegów sinus, trójkąt, piła etc. o nazwie "Mini DDS" Link

    Kod źródłowy do tego projektu napisany jest w asemblerze:

    Code:

    ; main loop
    ;
    ;   r28,r29,r30 is the phase accumulator
    ;     r24,r25,r26 is the adder value determining frequency
    ;
    ;    add value to accumulator
    ;   load byte from current table in ROM
    ;   output byte to port
    ;   repeat
    ;
    LOOP1:
          add      r28,r24         ; 1
          adc      r29,r25         ; 1
          adc      r30,r26         ; 1
          lpm                  ; 3
          out      PORTB,r0      ; 1
          rjmp   LOOP1         ; 2 => 9 cycles


    Pętla główna zajmuje 9 cykli zegara.
    Algorytm jest dość prosty: do akumulatora fazy dodawana jest stała wartość. Gdy ta wartość jest odpowiednia - pobierane są odpowiednie wartości z tablicy przebiegów (sinus, trójkąt, piła) etc.

    Po przeanalizowaniu projektu chciałem wykonać swój (podobny) z tym że napisany w C a nie asemblerze.

    Ogólnie w C cała pętla główna składa się z kilku linijek:

    Code:
    int main(void)
    
    {
       R2RDDR = 0xff;
       uint16_t akumulator=0 ,deltaPhase=1200;   
       
       
       while(1)
       {
             akumulator += deltaPhase    ;            
             R2RPORT = sinewave[akumulator >> 8]   ;                  
       }

          return 0;
    }


    U mnie zajmuje to 11 cykli zegara (wg. AVR Studio).

    W chwili obecnej symulowałem oba projekty w Proteusie.

    I tu zaczyna się sedno sprawy:

    Projekt oryginalny - asemblerowy - generuje zadaną częstotliwość.

    Mój projekt - z uwzględnieniem większej ilości cykli zegara i po przeliczeniach również generuje sygnał - jednak częstotliwość odbiega od tej która powinna być. Jakie są odchyłki ? np. dla zadanego 20kHz dostaję koło 17.5kHz etc.

    Różnica w obu projektach jest w wielkości akumulatora - ja zastosowałem 16 bitowy zamiast 24 bitowego - jednak teoretycznie wpływać powinno to tylko na rozdzielczość ustawiania częstotliwości.

    Zrobiłem również eksperyment - w oryginalnym projekcie dodałem 2 operacje "nop" - tak aby liczba cykli również była 11 - jednak tutaj częstotliwość sygnału generowanego zgadza się z tą obliczoną.

    Nie mam już zbytnio pomysłu dlaczego tak się dzieje. W proteusie liczyłem nawet ile wykonują się instrukcje z dekompilowanego hex'a z mojego projektu - wychodzi 11 - więc wszystko powinno być ok.

    Jeżeli ktoś ma jakieś sugestię będę wdzięczny. W załączniku oba projekty w AVR Studio
    Darmowe szkolenie: Ethernet w przemyśle dziś i jutro. Zarejestruj się za darmo.
  • Poziom 12  
    Witam


    Hmmm jak dla mnie to nie napiszesz dokładnie tego samego w C co w assemblerze chyba ze zrobisz wstawkę assemblerowską, pozatym wyłącz sobie optymalizacje w AVR studio to wykonanie 1 obiegu pętli z 11 cykli wzrośnie Ci do około 29 ;)


    Jak dokładniej chcesz zobaczyć jak kompilator tłumaczy Twój kod na assemblera to kliknij sobie view i dissasembler, włącz symulacje i zobacz jak wygląda Twój prosty program, i skąd się bierze to 11/29 cykli.

    Z tego co mi się wydaje to te różnice też wynikają z ilości cykli na wykonanie 1 jednego powtórzenia pętli oraz zastosowania 16 bitowego akumulatora, ale to już czystka matematyka, wystarczy że weźmiesz kalkulator i policzysz dla jednego i drugiego przypadku, to nie wyjdzie to samo...

    I tak w ogóle to nie polecam symulatorów, czasem to działają jak chcą, sam nie jedną noc spędziłem nad czymś co w symulatorze działało idealnie a po wgraniu na procka wręcz przeciwnie :)


    Pozdrawiam
  • Poziom 20  
    No tak .. ale ja nie miałem zamiaru tłumaczyć jota w jote kodu z asemblera na C - chodzi mi bardziej o idee działania programu :)

    Optymalizację właśnie po to włączyłem żeby zajmowało to najmniej cykli zegara jak się da.

    Odnośnie różnic - jak juz pisałem wcześniej - testowałem oryginalny program na 11 cyklach dodając 2x nop i wszystko było ok (po uwzględnieniu zmian we wzorze na f). Akumulator decyduje o dokładności ustawienia częstotliwości końcowej (chyba że właśnie w tej kwestii jest jakaś różnica której nie widzę).

    Co do symulatora - chwilowo dysponuje tylko nim ... realnie nie mam dostępu nawet do oscyloskopu :/ (dopiero za tydzień będzie taka możliwość)

    Program oryginalny działa - więc nie daje mi żyć, że program realizujący ten sam algorytm w C nie chce ;] ;)

    Czekam na dalsze sugestie :)
  • Poziom 17  
    Po co ci:
    Code:
     >> 8 
    ?
  • Poziom 17  
    No a czy w związku z tym wykorzystujesz te mniej znaczące? i Czy całość nie mogła by od razu na 8bit chodzić? Poza tym wrzuć może kod assemblera po kompilacji z c .
  • Poziom 20  
    Właśnie w tym tkwi cała zasada działania programu :)

    Akumulator jest zwiększany - w zależności od tego jak szybko - wybierane są odpowiednie próbki, oraz decyduje to o częstotliwości generowanego sygnału.

    Po deasemblacji w AVR studio pętla główna wykonuje takie zabiegi:

    Code:
    60:                akumulator += deltaPhase    ;            
    
    +00000033:   5520        SUBI      R18,0x50       Subtract immediate
    +00000034:   4F3B        SBCI      R19,0xFB       Subtract immediate with carry
    61:                R2RPORT = sinewave[akumulator >> 8]   ;                  
    +00000035:   2FE3        MOV       R30,R19        Copy register
    +00000036:   27FF        CLR       R31            Clear Register
    +00000037:   5AE0        SUBI      R30,0xA0       Subtract immediate
    +00000038:   4FFF        SBCI      R31,0xFF       Subtract immediate with carry
    +00000039:   8180        LDD       R24,Z+0        Load indirect with displacement
    +0000003A:   BB82        OUT       0x12,R24       Out to I/O location
    +0000003B:   CFF7        RJMP      PC-0x0008      Relative jump