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

[atmega32] tryb CTC timera1 i input capture

qczek 09 Wrz 2010 10:31 2814 17
REKLAMA
  • #1 8491465
    qczek
    Poziom 14  
    Witam,
    Czy da się jakoś magicznie zrobić, żeby licznik w trybie CTC nie kasował się przy dojściu do zadanej wartości, tylko liczył dalej sobie...
    Chodzi mi o to, ze zrobiłem na rejestrze przesuwnym taktowanym przez CTC sterowanie 8 serwami modelarskimi, a teraz chce jeszcze zrobić odczyt PPM 8 kanałów odbiornika (po kolejki przez multiplexer 74151) z wykorzystanie input capture. No i w atmedze32 to nie za bardzo wiem jak to zrobić, bo to ten sam licznik....
    Pozdrawiam
    Krzysiek
  • REKLAMA
  • #2 8491502
    szelus
    Poziom 34  
    Tryb CTC właśnie na tym polega, że timer się zeruje. Czyli nie, to niemożliwe...

    Nie możesz użyć innego timera do sterowania rejestrem?

    Ewentualnie, jeżeli używasz stałej i niezbyt malej wartości ograniczenia, to ciągle możesz używać ICP, tylko ze zmniejszoną rozdzielczością.
  • #3 8491513
    qczek
    Poziom 14  
    dzięki za odpowiedz, co do użycia innego timera to nie bardzo, bo chcę mieć regulowane serwo w 1024 krokach, więc na 8 bitowym timerze się nie da....
    z kolei input capture wystarczył by mi 8 bitowy, niemniej tej opcji 8 bitowe timery nie mają ;(
    wiec nie bardzo się da, chyba tylko większą atmege128 w to wsadzić....
    pozdrawiam
    Krzysiek
  • #4 8491720
    gaskoin
    Poziom 38  
    A nie możesz puścić licznika w normalnym trybie? wtedy mógłbyś wykorzystać i przerwanie od porównania i od przepełnienia
  • #5 8491734
    szelus
    Poziom 34  
    Nie wiem jak masz to zrobine, ale musisz zmieniać wartość ograniczenia?
    Użyj jednego rejestru OCR do ustalenia stałego ograniczenia np. na 1024 (tryb CTC) a drugiego do generacji szerokości impulsu dla serw (sprzętowo lub programowo). Wtedy spokojnie możesz korzystać z ICP równolegle z tym wszystkim.
  • REKLAMA
  • #6 8492005
    qczek
    Poziom 14  
    Hej,
    mam tak zrobione, że CTC dokładnie mi wyznacza jak długo jest 1 na kolejnym wyjściu rejestru przesuwającego (od 1-2ms). Programowo tylko sobie ustawiam wejście rejestru raz na 20ms na 1 (cała ramka sterowania) tak żeby znowu zaczęła się 1 przesuwać w rejestrze. Tak więc jest to bardzo dokładne. Na jeden okres (przerzutnik przerzucany jest zboczem narastającym) do OCR1A wpisuje po prostu długość jedynki dla danego serwa - 500 a potem drugi raz do OCR1A wpisuje 500. Mam wtedy dokładnie sterowane 8 wyjść niezależnie od przerwań itp itd....

    Generalnie z puszczenie zegara żeby sobie sam liczył jest taki problem, ze ja ten zegar muszę sam zmieniać, żeby zmienić okres taktowania rejestru przesuwnego, więc potem nijak nie dojdę w obsłudze input capture ile było tiknieć....

    Może ja czegoś dokładnie nie rozumiem....
    Pozdro
    Krzysiek
  • #7 8492048
    szelus
    Poziom 34  
    No tak. Jakbyś zrobił sterowanie na demultiplekserze, to by się dało użyć PWM. Tak niestety nie.
    --------
    Poprawka. Jaką masz prędkość taktowania timera 1? Bo myślę tak - mógłbyś, zamiast wpisywać stałą wartość do OCR, wpisywać nową wartość wyliczoną z bieżącej (bieżąca + delta modulo coś tam), a długość liczenia zostawić stałą (tryb fast-PWM lub ustaloną z drugiego OCR w trybie CTC).
  • #8 8492058
    qczek
    Poziom 14  
    Hej
    A co by to zmieniało?
    Wtedy miał bym na 3 pinach wybór serwa, a wyjście CTC dawało by mi od 1m-2ms jedynkę.... i ?
    Pozdrawiam
    Krzysiek
  • #9 8492086
    szelus
    Poziom 34  
    Z demultiplekserem używałbyś prosto trybu fast-PWM do określenia długości impulsu (wyjście OCR podłączone do wejścia multipleksera), ze stałą długością timera (2ms?), przełączając wyjście na przerwanie compare match. Przez pewien czas na wszystkich wyjściach byłoby zero.
  • REKLAMA
  • #10 8492219
    qczek
    Poziom 14  
    Hej,
    Ale jak konkretnie z tym fast PWM, bo z tego co widzę w notce to nawet jak wybiorę 10bitowy to mi licznik zlicza do max 3FF, wiec za mało
    ja potrzebuje tak naprawdę jeśli przechodzimy na PWB to mieć
    a) stały okres równy 2ms
    b) wypełnienie od 50-100%
    c) przerwanie wywoływane na końcu każdego okresu, lub przy zboczu opadającym...

    pozdrawiam
    Krzysiek
  • #11 8492582
    szelus
    Poziom 34  
    Faktycznie, tryby fast-PWM mają swoje ograniczenia, już nie pamiętałem dokładnie.
    Robisz więc tak:
    Zegar rejestru podłączasz do OC1B. Ustawiasz tryb CTC, toggle OC1B on compare match. Do OCR1A wpisujesz 2047, lub 4095 - itp. wartość, aby uzyskać okres >= 2ms z wymagana rozdzielczością. Z tym, że najlepiej dobrać tak preskaler, aby częstotliwość taktowania była c.a. 1000(1024)*1kHz.
    W przerwaniu od OC1B robisz prawie tak jak, dotychczas, tylko do OCR1B wpisujesz wyliczoną długość impulsu powiększoną o aktualną wartość rejestru licznika modulo zawartość OCR1A+1.

    Z ICP korzystasz normalnie - modulo zawartość OCR1A+1.

    P.S. Oczywiście w obsłudze przerwania odczyt timera, dodawanie i zapis do OCR1B muszą być bezpośrednio po sobie.
  • #12 8498717
    qczek
    Poziom 14  
    Hej,
    czy to twoje rozwiązanie nie spowoduje tego, ze tak naprawdę pomiędzy impulsami na kolejnych wyjściach rejestru będę miał przerwy wynikające z wpisów w OC1A (zegar będzie przecież liczył mnie tylko do 2ms, ale do 4ms.....
    pozdrawiam
    krzysiek

    Proszę poprawić błędy w pisowni w całym temacie - regulamin p.15
    [zumek]
  • #13 8505615
    szelus
    Poziom 34  
    Oczywiście, że tak nie mogłoby działać. Fakt, że dobrze myślałem, ale za szybko pisałem i mogłem wprowadzić Cię w błąd, przepraszam.
    Miałem na myśli coś takiego (bardziej pseudokod):
    
    #define TIMER_LEN	2048 //albo 4096
    #define SHIFT_REG_BIT	0
    #define TIMER_PRESCALER  ...
    
    uint8_t servo_id;
    uint8_t pulse_low;
    
    uint16_t next_pulse;
    
    uin16_t servo[8]; // tablica bieżących nastawień serw
    
    
    static inline void set_timer( uint16t delay)
    {
        next_pulse =  (next_pulse + delay) & (TIMER_LEN -1);
        OCR1B = next_pulse;
    }
    
    
    SIGNAL_OC1B()
    {
        if (pulse_low)
        {
            set_timer( OFF_PULSE_LEN);
            pulse_low = 0;
    
            if (servo_id == 0)
            {
                PORTX |= 0x01; //ustaw jedynkę na wejściu rejestru przes.
            }
            else
            {
                PORTX &= 0xFE; // ustaw zero na wejściu rejestru przes.
            }
        }
        else
        {
            set_timer( servo[servo_id] - OFF_PULSE_LEN);
    
            servo_id++;
    
            if (servo_id == 8)
            {
                servo_id = 0;
            }
    
            pulse_low = 1;
        }
    }
    
    
    main()
    {
    
        /* ... */
    
        servo_id = 0;
        pulse_low = 1;
        next_pulse = 0;
    
        PORTX &= 0xFE; // ustaw zero na wejściu rejestru przes.
        OCR1A = TIMER_LEN - 1;
        set_timer( OFF_PULSE_LEN );
        TCCR1A = _BV(COM1B0); //toggle on compare B
        TCCR1B = _BV(WGM12);  //CTC
        TIMSK = _BV(OCIE1B);
        sei();
        TCNT1 = 0;
        TCCR1B |= TIMER_PRESCALER; 	//włącz timer
    
        /* ... */
    }
    


    Pierwszy (krótki) impuls będzie narastający i wpisze jedynkę do rejestru przesuwającego. Przerwanie od niego skasuje wejście i ustawi następny impuls (opadający) tak jak u Ciebie.
    Czytanie czasu (względnego) nie bezpośrednio z timera, a ze zmiennej next_pulse załatwia problem ewentualnego opóźnienia obsługi przerwania.

    ---
    W sumie, kod by się jeszcze uprościł, gdyby po prostu użyć trybu normalnego (0). Nie trzeba by ustawiać OCR1A i liczyć modulo. ICP też byłoby prostsze, bo bez modulo.
  • REKLAMA
  • #14 8505953
    qczek
    Poziom 14  
    Hej,
    Dziękuje za odpowiedz, czyli generalnie to wygląda tak, że timerek 1 zlicza od 0 do wartosci OCR1A potem zmienia pin OCR1A potem wywołuje przerwanie od OCR1A, potem zlicza do wartości OCR1B, zmienia pina OCR1B i wywołuje przerwanie OCR1B a potem znowu od 0... tak to wygląda?

    Jeśli tak, to w zasadzie wystarczy do mojego kodu wstawić tylko prawidłowe wstawianie do OCR1B i będzie ok...
    Pozdrawiam
    Krzysiek
  • #15 8506166
    szelus
    Poziom 34  
    No nie. Timer zlicza sobie od zero do OCR1A (lub nawet do 0xFFFF) w kółko. To stanowi oś czasu. Zmiana stanu na OC1A nie jest Ci potrzebna.
    Na tej osi generujemy zmiany na OC1B w odpowiednich odstępach, dodając (kolejno) do OCR1B wartości odpowiadające długości odstępów pomiędzy impulsami.
    Tzn. to, czy timer "przejdzie przez zero" pomiędzy jedną zmianą na OC1B a następną nie ma żadnego znaczenia.
    ---
    Jeżeli użyjesz trybu normalnego timera, to możesz zamiast OC1B/OCR1B używać tak samo OC1A/OCR1A i nie musisz nic modyfikować w układzie.
  • #16 8507908
    qczek
    Poziom 14  
    czyli jest tak, że dojście licznika do wartości zapisanej w OCR1A powoduje jego wystartowanie od 0, natomiast dojście licznika do wartość z OCR1B powoduje zliczanie dalej?
    Pozdrawiam
    Krzysiek
  • #17 8508514
    szelus
    Poziom 34  
    qczek napisał:
    czyli jest tak, że dojście licznika do wartości zapisanej w OCR1A powoduje jego wystartowanie od 0,

    Tylko w trybie CTC(4).
    Cytat:

    natomiast dojście licznika do wartość z OCR1B powoduje zliczanie dalej?

    Tak. OCR1B nigdy nie stanowi ograniczenia licznika. Masz to dokładnie opisane w tabelce 47 - "Waveform Generation Mode Bit Description".
  • #18 8509723
    qczek
    Poziom 14  
    Hej,
    Przerobiłem programik na OCR1B i działa wyśmielicie, do OCR1A mam wpisane FFFF i jest dobrze.
    Pozdrawiam i dziękuje
    Krzysiek
REKLAMA