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

[mega32][C] timer, przerwania i precyzyjne odmierzanie czasu

stoowa 07 Lip 2008 22:13 7629 34
REKLAMA
  • #1 5321634
    stoowa
    Poziom 14  
    witam!
    Musze dosc precyzyjnie odmierzac czas (1ms)
    dysponuje ATmega 32 z zewnetrznym kwarcem 8Mhz...
    ustawilem sobie preskaler timmera na 8 ( czyli stuka sobie z f= 1Mhz)
    teoretycznie powinno byc dobrze... ale w praktyce pomiar spoznia mi sie o 3sec. na minute ( ???!!!) juz nie wiem co robie nie tak...

    jesli ktos moze spojrzec bylbym wdzieczny...

    
    #include <inttypes.h>
    #include <avr/io.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <avr/iom32.h>
    #include <avr/interrupt.h>
    #include <string.h>
    #include <avr/eeprom.h>
    #include <avr/pgmspace.h>
    #include "delay.h"
    #include "lcd.h"
    
    
    #define rf_in PD2 //wejscie na odbiornik
    #define rf_out PD3 //wyjscie na nadajnik
    #define led1   PB0//dioda LED
    #define led2   PB1//dioda LED
    #define led3   PB2//dioda LED
    #define button1   PC5//przycisk1
    #define button2   PC6//przycisk2
    #define button3   PC7//przycisk3
    
    
    //definicje zmiennych
    
    
    
    uint8_t bit,bajt; // odebrane bity i bajty
    int dms, ms, ds, s, ks;
    
    
    char buf[256];
    
    int aa, bb, cc;
    
    
    
    SIGNAL(SIG_OVERFLOW0)
    {
    aa++;
    
    
    }
    
    
    
    SIGNAL(SIG_INTERRUPT0)
    {
    	
    }
    
    
    
    SIGNAL(SIG_OVERFLOW1)
    {
    
    bb++;
    
    }
    
    SIGNAL(SIG_INTERRUPT1)
    {
    
    }
    
    
    SIGNAL(SIG_OVERFLOW2)
    {
    TCNT2=0x9C; // 156 = 9C 
    cc++;
    	
    }
    
    
    
    SIGNAL(SIG_INTERRUPT2)
    {
    
    }
    
    
    
    
    int main(void)
    {
    
    //ustawienia portów
    
    PORTA=0x00;
    DDRA=0xFF;
    
    PORTB=0xff;
    DDRB=0xFF;
    
    PORTC=0x00;
    DDRC=0x00;
    
    PORTD=0x00;
    DDRD=0x10;
    PORTD=0x00;
    
    
    
    TCCR0=0x02;   //2            4=preskaler 256
    TCNT0=0x00;
    OCR0=0x00;
    
    
    TCCR1A=0x00;
    TCCR1B=0x02;	// 02 = podzial przez 8
    TCNT1H=0x00;
    TCNT1L=0x00;
    OCR1AH=0x00;
    OCR1AL=0x00;
    OCR1BH=0x00;
    OCR1BL=0x00;
    
    
    ASSR=0x00;
    TCCR2=0x02;  
    TCNT2=0x00;
    OCR2=0x00;
    
    
    
    GICR|=0x40;
    MCUCR=0x03;  //3 zbocze narastajace INT0 generuje przerwani
    MCUCSR=0x00;
    GIFR=0x40;
    
    
     LCD_init();
     LCD_xy (0,0);
    
    GIMSK |=_BV(6);//odblokowanie przerwania INT0
    GICR |=_BV(6);
    TIMSK=0x45;
    sei();
    
    
    TCNT0=0x00;
    TCNT1=0x00;
    TCNT2=0x00;
    
    
    dms=0;
    
    while(1){
    
    
    
    if(cc>99){
    ++ms;
    cc=0;
    }
    
    
    if(ms>99){
    
    ++ds;
    ms=0;
    ++s;
    }
    
    
    LCD_xy(0,1);
    sprintf(buf,"czas :%03d ",s);
    LCD_putstr(buf);
    
    
     }//koniec while(1)
    
    }//end main
    
    
    


    Poprawiłem tytuł:
    https://www.elektroda.pl/rtvforum/topic1015361.html
    [c_p]
  • REKLAMA
  • #2 5321658
    ZbeeGin
    Poziom 39  
    Hmmm.. Już szerzej się tego napisać nie dało?

    Nie zapominaj, że licznik liczy do 655336 (16bitowy) / 256 (8 bitowy). Różnica o jeden nałożona przez ileś tam pętli może dać takie anomalie.

    Jesteś na 100% pewien, że fuseboty masz ustawione na External Crystal?
  • #3 5321670
    Balu
    Poziom 38  
    A includów więcej nie dało sie? zostały jeszcze ze 4 nagłówki których tam nie masz:P

    PS czemu wszyscy upierają się na signal a nie po ludzku jak to każe książek kucharski na ISR (nowa nomenklatura)?
  • #4 5321676
    stoowa
    Poziom 14  
    a jakos tak mi sie napisalo na okolo... :) ale jestem otwarty na sugestie...
    uzywam T2 wiec 8 bitow ( 256)
    na 100% mam ext crystal... (sprawdzilem... jak sobie zewre nozki kwarcu to scalak przestaje pracowac :) )
  • Pomocny post
    #5 5321681
    ZbeeGin
    Poziom 39  
    Ponadto pierwsze przejście licznika masz pełne 0...256 a potem dopiero licznik liczy 100 impulsów.
  • #6 5321698
    stoowa
    Poziom 14  
    masz racje... dopisze sobie w inicjalizacji ustawienie licznika..
    ale ta pierwsza bledna wartosc TCNT2 tak nie spowoduje 5% opoznienia... ( okolo 3sec na minute... )
  • #7 5321747
    ZbeeGin
    Poziom 39  
    Wczytaj sobie kod do AVR-Studio i uruchom symulację. Będziesz widział, że czas pomiędzy przerwaniami będzie rósł o parę mikrosekund co każde przerwanie.
    Wszystko przez czas jaki mija od zgłoszenia przerwania, a wykonania instrtrukcji ładującej nową wartość do licznika. Daltego warto zrobić opcję z dodawaniem:

    TCNT2 |= 0x9C;
  • #8 5321927
    stoowa
    Poziom 14  
    pomijajac juz naglowki itd... kod zoslal zmieniony na :
    
    //definicje zmiennych
    
    int dms, ms, ds, s, ks;
    char buf[256];
    int aa, bb, cc;
    
    SIGNAL(SIG_OVERFLOW0)
    {
    aa++;
    }
    
    
    
    SIGNAL(SIG_INTERRUPT0)
    {
       
    }
    
    
    
    SIGNAL(SIG_OVERFLOW1)
    {
    
    bb++;
    
    }
    
    SIGNAL(SIG_INTERRUPT1)
    {
    
    }
    
    
    SIGNAL(SIG_OVERFLOW2)
    {
    TCNT2|=0x9C; // 156 = 9C
    cc++;
       
    }
    
    
    SIGNAL(SIG_INTERRUPT2)
    {
    
    }
    
    
    
    int main(void)
    {
    
    //ustawienia portów
    
    PORTA=0x00;
    DDRA=0xFF;
    
    PORTB=0xff;
    DDRB=0xFF;
    
    PORTC=0x00;
    DDRC=0x00;
    
    PORTD=0x00;
    DDRD=0x10;
    PORTD=0x00;
    
    
    
    TCCR0=0x02;   //2            4=preskaler 256
    TCNT0=0x00;
    OCR0=0x00;
    
    
    TCCR1A=0x00;
    TCCR1B=0x02;   // 02 = podzial przez 8
    TCNT1H=0x00;
    TCNT1L=0x00;
    OCR1AH=0x00;
    OCR1AL=0x00;
    OCR1BH=0x00;
    OCR1BL=0x00;
    
    
    ASSR=0x00;
    TCCR2=0x02; 
    TCNT2=0x9C;
    OCR2=0x00;
    
    
    GICR|=0x40;
    MCUCR=0x03;  //3 zbocze narastajace INT0 generuje przerwani
    MCUCSR=0x00;
    GIFR=0x40;
    
    
     LCD_init();
     LCD_xy (0,0);
    
    GIMSK |=_BV(6);//odblokowanie przerwania INT0
    GICR |=_BV(6);
    TIMSK=0x45;
    sei();
    
    dms=0;
    
    while(1){
    
    
    if(cc>99){
    ++ms;
    cc=0;
    }
    
    if(ms>99){
    ++ds;
    ms=0;
    ++s;
    }
    
    
    LCD_xy(0,1);
    sprintf(buf,"czas :%03d ",s);
    LCD_putstr(buf);
    
    
     }//koniec while(1)
    
    }//end main 
    


    niestety ciagle pomiar nie jest wlasciwy... a wydawaloby sie ze wystarczy odmierzyc kilka taktow timera i po wszystkim... ;/

    jesli ktos ma pomysl na inny sposob odmierzania czasu to jestem otwarty na propozycje :)
  • #9 5322718
    szelus
    Poziom 34  
    Bo do pomiaru czasu najbezpieczniej (najdokładniej) używać timera w trybie CTC i nie dotykać go po inicjalnym ustawieniu.
    Tak jak teraz (w trybie 0), przy taktowaniu 1MHz masz tylko 10us od wystapienia przerwania na uaktualnienie rejestru licznika - inaczej będzie "poślizg". A 10us to nie jest dużo - powiedziałbym, że bardzo mało.

    Ewentualnie przestaw preskaler na wolniejszy zegar do licznika - będziesz miał więcej czasu na obsługę.

    P.S.
    ZbeeGin napisał:

    TCNT2 |= 0x9C;

    Chyba lepiej:
    TCNT2 += 0x9C;

    ;)
  • REKLAMA
  • #10 5322791
    stoowa
    Poziom 14  
    niestety scalak oprocz pomiaru czasu bedzie musial odebrac transmisje bezbprzewodowa ( odbior przerywa pomiar czasu)...
    teraz opoznienie jest dosc spore...a scalak nie robi nic oprocz liczenia... nie wiem czy CTC to zmieni... ;/ jak tylko bede mial okazje postaram sie to sprawdzic... jakies inne sugestie ??

    wydawaloby sie, ze banalny pomiar czasu, a jakie niespodzianki.
  • #11 5322947
    szelus
    Poziom 34  
    Po pierwsze, w trybie CTC masz prawie tyle czasu, ile normalnie jet pomiędzy przerwaniami od licznika na obsłużenie przerwania.
    Po drugie - o ile używsz timera 2 tylko do mierzenia czasu (zegarowego) to spokojnie możesz ustawić preskaler na 256 i podzielnik na 250 (w trybie CTC) - będziesz miał przerwanie co 8ms i tyle też prawie czasu na jego obsłużenie.

    Po trzecie - niespodzianki, jak dla kogo. ;) Dokładny pomiar czegokolwiek to z reguły nie jest banalna sprawa. ;)
  • #13 5323204
    szelus
    Poziom 34  
    Balu napisał:
    Heh a wystarcz wstawić 32768Hz i użyć timera 2...

    Może źle zrozumiałem intencję Twojej wypowiedzi, ale ja widzę tu tylko różnicę dokładności pomiędzy ok. 1,5s/dobę na kwarcu zegarkowym a ok. 4s na dobę na zwykłym...
  • #14 5323226
    Balu
    Poziom 38  
    No powiedzmy, po to zostało to wymyślone, aby tego używać, więc czemu nie używać? Koszta? 30groszy w detalu? Oczywiście można zrobić na głównym kwarcu. Ale tak jak piszę, skoro producent przewidział taką opcję... trzeba z niej skorzystać:) i ni utrudniać Sobie życia.
  • REKLAMA
  • #15 5323416
    marek-c
    Poziom 19  
    Mam na timerach zrobiony taki dziwny układ: czeka n (1..24) godzin i załącza przekaźnik na 10*n sekund (10-250).
    8MHz i preskaler /256 co daje 31250 taktów na sekundę.
    Kilka dni toto sprawdzałem i ... żeby wyszło w miarę dokładnie 24h licznik mam 31246!
    Albo kwarc trochę niedokładny, albo ... nie wiem co, ale przy zliczaniu do 31246 jest dobrze.

    Tak więc jakbym miał coś proponować:
    -duża liczba taktów
    -licznik... hmmm milisekunda powiadasz
    -i eksperymentalnie dobrać dokładną wartość.

    Niestety nie podpowiem na ile program wykonywany wpływa na dokładność, bo nie sprawdzałem.
    aha! też zastanawiałem nad zastosowaniem osobnego kwarcu, ale to co otrzymałem mi starcza. No i 1ms przy 32,768 kHz, może być 'ciasno'.
    Ale ja 'guru' nie jestem.


    Marek
  • #16 5324318
    ZbeeGin
    Poziom 39  
    szelus napisał:

    P.S.
    ZbeeGin napisał:

    TCNT2 |= 0x9C;

    Chyba lepiej:
    TCNT2 += 0x9C;

    Właśnie, że takie podejście jest złe. Ma to być suma logiczna nie arytmetyczna.
    2 + 192 to nie to samo co 2 | 192.
  • #17 5326260
    szelus
    Poziom 34  
    ZbeeGin napisał:

    Właśnie, że takie podejście jest złe. Ma to być suma logiczna nie arytmetyczna.
    2 + 192 to nie to samo co 2 | 192.

    Może ja się jeszcze całkiem nie obudziłem dzsiaj, ale...
    1. Oczywiście, że suma logiczna to co innego niż arytmetyczna :)
    2. Akurat 2|192 = 194 = 2+192 ;)
    3. Dalej nie wiem, dlaczego akurat suma logiczna.

    Chcemy skrócić czas do następnego przerwania o czas opóźnienia obsługi bieżącego. Czyli trzeba odjąć od okresu bieżącą wartość licznika (liczącego w przód). Ponieważ faktycznie ładowana stała jest ujemna, to odejmowanie realizujemy przez dodawanie. :)

    Normalnie mamy (dla 8-bitowego licznika liczącego w przód):

    licznik = 256 - OKRES => licznik = (-OKRES)

    korekcja opóźnienia obsługi:
    nst_okres = OKRES - opóźnienie
    licznik = -nst_okres => licznik = -(OKRES - licznik) => licznik += (-OKRES)
  • Pomocny post
    #18 5326572
    ktrot
    Poziom 20  
    >stoowa Twoje kłopoty powodują 3 ostatnie linijki w pętli while czyli obsługa LCD. W ciele funkcji obsługujących lcd na pewno sa instrukcje blokowania przerwań na czas transmisji danych do lcd. Powoduje to pomijanie wywołań przerwań a co za tym idzie zegarek będzie się spóźniał.
    Cytat:

    Chcemy skrócić czas do następnego przerwania o czas opóźnienia obsługi bieżącego.

    To tak nie działa. Przerwania od timerów są wywoływane regularnie o ile tylko nie zmieniamy odpowiednich wartości TCNT i nie przekroczymy czasu trwania przerwania. Liczba taktów w obsłudze przerwania jest nie istotna (z zastrzeżeniem jak wyżej).

    Rozwiązanie szybkie (nie idealne):
    Ustaw jakiś znacznik i wyświetlaj czas na lcd gdy ten czas się zmieni.

    Rozwiązanie problemu:
    Skoro potrzebujesz dokładność 1ms to ustaw przerwanie timera na 1ms.
    Może być timer2 i przerwanie overflow. preskaler na 32 lub 64 wartość tcnt2 sobie policz. Wszystkie operacje z inkrementacją tych milisekund możesz zrobić bezpośrednio w ciele przerwania.
  • #19 5327022
    krzemowy
    Poziom 19  
    Mój post z innego wątku:


    Można to zrobić np. tak:

    volatile unsigned long int czas = 0;
    volatile unsigned long int czas_wynikowy = 0;
    
    (przerwanie od zbocza sygnału taktującego)
    {
        czas += (rejestr licznika); //zapisanie aktualnie pomierzonego czasu
        (rejestr licznika) = 0;   //wyzerowanie licznika
        czas_wynikowy = czas;  //przepisanie całego pomierzonego czasu
        czas = 0;
    }
    
    (przerwanie od przepełnienia licznika)
    {
        czas += 0x10000;  //zwiększenie zmierzonego czasu o 65536 - zakładam że licznik ma 16 bitów
    }
    


    Drugi sposób:

    volatile signed long int czas = 0;
    volatile unsigned long int czas_zmierzony = 0;
    (przerwanie od rejestru przechwytywania licznika wysterowanego zboczem sygnału taktującego)
    {
         czas += (rejestr przechwytywania);  //dodajemy aktualnie pomierzony czas
         czas_zmierzony = czas;  //przepisujemy wynik
         czas = -(rejestr przechwytywania); //dlatego jest to zmienna signed a nie unsigned
    }
    
    (przerwanie od przepełnienia licznika)
    {
         czas += 0x10000;  //podobnie jak wyżej inkrementacja przy założeniu licznika 16 bitów
    }
    


    Kod jest pisany dla licznika 16bitowego ale dla 8bitowego też będzie działał, wystarczy zmienić jedną rzecz. W taki sposób uzyskujemy 31(albo 23) bitów pojemności licznika i precyzja jest perfekcyjna. Przy pomocy takiego algorytmu mierzę czasy między impulsami z czujnika położenia wału korbowego w centralce wtrysku i największy błąd jaki udało mi się wymierzyć był rzędu 0.02%.
  • #20 5327105
    szelus
    Poziom 34  
    ktrot napisał:
    >
    To tak nie działa. Przerwania od timerów są wywoływane regularnie o ile tylko nie zmieniamy odpowiednich wartości TCNT i nie przekroczymy czasu trwania przerwania. Liczba taktów w obsłudze przerwania jest nie istotna (z zastrzeżeniem jak wyżej).

    Chyba się chyba nie rozumiemy. Tak jest owszem, o ile timer pracuje w trybie CTC, ale wtedy wyliczoną wartość okresu trzeba wpisać do odpowiedniego rejestru xCMP, a nie do TCNT.

    Ale kolega stoowa używa timera w trybie 0, a tu jest troche inaczej ;) I do takiego wykorzystania odnosiła się sugestia kolegi ZbeeGin i moje późniejsze dywagacje.

    Przejście na tryb CTC sugerowałem wyżej. ;)
  • Pomocny post
    #21 5327492
    ktrot
    Poziom 20  
    Wszystko co napisałem powyżej pozostaje w mocy. Tryby normalny i CTC działają przy odmierzaniu czasu podobnie: obydwa tryby odliczaja ileś tam tyknięć rejestru TCNT i generują przerwanie. W przypadku trybu normalnego to liczenie następuje od wartości ustawianej w TCNT do wartości 0 (256), a w trybie CTC od wartości 0 do OCR (lub ICR).

    Zacznijmy od tego, że kolega stoowa prawidłowo zrozumiał i zastosował licznik timer2 z przerwaniem overflow. Zegar 8MHz i preskaler 8 daje 1MHz i z tą czestotliwością pracuje u niego licznik TCNT2. Wpisując za każdym razem początkową wartość 0x9C (156) odmierza 100 tyknięć TCNT2 (256-156). Innym słowy przerwanie wywołuje się co 1/10000s i dalej za pomocą zmiennych odlicza te 10tys tyknięć otrzymując 1 sekundę. W przerwaniu ma być TCNT2=0x9c a nie zadne +=0x9c.
    Dlaczego tak ustawiony timer się spóźnia napisałem wyżej (obsługa lcd).

    Na koniec 2 przykłady odmierzania 1ms - jeden dla trybu normalnego i jeden dla CTC (z OCR2 jako TOP):

    1. Tryb 0 (normalny)
    - ustawiamy licznik w tryb 0
    - uaktywniamy przerwanie OVF2
    - preskaler na 64
    - TCNT2=0x83 (odliczenie 125 tyknięć co razem z preskalerem da 125*64=8000 taktów zegara)
    W ciele przerwania:
    ISR2_OVF
    {
    TCNT2=0x83
    //ewentualne inne operacje
    }

    2. Tryb CTC
    - ustawiamy licznik w tryb CTC z OCR2 jako TOP
    - aktywujemy przerwanie CTC2
    - preskaler 64
    - OCR2=0x7D (7D=125 tyknięć)
    - TCNT2=0
    W ciele przerwania:
    ISR2_CTC
    {
    //ewentualne operacje - nie ma potrzeby wpisywać TCNT2=0
    }
  • REKLAMA
  • #22 5327558
    szelus
    Poziom 34  
    Nie myślałem, że to takie trudne do załapania o co mi chodzi...

    Wg. punktu 1 powyżej - jeżeli przerwania pozostają zablokowane przez czas dłuższy niż 64 takty zegara, czyli 8us, to istnieje ryzyko (graniczące z pewnością), że to nie będzie działać poprawnie:

    1. zgłaszane jest przerwanie, ale przerwania są zablokowane
    2. mija kolejne 64 takty, tajmer przeskakuje z 0 na 1
    3. opcjonalnie mija kolejne (N-1)*64 takty
    4. odblokowujemy przerwania i przechodzimy do obsługi przerwania od timera
    5. wpisujemy do licznika 0x83
    6. właśnie spóźniliśmy się z odliczaniem czasu o N*64 takty.

    Jeżeli w kroku 5 dodawalibyśmy do licznika timera to 0x83, to skompensowalibyśmy to nieszczęsne opóźnienie. Choć i tak może być to mniej dokładne jak timer w trybie CTC.
  • #23 5327754
    ktrot
    Poziom 20  
    Cytat:
    Wg. punktu 1 powyżej - jeżeli przerwania pozostają zablokowane przez czas dłuższy niż 64 takty zegara, czyli 8us, to istnieje ryzyko (graniczące z pewnością), że to nie będzie działać poprawnie:

    Nie. Przerwanie jest wywoływane co 8000 taktów czyli co 1ms.
    Na tyknięcia rejestru TCNT2 przerwania nie mają wpływu.

    Jezeli w czasie, w którym powinno zostać zrealizowane przerwanie przerwania bedą zablokowane to zostanie ono pominięte niezależnie czy to bedzie przerwanie od OVF CTC czy INT0.

    Cytat:
    Jeżeli w kroku 5 dodawalibyśmy do licznika timera to 0x83, to skompensowalibyśmy to nieszczęsne opóźnienie.

    Nie. Nie ma co poprawiać Wartość TCNT2 w tym momencie wynosi zero.
  • #24 5327812
    szelus
    Poziom 34  
    ktrot napisał:

    Jezeli w czasie, w którym powinno zostać zrealizowane przerwanie przerwania bedą zablokowane to zostanie ono pominięte niezależnie czy to bedzie przerwanie od OVF CTC czy INT0.

    Nie zostanie pominięte, tylko obsłużone po odblokowaniu przerwań. Chyba, że byłoby zablokowane dłużej niż 1ms, ale ja nie o tym.

    Cytat:

    Nie. Nie ma co poprawiać Wartość TCNT2 w tym momencie wynosi zero.


    W AVR timer w trybie 0 się nie zatrzymuje po zgłoszeniu przerwania, tylko liczy dalej. Jeżeli wejdziesz do obsługi przerwania z opóźnieniem, to wartość TCNT nie bedzie zero, tylko proporcjonalna do czasu opóźnienia. Dlatego sztuczka z dodawaniem może ewentualnie zadziałać.

    Po drugie, na wpisanie nowej wartości do odliczenia, od momentu zgłoszenia przerwania masz tylko czas do nastepnego tyknięcia licznika, a nie do nastepnego zgłoszenia przerwania. Czas będzie bowiem odmierzany od momentu wpisania, a nie od poprzedniego przerwania.
    Oczywiście, to tylko wtedy, gdy chcesz mieć przerwania co stały odcinek czasu (stały okres).
  • #25 5328176
    ktrot
    Poziom 20  
    Cytat:
    Po drugie, na wpisanie nowej wartości do odliczenia, od momentu zgłoszenia przerwania masz tylko czas do nastepnego tyknięcia licznika, a nie do nastepnego zgłoszenia przerwania.

    To nie jest problem bo instrukcja ustawienia TCNT2 powinna być pierwszą instrukcją w przerwaniu. Niemniej zgadzam się z Twoimi sugestiami co do wad przerwania overflow w sytucji jeżeli mamy niekontrolowane blokowanie przerwań. Jeżeli przed udostępnieniem przerwań nie ustawiamy TOV2 (co też jest złe bo spowoduje pominięcie całego cyklu) to przerwanie zostanie wykonane z opóźnieniem. Co gorsza to dodawanie aktualnej wartości TCNT2 tylko częściowo rozwiązuje problem bo działa z dokładnością do preskalera (czyli maksymalne opóźnienie moze wynieść 63 takty dla preskalera 64). W tej sytuacji tryb CTC lepszy!
  • #26 5330040
    stoowa
    Poziom 14  
    Przedewszystkim dziekuje wszytkim za zainteresowanie tematem... :)

    Balu napisał:
    Heh a wystarcz wstawić 32768Hz i użyć timera 2...


    rozwaze tą opcje... narazie pomecze sie z kwarcem glownym... mysle ze ta 1ms jest tu do uzyskania... :)


    ktrot napisał:
    >stoowa Twoje kłopoty powodują 3 ostatnie linijki w pętli while czyli obsługa LCD.

    <ciach>
    Rozwiązanie szybkie (nie idealne):

    Ustaw jakiś znacznik i wyświetlaj czas na lcd gdy ten czas się zmieni.

    <ciach>

    Rozwiązanie problemu:
    Skoro potrzebujesz dokładność 1ms to ustaw przerwanie timera na 1ms.
    Może być timer2 i przerwanie overflow. preskaler na 32 lub 64 wartość tcnt2 sobie policz. Wszystkie operacje z inkrementacją tych milisekund możesz zrobić bezpośrednio w ciele przerwania.


    Dziekuje bardzo!! faktycznie eliminacja LCD pomogla dosc znacznie.. w tej chwili wyswietlacz uruchamiam co sekunde i mam opoznienia "tylko" 6 sekund na godzine... co daje jakies 1,7ms /sec
    (przy uruchamianiu LCD raz na 10sec. ten sam efekt.. okolo 6s na godzine w plecy...)

    no juz sam nie wiem... spróbowalem jeszcze tą opcję, która wg. Ciebie powinna zalatwic problem (OVF T2 co 1ms)... teraz spieszy mi sie o jakies 8 sec na 10 minut... cos nie tak jest ze zliczaniem.. poniewaz blad w obydwu wypadkach narasta w miare uplywu czasu... jakies pomysły?
  • #27 5333203
    szelus
    Poziom 34  
    stoowa napisał:

    no juz sam nie wiem... spróbowalem jeszcze tą opcję, która wg. Ciebie powinna zalatwic problem (OVF T2 co 1ms)... teraz spieszy mi sie o jakies 8 sec na 10 minut... cos nie tak jest ze zliczaniem.. poniewaz blad w obydwu wypadkach narasta w miare uplywu czasu... jakies pomysły?


    Tzn. próbowałeś rozwiązania opisnego przez ktrota jako punkt 2 (tryb CTC)? To powinno działać bez problemu. Jeżeli próbowałeś tego i nie działa, to może wrzuć tu swój nowy kod.
  • #28 5333391
    stoowa
    Poziom 14  
    probowalem tego rozwiazania:

    1. Tryb 0 (normalny)
    - ustawiamy licznik w tryb 0
    - uaktywniamy przerwanie OVF2
    - preskaler na 64
    - TCNT2=0x83 (odliczenie 125 tyknięć co razem z preskalerem da 125*64=8000 taktów zegara)
    W ciele przerwania:
    ISR2_OVF
    {
    TCNT2=0x83
    //ewentualne inne operacje
    }


    za CTC wezme sie wieczorem... ja to tez zawiedzie to ja juz nie mam pomyslu...
  • #29 5333538
    szelus
    Poziom 34  
    No przecież pisałem, że to rozwiązanie ma małe szanse działać poprawnie, bo jest b. mało czasu na obsługę przerwania. Jaki problem ustawić tryb CTC i zamknąć sprawę?

    P.S. Ale to, że się spieszy, to b. dziwne. Coś musiałeś zrobić nie tak.
  • #30 5334166
    stoowa
    Poziom 14  
    1. Trybem CTC zajmę sie za chwilkę... ;)

    2.obecnie program ma pustać:
    
    
    #include <inttypes.h>
    #include <avr/io.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <avr/iom32.h>
    #include <avr/interrupt.h>
    #include <string.h>
    #include <avr/pgmspace.h>
    #include "delay.h"
    #include "lcd.h"
    
    
    
    
    //definicje zmiennych
    
    
    int ms,s, znacznik;
    
    char buf[256];
    
    int aa, bb, cc;
    
    
    
    SIGNAL(SIG_OVERFLOW0)
    {
    aa++;
    }
    
    
    SIGNAL(SIG_INTERRUPT0)
    {
    }
    
    
    SIGNAL(SIG_OVERFLOW1)
    {
    bb++;
    }
    
    SIGNAL(SIG_INTERRUPT1)
    {
    }
    
    
    SIGNAL(SIG_OVERFLOW2)
    {
    TCNT2=0x06;
    ms++;	
    }
    
    
    SIGNAL(SIG_INTERRUPT2)
    {
    }
    
    
    
    
    int main(void)
    {
    
    //ustawienia portów
    
    PORTA=0x00;
    DDRA=0xFF;
    
    PORTB=0xff;
    DDRB=0xFF;
    
    PORTC=0x00;
    DDRC=0x00;
    
    PORTD=0x00;
    DDRD=0x10;
    PORTD=0x00;
    
    
    
    // Timer/Counter 0 initialization
    TCCR0=0x02;   //2            4=preskaler 256
    TCNT0=0x00;
    OCR0=0x00;
    
    
    
    // Timer/Counter 1 initialization
    TCCR1A=0x00;
    TCCR1B=0x02;	// 02 = podzial przez 8
    TCNT1H=0x00;
    TCNT1L=0x00;
    OCR1AH=0x00;
    OCR1AL=0x00;
    OCR1BH=0x00;
    OCR1BL=0x00;
    
    // Timer/Counter 2 initialization
    ASSR=0x00;
    TCCR2=0x03;  //podzial na 32
    TCNT2=0x06;		//9c
    OCR2=0x00;
    
    // External Interrupt(s) initialization
    
    GICR|=0x40;
    MCUCR=0x03;  //3 zbocze narastajace INT0 generuje przerwani
    MCUCSR=0x00;
    GIFR=0x40;
    
    GIMSK |=_BV(6);//odblokowanie przerwania INT0
    GICR |=_BV(6);
    TIMSK=0x45;
    sei();
    
    LCD_init();
    
    znacznik=2;
    
    
    
    while(1){
    
    
    
    if (ms>999){
    
    ++s;
    znacznik++;
    ms=0;
    
    }
    
    
    
    
    if(znacznik>0){
    LCD_xy(0,0);
    sprintf(buf,"czas :%03d ",s);
    LCD_putstr(buf);
    znacznik=0;
    }
    
    
    if (PIND & (1 << PD7))
    {
    }
    
    
     }//koniec while(1)
    
    }//end main
    
    
    


    w ciągu 15 minut ( 900s) naliczył mi 907s czyli spieszy sie o 7...

    zauważyłem jeszcze ze jak usunę ten fragment:
    
    if (PIND & (1 << PD7))
    {
    }
    


    program chyba w ogóle nie wchodzi do pętli w której jest procedura wyświetlania czasu-nic nie pojawia sie na wyswietlaczu...
    normalnie jestem już "osłabiony" i nie wiem co jest...
REKLAMA