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

[AVR] obsługa przerwania od licznika 1

mar3kk 01 Lis 2010 22:24 3897 8
REKLAMA
  • #1 8691118
    mar3kk
    Poziom 11  
    Mam następujący kod programu. Moje pytanie brzmi: Dlaczego program nigdy nie wchodzi do przerwania, a przynajmniej tak pokazuje VMLab, w którym to symuluję. Czy trzeba jeszcze coś w jakimś rejestrze ustawić. Program ma wchodzić do przerwania w momencie kiedy licznik zrówna się z OCR1A.

    
    #include<avr/io.h>
    #include<avr/interrupt.h>
    #include<avr/signal.h>
    
    ISR(TIMER1_COMPA_vect) {
     PORTD = 0x00;
    }
    
    int main() {
    
    	DDRD = 0xFF; // caly port D jako wyjscie
    	PORTD = 0xFF; // jedynka na porcie D
    	OCR1A = 0xFF; // zliczaj do 255
    	TCNT1 = 0x00; // zaczynaj liczyc od 0
    	TCCR1A = 0x00; 
    	TCCR1B |= _BV(CS10); // preskaler 1024
    	TCCR1B |= _BV(CS12);
    	TCCR1B |= _BV(WGM12); // tryb ctc
    	TIMSK |= _BV(OCIE1A); // wlaczenie przerwan od output compare
    	
    	sei();
    	
    	return 0;
    	while(1==1){
    	int i=1;
    	}
    }
    


    Pozdrawiam, Marek
  • REKLAMA
  • #2 8691187
    sulfur
    Poziom 24  
       return 0; 
       while(1==1){ 
       int i=1; 
       }


    "return 0;" powoduje zablokowanie przerwań i wejście w nieskończoną pętle. Przenieść za pętle while.
  • REKLAMA
  • #3 8699902
    mar3kk
    Poziom 11  
    Dziekuję, faktycznie tu tkwił błąd. A czy licznik liczy w momencie kiedy jest w przerwaniu czy przerywa swoja prace na czas trwania przerwania?
  • #4 8700307
    tadzik85
    Poziom 38  
    Liczy dalej o ile nie zatrzymujesz go w obsłudze przerwania.
  • REKLAMA
  • #5 8708657
    mar3kk
    Poziom 11  
    Natrafiłem na kolejny problem w mojej walce z licznikami. Mój plan jest taki:
    - licz 18ms
    - uruchom przerwanie od OCR1A
    - zmień stan portu D na wysoki
    - ustaw teraz przerwanie od OCR1B
    - licz 512 razy po 62 takty (tak mi wyszło z obliczeń przy zegarze 16MHz bez preskalera)
    - ustaw PORT D na stan niski kiedy wartość licznika będzie się równała 255+polozenie serwa
    - kiedy licznik doliczy 512 (2ms) rozpocznij cykl od nowa

    Docelowo ma to być sterownik serwomechanizmów.

    Problem jest taki, że 18ms liczy dobrze natomiast te pozostałe 2ms już nie. Trwaja one po kilkanaście sekund albo i więcej.

    
    #include<avr/io.h>
    #include<avr/interrupt.h>
    #include<avr/signal.h>
    
    int licznik,licznik1, servo1, servo2;
    ISR(TIMER1_COMPA_vect) {
    
     PORTD = 0xFF; // wlacz PORT D w stan wysoki
    
     TCCR1B = 0x00;
     TCCR1B |= _BV(WGM12);   // tryb ctc
     TCCR1B |= _BV(CS10);  // wylacz preskaler
     OCR1B = 62; // ustaw wartosc max na 62
     TCNT1 = 0x00; // wyzeruj licznik
     TIMSK = 0x00;
     TIMSK |= _BV(OCIE1B);  // wlacz przerwanie od OCR1B
     licznik = 0;
    
    
    }
    
    ISR(TIMER1_COMPB_vect) {
     if (licznik == 255+servo1) {
     	PORTD = 0x00;
     }
    
     if (licznik == 512) {
       OCR1A = 0x8CA0;
       TCCR1B = 0x00;
     	TCCR1B |= _BV(CS11); // wlacz preskaler przez 8
    	TCCR1B |= _BV(WGM12);   // tryb ctc
    	TIMSK = 0x00;
    	TIMSK |= _BV(OCIE1A);  // wlacz przerwania od OC1A
    	TCNT1 = 0;    // wyzeruj licznik
    
     }
     licznik++;
    }
    
    
    int main() {
    
    	DDRD = 0xFF;
    	PORTD = 0x00;
    	OCR1A = 0x8CA0;
    	
    	TCNT1 = 0x00;
    	TCCR1A = 0x00;
    	TCCR1B = 0x00;
    	TCNT1 = 0;
    	
    	TCCR1B |= _BV(CS11);
    	TCCR1B |= _BV(WGM12);
    	TIMSK |= _BV(OCIE1A);
    	
    	licznik = 0;
    	
    	sei();
    	servo1 = 50; // pozycja serva zakresu 0-255
    	
    	
    	while(1==1){
    	int i=1;
    	}
    	return 0;
    }
    
  • #6 8708952
    mieczotronix
    Poziom 16  
    2 serwa można zrobić całkowicie sprzętowo
    Pod tym linkiem znajdziesz ten kod (komentarze PL moje). To jest inicjalizacja timera:
    
    TCCR1A = _BV(WGM11); /* Fast PWM, ICR1 is top */
        TCCR1B = _BV(WGM13) | _BV(WGM12) /* Fast PWM, ICR1 is top */
            | _BV(CS11) /* div 8 clock prescaler */
            ;
        OCR1A = 3000;  // to jest chyba dla zegara 16 Mhz - to by odpowiadało impulsom 1.5 ms 
        OCR1B = 3000;
        ICR1 = clockCyclesPerMicrosecond()*(20000L/8);  // 20000 uS is a bit fast for the refresh, 20ms, but 
                                                        // it keeps us from overflowing ICR1 at 20MHz clocks
                                                        // That "/8" at the end is the prescaler.
    // zamiast clockCyclesPerMicrosecond() w przypadku 16MHz będzie oczywiście 16, itd...
    #if defined(__AVR_ATmega168__)
        TIMSK1 &=  ~(_BV(OCIE1A) | _BV(OCIE1B) | _BV(TOIE1) );
    #else  // to jest kod z Arduino, chyba jeśli nie ATmega168 to jest to atmega 8 - ale nie jestem pewien - trzeba sobie sprawdzić
        TIMSK &= ~(_BV(TICIE1) | _BV(OCIE1A) | _BV(OCIE1B) | _BV(TOIE1) );
    #endif
    


    potem do OCR1A i OCR1B wpisujesz tylko położenia dwóch serw
    przeliczone z ticków na ms i tyle, kiedy chcesz. Mikrokontroler sam cały czas (w tle) generuje dwa przebiegi.
  • #7 8709254
    mar3kk
    Poziom 11  
    to będzie sterownik 6 serw.
  • REKLAMA
  • #9 8720216
    mar3kk
    Poziom 11  
    Ja wiem jak chcę to zrobić, ale najwyraźniej brakuje mi jakiejś wiedzy z zakresu timerów. Pobawiłem się właśnie tymi przerwaniami i coś jest nie tak. Ustawiając przerwania od OCR1A jest wszystko ok a wystarczy, że zmienię na przerwanie od OCR1B (bit OCIE1B) przy takiej samej wartosci OCR1B jak OCR1A i przy takim samym preskalerze wchodzi do przerwania wielokrotnie rzadziej.

    Edit: Bardzo dziwne rzeczy się dzieją. Muszę mieć ustawione OCR1A i OCR1B na takie same wartości, żeby zadziałało przerwanie od OCR1B. Chyba, że to ten symultor wariuje.
REKLAMA