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.

Pytanie o pomiar czasu w ATMEGA8 - Dokładność

emilax 09 Mar 2008 13:48 5503 9
  • #1 09 Mar 2008 13:48
    emilax
    Poziom 10  

    Witam wszystkich majsterkowiczów :D

    Oto mój jeden z pierwszy programów napisanych na ATMEGE 8. Dotychczas programowanie mikrokontrolerów traktowałem jako zabawę, używając do tego BASCOMA i zestawu Treningowego. Obecnie stoję przed zamiarem budowy pewnego urządzenia ale przed rozpoczęciem prac chciałbym mieć pewność że część programowa będzie ok.
    Sam nie jestem żadnym specjalistą od programowania mam parę napisanych programów w Pascalu, c++ czy Delphi ale raczej wszystkie prace są okupione ogromną ilością godzin spędzonych przy komputerze i wyrwanych włosów oczywiście….

    Ale do rzeczy, urządzenie ma za zadanie zmierzy czas reakcji dwóch strzelców mierzących do celu. Procedura wygląda następująco:

    1 - Urządzenie w stanie gotowości.
    2 – Naciskamy przycisk START. Od naciśnięcie przycisku start następuje losowe załączenie w ciągu 8 sekund lampy bądź sygnalizatora dźwiękowego dającego strzelcom sygnał do oddania strzału. Od momentu uruchomienia sygnalizatora następuje odmierzanie T czasu.
    3 – Zawodnicy oddają strzał aktywując przy tym sensory u celu, które automatycznie określają przedział czasu T1 i T2. (T1w = T1-T oraz T2w = T2-T)
    4 – Procesor wylicza czasy reakcji i wyświetla czas zwycięzcy
    5 – Po naciśnięciu przycisku RESET wszystko wraca do punktu nr 1.

    Program po wgraniu do procesora i emulacji na przyciskach działa aczkolwiek w mojej głowie rodzą się pewne pytania i wątpliwości oto i one:

    1. Używając kwarcu 8MHz i przy wybraniu preskalera 8 przy załadowaniu do licznika TIMER0 wartości 156, przerwanie zostaje wywołane co 0,1 ms. Moje pojęcie wywołania tego przerwania jest następujące - (jeżeli jest mylne prosiłbym o wyprowadzenie z błędu), Zmienna Ti w ciągu 0,1 ms zwiększa swoją wartość TYLKO o jeden.

    Code:
    Pomiar:            ‘ Przerwanie
    

    Timer0 = 156                                               
    If Licz = 1 Then
                      Incr Ti
    End If
    Return


    2. Czy ATMEGA 8 jest na tyle szybka aby dokonać pomiaru takiego czasu rzędu 0,1 ms w istniejącym programie, czy też byłaby potrzebna jakaś wstawka asemblerowa. Czytałem w różnych poprzednich postach że przy precyzyjnych pomiarach czasu najlepszy jest asembler ponieważ można policzy czasy procedur co do jednego cyklu zegara. Nie zamierzam tu wywoływać zażartej polemiki pomiędzy zwolennikami prostych języków programowania a programistami używającymi asemblera, bo wskazania logiki mówią o tym że asembler pod tym względem jest dużo lepszy, niestety nie każdy miał z nim do czynienia i nie każdy go rozumie.




    Jednak wracając do problemu, czy procedury bascom’owe w tym przypadku nie będą spowalniały pomiaru zegara.
    Czy istniałby sens zmiany preskalera na 1, automatycznie zmiany czasu wywołania przerwania a tym samym uprecyzyjnienia pomiaru, czy ma to sens dla tego procesora, czy poradziłby on sobie z takim zliczaniem przy użyciu istniejącego kodu. Czy też musiałbym urzyc czegoś szybszego.

    W przypadku niejasności co do kodu proszę pytać, jestem ciekaw ile popełniłem błędów a co do optymalizacji wiem że każdy ma swoje drogi i rozwiązania. Ja na razie oparłem to na części czysto teoretycznej aczkolwiek praktyka pokazała że program po wgraniu do procesora działa, tylko zastanawia mnie na ile realne jest zliczenie tego czasu. Nie wiem też za bardzo jakie w rzeczywistości mogą by różnice pomiędzy dwoma osobnikami oddającymi strzał, tak że z góry zakładam najmniejszy przedział czasu dostępny w mojej testowej konstrukcji ATMEGA 8 .

    Oto listing programu

    Code:
    $crystal = 8000000
    

    Config Lcd = 16 * 2
    Config Lcdpin = Pin , Db4 = Portd.5 , Db5 = Portd.6 , Db6 = Portd.7 , Db7 = Portd.4 , E = Portd.3 , Rs = Portd.2

    Config Portb = &B11000110 : Portb = &B1111001
    Config Portc = &B11111110 : Portc = &B00000001
    Config Portd = &B11111111 : Portd = &B00000000

    Config Timer0 = Timer , Prescale = 8 :

    Dim A1 As Byte
    Dim A2 As Byte
    Dim Ti As Long
    Dim T1 As Long
    Dim T2 As Long
    Dim Tl As Byte
    Dim T1r As Single
    Dim T2r As Single
    Dim Licz As Bit
    Dim C As Bit

    Declare Sub Czysc
    Declare Sub Show
    Declare Sub Losuj

    A1 = 0 : A2 = 0 : Licz = 0 : C = 0 : Ti = 0 : T1 = 0 : T2 = 0

    Cls
    Lcd " Hey Guys !!!"
    Lowerline : Lcd "  Lets Go !!!" : Cursor Off


    '---------------------------------main program-----------------------------------------

    On Timer0 Pomiar

    Do
       If Pinc.0 = 0 Then                                       'Pushbutton RESET
                     Czysc                                     
                     End If
       If Pinb.5 = 0 And C = 0 Then      'Pushbutton START
                               C = 1
                               Losuj
                               Enable Interrupts : Enable Timer0
                               Licz = 1
                               End If

    If A1 = 1 And A2 = 1 Then
                            Show
                            End If
    Loop
    End                                                         'Glowna petla programu

    '----------------------------------przerwanie------------------------------------------
    Pomiar:

    Timer0 = 156                                                '


    If Pinb.3 = 0 And A1 = 0 Then                               'Uruchamia strzelec nr 1
                            A1 = 1 : T1 = Ti                    'in 0-zwarte do masy
                            End If

    If Pinb.4 = 0 And A2 = 0 Then                               'Uruchamia strzelec nr 2
                            A2 = 1 : T2 = Ti
                            End If

    If Licz = 1 Then
                Incr Ti
                End If
    Return

    '-----------------------procedura pokazania wyniku------------------------------
    Sub Show

    Cls

    Disable Timer0
    T1r = T1 / 10000
    T2r = T2 / 10000

    Lcd "T1=" ; T1r ; " S"
    Lowerline
    Lcd "T2=" ; T2r ; " S"

    If T1 > T2 Then                                             'wygrywa nr 2
               Portb.2 = 1
               End If
    If T1 < T2 Then                                             'wygrywa nr 1
               Portb.1 = 1
               End If
    A1 = 0 : A2 = 0


    Cursor Off
    End Sub

    '-----------------------procedura zerowania i kasowania ekranu------------------

    Sub Czysc
                   Cls
                   Lcd " Hey Guys !!!"
                   Lowerline : Lcd "  Lets Go !!!" : Cursor Off
                   Portb.1 = 0 : Portb.2 = 0
                   Ti = 0 : T1 = 0 : T2 = 0 : A1 = 0 : A2 = 0 : Licz = 0 : C = 0
                   Portd.0 = 0                                  'Wylaczenie sygnalizatora




    End Sub

    '-----------------------procedura losowania czasu do startu------------------

    Sub Losuj
                   Cls
                   Lcd "....Counting...."
                   Tl = Rnd(8) : Incr Tl
                   Wait Tl
                   Portd.0 = 1              'Sygnalizator, Lampa, syrena - cokolwiek

    End Sub


    Z góry dziękuję za wszystkie sugestie i porady. Pozdrawiam wszystkich użytkowników portalu.

    :D:D:D

    0 9
  • #2 09 Mar 2008 14:18
    nsvinc
    Poziom 35  

    jesli juz chcesz to pisać w jakims jezyku wysokiego poziomu to polecam C...w bascomie masz zero kontroli nad rzeczywistym wykonywaniem komend i czasem ich trwania. Zeby zmierzyc czas z dokladnoscia 0.1s nie potrzebujesz asemblera do tego, wystarczy zastosowac timer...

    0
  • #3 09 Mar 2008 15:12
    asembler
    Poziom 32  

    No tak asembler ci to mówi:)
    Nawet z wieksza dokładnością 0.000 001s

    0
  • #4 09 Mar 2008 15:23
    nsvinc
    Poziom 35  

    Ale chyba az tyle to nie potrzeba :D Teoretycznie bascom tez potrafi stworzyc w miare normalny kod o ile sie nie namota warunkami...Twoj kod mozna napisać prościej :) Twój timer buja z prędkością 1MHz, po co, skoro wymagana dokladnosc wynosi 0.1s?

    0
  • #5 15 Mar 2008 20:40
    emilax
    Poziom 10  

    Dziękuję za zainteresowanie tematem !!! Chwilowo wystąpił u mnie brak czasu i przystopowanie projektu ale mam nadzieję że w tym tygodniu będzie już lepiej.

    Na początku kilka przemyśleń „do użytkownika nsvinc” odnośnie C+ czy asemblera wiem że jest on dużo dokładniejszy i nie mam co do tego wątpliwości jeżeli chodzi o zagadnienia związane z czasem. Jeszcze raz zaznaczam że nie zamierzam wywoływać dyskusji co jest lepsze (rower czy samochód) ….. Ja na obecny dzień dysponuje tylko umiejętnością pisania w Bascomie i to mi musi wystarczyć.

    Niestety moja wiedza na temat praktycznego zastosowania Bascoma w realizacji mikroprocesorowych projektów nie jest zbyt obszerna, dlatego też nękają mnie pewne pytania wynikające z różnic pomiędzy zastosowaniem symulatora a realnym procesorem.

    Q1. Czy powyższy kod będzie w stanie realnie odmierzyć czas z dokładnością co do 0,1 milisekundy (nie 0,1 sekundy), czy ustawienie preskalera na 8 (timer0 1Mhz) i warunki w pętli przerwania nie opóźnią zbytnio mierzonego czasu lub też go nie zafałszują.

    Q2. Jak się sprawa będzie miała jeżeli ustawię preskaler na 1 (timer0 8Mhz) i zwiększę szybkość wywołania przerwania. Czy to Bedze miało sens ????
    Rozumiem że warunki i pętlę w procedurze przerwania tłumaczone na ilość cykli maszynowych (taktów zegara) opóźniają realne odmierzanie czasu.

    Q3. Jak ewentualnie można uprościć kod samego przerwania ???

    Q4. Jaka jest najmniejsza jednostka czasu możliwa do zmierzenia Atmegą8 przy użyciu Bascoma i na ile taki pomiar jest wiarygodny ??? Nie pytam teraz o procedury pisane w C lub asemblerze.

    Q5. Czy ewentualnie procedura odmierzenia czasu w asemblerze albo innym precyzyjniejszym kompilatorze dla tego przypadku byłaby bardzo skomplikowana ???

    Q6. Czy ktoś byłby w stanie wyliczyć błąd pomiaru na podstawie istniejącego kodu. Ewentualnie zasugerować co zmienić aby ten błąd był jak najmniejszy.

    Dzięki za wszelkie sugestie i pozdrawiam.:idea:

    0
  • #6 15 Mar 2008 21:18
    asembler
    Poziom 32  

    Mnie sie wydaje ze dokladnosc pomiaru nie zalezy od uzytego jezyka.
    W przypadku 1MHz bedzie to 0.001mS oczywiscie to jest rozdzielczosc (1 mln probek na sek)bo co do dokladnosci to trzba uwzglednic czy ten 1MHZ to rzeczywiscie 1MHz

    0
  • #7 15 Mar 2008 21:40
    nsvinc
    Poziom 35  

    Co do bascoma...jako ze generuje wolny kod to nie możesz wepchnąć rozbudowanej obsługi licznika w przerwanie, bo pogubisz takty albo wykopyrtniesz procesor bo sie stos przepełni(w przypadku gdybys w obsłudze przerwań nie wyłączał przerwań, a w trakcie trwania jego obsługi wystąpiło następne przerwanie i tak w kółko)
    Musisz wziąć pod uwagę czas reakcji procesora.
    W twoim przypadku NA PEWNO NIE możesz użyc przerwan SYNCHRONICZNYCH...
    1.Musisz zastosować mege64 albo 128, w ktorej pierwsze 4 zewn przerwania są asynchroniczne...
    2.Nie pchaj kodu w suby tu gdzie nie musisz, sub=skok a skok trwa....
    3.PRZERWANIE CO 1uS OD TIMERA W MEGAxx i BASCOM= ZAPOMNIJ...

    Lepsza technika:
    W momencie "start" odpalasz timer, i to nie 8bitowy tylko 16, preskaler na /64, jesli masz kwarc 8mhz, to licznik buja z f=125kHz, więc zwieksza sie co 0.000008s, więc to ci powinno wystarczyć...Przerwanie przepełnienia licznika bedzie wywalać co 0.524288s

    Jak wystąpi przerwanie zewn. z jakiegos INTa, to tylko przechwytujesz zawartość tego timera .

    W samej obsludze przerwania od timera...

    Code:

    timer1_isr:
    incr licznik_przepelnien
    return


    Przerwanie INT0, przyklad
    Code:

    ilosc_taktow=timer1
    temp=licznik_przepelnien*65536
    ilosc_taktow=ilosc_taktow+temp


    Oczywiscie musisz to rozszerzyć o warunek ktory sprawdza czy oba przerwania juz zostaly wyzwolone, np na flagach jak w twoim przykladzie

    Ale jesli juz posiadasz gotowe zmienne zawierajace ilosc zliczonych taktów od "start" do przerwania, to
    moj_czas=ilosc_taktow*0.000008
    Czas dostaniesz w mikrosekundach :)

    Nie wiem czy dobrze opisałem, moge tylko domniemać bo sie nie bawiłem w mierzenie superkrótkich odstępów czasu :D

    pozdrawiam

    Dodano po 4 [minuty]:

    asembler: dokladnosc NIE zalezy od uzytego języka, ale skutecznosc wykonywania kodu juz tak.
    Napisalem to kierujac sie wyzej wspomnianym powolnym kodem z bascoma w przerwaniu, juz sie pare razy naciołem jak pisalem cos w bascomie, a po przejsciu na C problem prawieże zniknął...

    0
  • #8 15 Mar 2008 21:45
    asembler
    Poziom 32  

    Po co utrudniac sobie życie jeżeli jest w Atmedze taka nóżka (ICP), która zrobi to sama wszystko i nie pogubi ani jednego taktu procesora.

    0
  • #9 15 Mar 2008 21:55
    nsvinc
    Poziom 35  

    ano racja, tez chcialem o tym wspomnieć a zapomnialem jak sie ten pin nazywa :D:D a datasheet nie chcial mi sie otworzyc bo komp jest w agonii :D

    Ale autor tematu musi przechwycić DWIE wartośći więc bedzie musiał zastosować dodatkowy uklad logiczny przed tym pinem...Chybaże sprzężyć jakoś pin ICP z INTami....:D

    0
  • #10 15 Mar 2008 22:10
    asembler
    Poziom 32  

    Jest powiedziane ze wylicza czasy reakcji i wyswietla czas zwyciezcy
    Dla mnie mozna przyjąc ze czas przegranego jest juz z góry wiadomy bo i tak nie ujrzy światła dziennego. Inaczej mówiac mierzymy tylko czas zwycięzscy oraz musimy wskazac kto zwycięzył.

    Dodano po 3 [minuty]:

    Poza tym pierwsze wywolanie przerwania od ICP to pomiar czasu teraz mozna badac dowolnie długo od którego pinu jest to przerwanie bo zakladam ze kazdy zawodnik bedzi emial swoj pin. Rozwijajac temat mozna w ten sposób rozróznic nie tylko dwóch zawodników.

    0