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

obliczanie timerów - teoria a praktyka

bisz 13 Lip 2010 23:16 1698 6
  • #1 13 Lip 2010 23:16
    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.


    Code:

    #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.?

    0 6
  • Pomocny post
    #2 13 Lip 2010 23:22
    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!!

    0
  • #3 14 Lip 2010 15:54
    bisz
    Poziom 18  

    Dobrze, zatem po zmodyfikowaniu programu :

    Code:

    #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 ?

    0
  • #4 14 Lip 2010 16:41
    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!!

    0
  • #5 14 Lip 2010 17:24
    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.

    Code:


    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

    }

    0
  • #6 16 Lip 2010 16:33
    asembler
    Poziom 32  

    Reset? czy skok do obsługi nieistniejącej obsługo przerwania? co wcale nie musi wywołac resetu.

    0
  • #7 16 Lip 2010 20:50
    tmf
    Moderator Mikrokontrolery Projektowanie

    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.

    0