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.

[ATMega16] Pomiar czasu - mikrosekundy

andrusz 04 Lis 2009 13:05 3373 11
  • #1 04 Lis 2009 13:05
    andrusz
    Poziom 9  

    Witam,

    Od kilku dni próbuję za pomocą ATMega16 zmierzyć czasy:
    - opóźnienia jednego impulsu w stosunku do drugiego (czasy 70-100µs)
    - czas drugiego impulsu (czasy są 10-500µs)
    I wyniki są niezbyt dobre - kilka pomiarów daje wyniki całkowicie różne (a czasem nawet wartości ujemne). A próbowałem tak robić:

    1. Ustawiam Timer2 na generowanie przerwania co 64µs (16MHz z prescalerem 8 ) - przerwanie generowane po przepełnieniu licznika:

    Code:
    ISR(TIMER2_OVF_vect) {
    
      counter++;
    }

    Do tego napisałem funkcję zwracającą czas w µs:
    Code:
    unsigned long mmicros() {
    
      unsigned char t = TCNT2;;
      return ((counter << 8) | t) >> 1;
    }

    Pomiaru czasu następuje za pomocą przerwań sprzętowych:
    - INT1 sygnał wyzwalający (zbocze opadające):
    - INT0 sygnał, który jest opóźniony w stosunku do tego INT1, którego czas mierzę (generowany po każdej zmianie stanu - logical change):
    Code:
    ISR(INT1_vect) {
    
      trigger_time = mmicros();
    }

    Code:
    ISR(INT0_vect) {
    
      unsigned long get_time = mmicros();
      if (start_time == 0) {
        start_time = get_time;
      } else {
        end_time = get_time;
      }     
    }

    A w programie głównym mam:
    Code:
    while (1) {
    
        trigger_time = 0;
        start_time = 0;
        end_time = 0;

        while (trigger_time == 0) ;
       
        while (start_time == 0) ;
       
        while (end_time == 0) ;

        [wysłanie wyniku]

    }

    No i czasy opóźnienia (start_time - trigger_time) są czasem sensowne (czas ok. 90µs), a czasem głupie np. 12 µs, a czasem ujemne :| Timer0 jest wyłączony, więc na pewno nie przeszkadza.

    Macie pomysł co jest nie tak, ew. jak zrobić ten pomiar lepiej?

    Pozdr.
    andrusz

    0 11
  • #2 04 Lis 2009 13:40
    tmf
    Moderator Mikrokontrolery Projektowanie

    Rozumiem, ze zmienne zadeklarowales jako volatile, czy zapomniales o tym?:)
    Druga sprawa - komplikujesz. Ustaw 16-bitowy timer tak, zeby np. mial impuls co 1us, albo czesciej, w zaleznosci jaka potrzebujesz rozdzielczosc. Impuls, ktorego czas badasz wyzwala przerwanie, odczytujesz zawartosc 16 bitowego timera, czekasz na kolejne przerwanie, ponownie odczytujesz. Roznica == czas trwania umpulsu w jednostkach rozdzielczosci timera. Koniec.

    0
  • #3 04 Lis 2009 14:16
    mirekk36
    Poziom 42  

    Albo użyj po prostu wejścia ICP związanego z Timerem1 , które będzie od razu generowało przerwania w których od razu też można pięknie obliczać czas trwania impulsu. Szybko łatwo i przyjemnie.

    Ja zwykle korzystam z ICP do odbierania kodów z pilotów IR gdzie trzeba właśnie dokładnie mierzyć czasy impulsów żeby odkodować nadlatujące dane.

    Post był raportowany.
    Pisanie postu pod postem.
    Należy używać przycisku ZMIEŃ.
    Scaliłem dwa posty. [hefid]

    0
  • #5 04 Lis 2009 15:38
    andrusz
    Poziom 9  

    Dzięki za tak szybkie odpowiedzi :-)

    tmf napisał:
    Rozumiem, ze zmienne zadeklarowales jako volatile, czy zapomniales o tym?:)
    Druga sprawa - komplikujesz. Ustaw 16-bitowy timer tak, zeby np. mial impuls co 1us, albo czesciej, w zaleznosci jaka potrzebujesz rozdzielczosc.


    Tak, zmienne mam jako volatile, ale rzeczywiście nie policzyłem, że counter tak szybko się przepełnia :idea: To pewnie dlatego wychodzą mi te ujemne wartości.

    Aaaa... nie zauważyłem, że ATMega16 ma licznik 16-bitowy ;-) spróbuję w takim razie użyć go tak, jak sugerujesz - co 1µs.

    Dodano po 5 [minuty]:

    Petros napisał:
    to zmienna counter przepełnia się po ok 4 sekundach i liczy od nowa!

    problemem mogą być drgania styków, co masz na INT1 i INT0


    Na stykach mam:
    INT1 - mikroprzełącznik podpięty do masy i podciągnięty do +5V opornikiem 10k
    INT0 - fotodioda podpięta do masy i prostego dzielnika napięcia (błysk światła wywołuje spadek napięcia do ok. zera - czas tego błysku mierzę).

    Może jakieś komparatory zastosować?

    Dodano po 5 [minuty]:

    mirekk36 napisał:
    Albo użyj po prostu wejścia ICP związanego z Timerem1 , które będzie od razu generowało przerwania w których od razu też można pięknie obliczać czas trwania impulsu. Szybko łatwo i przyjemnie.

    Szczerze mówiąc poznaję dopiero ATMega i nie wiem co daje wejście ICP - możesz krótko podpowiedzieć, jak go uzyć? Ale i tak zaraz poczytam dokumentację :-)

    0
  • #6 04 Lis 2009 16:27
    mirekk36
    Poziom 42  

    Wejście (przerwanie) ICP może być wyzwalane dowolnym zboczem i można to także zmieniać w przerwaniu jeśli jest to konieczne - zależy czy chcesz mierzyć okres czy pojedyńczy impuls - to tak sobie ustawiasz wyzwalanie.

    Generalnie Timer1 sobie cały czas tyka tak jak go ustawisz i za każdym razem gdy nastąpi przerwanie ICP to można odczytać rejestr ICR w którym jest bieżący stan licznika. Dzięki temu zapamiętując jaki był poprzednio można sobie obliczać swobodnie czas jaki upłynął pomiędzy tymi dwoma zdarzeniami.

    Code:
    SIGNAL(SIG_INPUT_CAPTURE1)
    
    {

         static uint16_t LastCapture;
         uint16_t PulseWidth;
         PulseWidth = ICR1 - LastCapture;
         LastCapture = ICR1;

      .... tutaj obrabiasz sobie to co chcesz z tym zmierzonym czasem PulseWidth

    }

    dzięki temu wszystko robisz korzystając tylko z jednego timera i jego przerwania - dzięki temu pozostałe przerwania masz wolne do innych zastosowań

    0
  • #7 04 Lis 2009 20:29
    janbernat
    Poziom 38  

    "Może jakieś komparatory zastosować? "
    Jeden komparator masz w ATmedze.

    0
  • #8 05 Lis 2009 10:55
    andrusz
    Poziom 9  

    mirekk36 zrobiłem tak, jak piszesz. Teraz INT0 zeruje mi liczniki, a ICP mierzy czas (raz dla zbocza opadającego, a potem dla narastającego). Działa super :-)

    Code:
    ISR(INT0_vect) {
    
      unsigned char oldSREG = SREG;
      cli();
     
      counter = 0;
      TCNT1 = 0;
     
      SREG = oldSREG;
    }

    ISR(TIMER1_CAPT_vect)
    {
      unsigned int curr_time = ICR1;
      if (start_time == 0) {
        start_time = curr_time;
      } else {
        end_time = curr_time;
      }
      TCCR1B ^= _BV(ICES1);
    }

    Okazało się, że... mam problem z elektroniką :| Bo "dziwne" pomiary znikają się, jak do pinu INT0 podepnę oscyloskop! Chyba jakieś stany nieustalone mi się pojawiają na tej linii. Na wykresie widzę to tak:

    [ATMega16] Pomiar czasu - mikrosekundy

    Niebieska linia to wyzwalanie z zewnętrznego źródła (INT0), czerwona to sygnał z fotodiody, którego czas mierzę (oraz opóźnienie w stosunku do "niebieskiego").

    Moglibyście coś doradzić, jak ustabilizować ten sygnał wyzwalający? Podpiąć zewnętrzny komparator np.LM339 (ten wbudowany nie wiem, czy jest wystarczająco szybki i chyba nie wygeneruje mi przerwania ICP)? Może wystarczy jakiś kondensator?

    I jeszcze przy okazji: używam starej fotodiody BPYP44A, ale nie wiem czy się nadaje do pomiaru tak szybkich impulsów (10µs)? Może są jakieś lepsze, dostępne w Polsce?

    0
  • #9 05 Lis 2009 13:11
    mirekk36
    Poziom 42  

    No ale czegoś nie rozumiem może - tzn coś robisz troszkę na okrętkę jeśli chodzi o ICP.

    Ja bym na twoim miejscu zamiast podawać ten sygnał na INTx - podałbym go właśnie na wejście ICP. Po co ta kombinacja z INT? Toż chodzi o to, że to właśnie ICP ustawiasz aby reagowało na początku na zbocze opadające i zostanie ładnie wygenerowane przerwanie TIMER1_CAPT_vect.

    I to tylko w nim dokonasz pomiaru i nie trzeba w ogóle resetować licznika Timer0 - bo po kiego? ;) podałem ci wyżej fragmencik kodu tego przerwania jak to sobie zacząć obsługiwać. Wtedy INTx możesz zostawić w całkowitym spokoju. Pomyśl jeszcze raz nad tym i pokombinuj.

    0
  • #10 05 Lis 2009 15:35
    andrusz
    Poziom 9  

    mirekk36 napisał:
    No ale czegoś nie rozumiem może - tzn coś robisz troszkę na okrętkę jeśli chodzi o ICP.

    Ja bym na twoim miejscu zamiast podawać ten sygnał na INTx - podałbym go właśnie na wejście ICP. Po co ta kombinacja z INT? Toż chodzi o to, że to właśnie ICP ustawiasz aby reagowało na początku na zbocze opadające i zostanie ładnie wygenerowane przerwanie TIMER1_CAPT_vect.

    mirekk36, ale ja mam dwa sygnały:
    1. wyzwalający (w stosunku do niego następuje pomiar czasu, dlatego zeruję licznik)
    2. drugi, któremu mierzę czas opóźnienia w stosunku to tego pierwszego oraz jego długość.

    Jednym przerwaniem tego nie zrobię (chyba).

    No, a docelowo będzie jeszcze trzeci impuls do pomiaru (też opóźnienie w stosunku do 1. oraz jego długość).

    0
  • #11 05 Lis 2009 16:12
    mirekk36
    Poziom 42  

    a no to źle zrozumiałem sorki, ale w takim razie miałeś chyba słuszną koncepcję że w przerwaniu INT zerujesz sobie Timer a w przerwaniu od ICP odczytujesz ICR tyle, że.....

    .... po pierwsze ty chcesz mierzyć czasu rzędu ? kliku mikro-sekund??? tzn jakie bywają te różnice po wyzwoleniu ??? jakie masz założenie

    bo jeśli kilka us albo i mniej - to musisz mieć jakieś duże taktowanie procka co najmniej 16MHz najlepiej

    bo przy 8MHz cykl jednego rozkazu jest dosyć długi więc cała procedura przerwania jednego czy drugiego już może ci "zeżreć" kilka us i wynik będzie "taki sobie" (zajrzyj sobie po kompilacji ile jest rozkazów w tych przerwaniach - przecież odkładanie na stos, zdejmowanie itp (w ogóle niepotrzebnie dajesz to cli w przerwaniu.

    Najlepiej by było wręcz samemu w czystym asm napisać sobie obsługę takiego przerwania INT - gdzie miałbyś tylko kilka rozkazów a m.inn zerowanie Timera1

    a ty jakie masz teraz taktowanie procka? bo jeśli jakieś wolne to po prostu może się kod w przerwaniach nie wyrabia - trza spojrzeć na kod asm i przeliczyć sobie tak na oko nawet czy się wyrabia czy nie

    0
  • #12 05 Lis 2009 19:48
    andrusz
    Poziom 9  

    mirekk36 napisał:
    .... po pierwsze ty chcesz mierzyć czasu rzędu ? kliku mikro-sekund??? tzn jakie bywają te różnice po wyzwoleniu ??? jakie masz założenie

    bo jeśli kilka us albo i mniej - to musisz mieć jakieś duże taktowanie procka co najmniej 16MHz najlepiej

    Tak, mam zegar 16MHz. Timer1 chodzi z prescalerem 8, czyli licznik cyka co 0,5µs. Opóżnienie impulsu 2. to ok. 10-15µs, a sam impuls, który mierzę ma 100-1000µs. Mam nadzieję, że mi się procek wyrabia :-)

    0
  Szukaj w 5mln produktów