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

obliczanie timerów - teoria a praktyka

bisz 13 Lip 2010 23:16 2049 6
REKLAMA
  • #1 8289490
    bisz
    Poziom 18  
    Witam, tworzę pewien projekt i spotykam się z tak zwaną rozbieżnością między teorią a praktyką.

    Używam 2 timerów, z których jeden generuje sekundę a drugi chcę aby generował coś najbliższe 24kHz

    Timer generujący sekunde to 16bitowy timer1 z preskalerem 1024, kwarc 16MHz, z wartością początkową wyliczoną przeze mnie 0xc2f6, którego sprawdzałem z zegarkiem i po 5 minutach sekundy sie nie rozjeżdżaja wiec mniemam, że jest możliwie najbliższy sekundzie jak tylko sie da to osiągnąć z takim kwarcem.


    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/signal.h>
    #include "lcd.c"
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #define cbi(port, bit) (port) &= ~(1 << (bit))
    #define TCN_T0 0xb5
    
    unsigned long T=0;
    SIGNAL(SIG_OVERFLOW0)  // przepelnienie timera0
    	{
    	TCNT0 = TCN_T0; //ustawienie wartosci poczatkowej
       T++;
    
    jakieś tam kilka poleceń
    	}
    
    
    int main( void )
    {
    DDRC=255;
    
    lcd_init(LCD_DISP_ON); //inicjacja wyswietlacza
    
    sei();
    
    
    //timer1 do zliczania km/h 16 bitowy :
    TCNT1 = 0xc2f6; // wartosc poczatkowa, T=1s c2f6
    TCCR1A = 0x00; // T/C1 w trybie czasomierza
    TCCR1B = _BV(CS10)|_BV(CS12); // preskaler ck/1024
    
    
    //timer0 do zliczania obrotow silnika i synchronizacji z kolem zebatym 8 bitowy
    
    TCNT0 = TCN_T0; 
    TCCR0 = _BV(CS01) ; // preskaler ck/8
    TIMSK = _BV(TOIE0); //wlacza obsluge przerwan T/C0
    
    
    while(1) //petla glowna
    {
    if ((_SFR_BYTE(TIFR) & _BV(TOV1)) == _BV(TOV1)) {//co 1 s
    TCNT1 = 0xc2f6; 
    TIFR = _BV(TOV1); 
    lcd_puts(ltoa(T,buffer,10));
    
    T=0;
    }}
    


    Podsumowując kod, chcę aby na lcd wyświetlał mi ile cykli na sekundę faktycznie wywołuje timer0. Wyniki jednak odbiegają od teorii o kilkanaście procent. Czy robię coś źle w rozumowaniu czy tak poprostu jest, że w C nie ma się kontroli nad ilością operacji, nawet jeżeli nadaje timerom wartość początkową w pierwszej instrukcji obsługi przepełnienia.?
  • REKLAMA
  • Pomocny post
    #2 8289507
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Do dokładnego odliczania czasu stosuje się PORÓWNANIE timera, a nie antyczną metodę wpisywania wartości początkowej. Poczytaj datasheeta pod kątem hasła "compare"

    No i "jak zwykle" - volatile.

    4\/3!!
  • REKLAMA
  • #3 8291491
    bisz
    Poziom 18  
    Dobrze, zatem po zmodyfikowaniu programu :
    
    #include <avr/io.h> 
    #include <avr/interrupt.h> 
    #include <avr/signal.h> 
    #include "lcd.c" 
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) 
    #define cbi(port, bit) (port) &= ~(1 << (bit)) 
    
    
    volatile unsigned long T=0; 
    SIGNAL(SIG_OUTPUT_COMPARE0)
       { 
       T++; 
       } 
    
    
    int main( void ) 
    { 
    lcd_init(LCD_DISP_ON); 
    sei(); 
    
    //timer1 do zliczania km/h 16 bitowy : 
    TCNT1 = 0xc2f6; // wartosc poczatkowa, T=1s c2f6 
    TCCR1A = 0x00; // T/C1 w trybie czasomierza 
    TCCR1B = _BV(CS10)|_BV(CS12); // preskaler ck/1024 
    
    
    //timer0 do zliczania obrotow silnika i synchronizacji z kolem zebatym 8 bitowy 
    TCNT0 = 0; 
    TCCR0 = _BV(CS01) ; // preskaler ck/8 
    TIMSK = _BV(TOIE0)|_BV(OCIE0); //wlacza obsluge przerwan T/C0 
    OCR0=0x50;
    
    while(1) //petla glowna 
    { 
    if ((_SFR_BYTE(TIFR) & _BV(TOV1)) == _BV(TOV1)) {//co 1 s 
    TCNT1 = 0xc2f6; 
    TIFR = _BV(TOV1); 
    lcd_puts(ltoa(T,buffer,10)); 
    
    T=0; 
    }}


    w efekcie nie wchodzi w ogóle w pętle główną... co jest nie tak ?
  • REKLAMA
  • #4 8291657
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Włączasz dwa przerwania, obsługujesz jedno, więc czemu się dziwić?

    Pozatym tryb porównania można wykorzystać przy każdym timerze, więc przestań w ogóle myśleć o jakiejś "wartości początkowej" to nigdy nie będzie dokładne już z samego założenia.

    4\/3!!
  • REKLAMA
  • #5 8291802
    gaskoin
    Poziom 38  
    słabo coś przeczytałeś tego datasheeta. Co do tego co freddie napisał: jak włączysz przerwanie a go nie obsłużysz, to podczas jego wystąpienia następuje reset.

    
    
    ISR(TIMER1_COMPA_vect){
    	usmaż_placki();
    }
    
    void timer_init(void){
    
    	OCR1A = 1000;									//do ilu ma liczyc licznik
    	TCCR1B |= (1 << CS11) | (1<<WGM12);				//Ustawienie timera z preskalerem 8 w tryb ctc
    	TIMSK |= (1 << OCIE1A);							//wlaczenie obslugi przerwania
    	sei();											//odblokowanie globalnych przerwan
    
    }
    
  • #6 8298878
    asembler
    Poziom 32  
    Reset? czy skok do obsługi nieistniejącej obsługo przerwania? co wcale nie musi wywołac resetu.
  • #7 8299709
    tmf
    VIP Zasłużony dla elektroda
    asembler - to jest C, w dodatku C na AVR, więc zamiast gdybać należy spojrzeć do dokumentacji.
    W przypadku wywołania przerwania dla którego nie ma procedury obsługi następuje skok pod wektor __bad_interrupt, pod którym to znajduje się instrukcja jmp 0, czyli programowy reset. Także efekt nie będzie do końca taki jak po resecie, bo nie zostaną zainicjalizowane rejestry i przestrzeń IO procesora.
REKLAMA