Skocz do zawartości

[Bascom] Przerwanie w przerwaniu?


Pomocna odpowiedź

Napisano

Witam.

Mam problem z przerwaniami.

Chcę przerwaniem INT0 uruchomić timer0 a przerwaniem INT1 go zatrzymać.

Czy jest taka możliwość?

$regfile = "m8adef.dat"
$crystal = 8000000

Config Lcd = 16 * 2
Cursor Off
Config Lcdpin = Pin , Db7 = Portc.5 , Db6 = Portc.4 , Db5 = Portc.3 , Db4 = Portc.2 , E = Portc.1 , Rs = Portc.0
Config Timer0 = Timer , Prescale = 1
Config Portd = Input
Set Portd

Dim Czujka As Byte
Dim Y As Bit
Dim X As Bit
Dim 1us As Word

On Int0 Startt
On Int1 Stopt
On Timer0 Czas

Enable Interrupts
Enable Int0
Enable Int1
Timer0 = 7
1us = 0

Cls
Lcd "  Chrono VER.1  "
Lowerline
Lcd "    By Tded     "
Wait 1
Cls


Lcd Czujka
Lowerline
Lcd 1us

Do
If Y = 1 Then
Disable Interrupts

  Cls
  Incr Czujka

  Lcd Czujka
  Lowerline
  Lcd 1us

  Reset Y
  Reset X
  1us = 0

Enable Interrupts
End If

Loop
End


Startt:
If X = 0 Then
  Enable Timer0
  Timer0 = 7
  Set X
End If
Return

Czas:
Timer0 = 7
Incr 1us
Return

Stopt:
If Y = 0 Then
  Disable Timer0
  Set Y
End If
Return

Kompletnie już nie wiem jak to dalej popchnąć do przodu, założenie jest całkiem proste:

2 czujniki podpięte pod INT0/1, za pomocą których chcę zmierzyć czas przejazdu.

Jeden czujnik ma wystartować zegar (bardzo dokładny przepełnienie timer0 co 1us) a drugi ma zatrzymać pomiar.

Z góry dziękuję za pomoc i pozdrawiam.

Mam problem z przerwaniami.

Może napisz jaki.

Chcę przerwaniem INT0 uruchomić timer0 a przerwaniem INT1 go zatrzymać.

Czy jest taka możliwość?

Jasne że jest.

Ale musi być to na przerwaniach?

Bo bez przerwań chyba prościej i dokładniej krótsze czasy można zmierzyć gdyż przerwania powodują odłożenie wszystkich rejestrów na stos a potem ich ściągnięcie co trwa dość długo.

Pozbyłem się przerwań int, dałem kwarc 16MHz, bo na 8 miałem błędne wyniki, chyba przerwania timera0 uciekały?

Może przerwanie timer0 tez da się wyeliminować?

Problemem było to, że zaplanowałeś sobie przerwania co 1µs. Zastanów się tylko chwilę: zegar procesora to 8MHz a wykonanie jednej instrukcji trwa 1 lub 2 takty zegara. To co procesor może zrobić od przerwania do przerwania? Brawo! Nic 🙁 Przecież sama reakcja jądra na zgłoszone przerwanie trwa ze trzy takty a potem dopiero zaczyna się obsługa. Trzeba zapisać rejestry na stos, zmienić kontekst, coś zrobić sensownego, odtworzyć stan procesora i wrócić do przerwanego programu. Jak wyobrażasz sobie wykonanie tego wszystkiego w 8 taktów zegara? W ogólnym przypadku przerwania od timera (i jakiekolwiek inne) możesz zgłaszać najczęściej co kilkadziesiąt mikrosekund a i to zapcha procesor dokumentnie.

Skoro miałeś zegar procesora 8MHz, to:

1. Ustaw prescaler timera na 1/8. Wtedy timer sam będzie liczył mikrosekundy.

2. W obsłudze przerwania od sygnału START:

a. Zatrzymaj timer, wyzeruj go i puść, niech liczy.

3. W obsłudze przerwania od sygnału STOP zatrzymaj timer i odczytaj z niego wynik wprost w mikrosekundach.

No dobrze, zaraz zapytasz, a co jeśli czas będzie dłuższy niż 255µs (albo 65535µs dla timera 16-bitowego)? To proste, wtedy zasadź się również na przerwanie od przepełnienia tego timera i w obsłudze zwiększaj jakąś zmienną statyczną int o 1. Dostaniesz jakby przedłużenie timera o 16-bitów. Wtedy po zatrzymaniu liczenia składasz timer i ten programowy licznik w jedną długą liczbę, np 32-bitową pokazującą czas w mikrosekundach 🙂

Możesz też zrobić troszkę inaczej:

1. Ustaw timer do pracy w trybie CTC z okresem np. 1ms (nie 1µs!) i zegarem 1MHz.

2. W obsłudze przerwania od sygnału START:

a. Zatrzymaj timer, wyzeruj programowy licznik milisekund, wyzeruj timer i puść, niech liczy.

3. W obsłudze przerwania od timera (będzie przychodziło co 1ms) inkrementuj licznik milisekund.

4. W obsłudze przerwania od sygnału STOP zatrzymaj timer. W liczniku milisekund będzie liczba pełnych milisekund a w timerze pozostałość "po przecinku" czyli liczba mikrosekund 🙂

I na przyszłość trochę rozsądniej planuj obciążenie procesora. AVR jest szybki ale bez przesady, nie jest w stanie obsługiwać zdarzeń co 1µs..

Zmieniłem już swoją koncepcję, teraz wygląda to tak:

$regfile = "m8adef.dat"
$crystal = 16000000

Config Lcd = 16 * 2
Cursor Off
Config Lcdpin = Pin , Db7 = Portc.5 , Db6 = Portc.4 , Db5 = Portc.3 , Db4 = Portc.2 , E = Portc.1 , Rs = Portc.0
Config Timer0 = Timer , Prescale = 1
Config Portd = Input
Config Portb.0 = Output
Portb.0 = 1
Set Portd

Dim Czujka As Byte
Dim Y As Bit , X As Bit
Dim 10us As Long
Dim Mps As Single , Fps As Single , Us As Single
Dim Napfps As String * 10 , Napmps As String * 10

'On Int0 Startt
'On Int1 Stopt
On Timer0 Czas

Enable Interrupts
Enable Int0
Enable Int1
Enable Timer0

Stop Timer0
Load Timer0 , 15
10us = 0
X = 0
Y = 0

Cls
Lcd "   Chrono V.2   "
Lowerline
Lcd "    By Tded     "

If Pind.2 = 0 Then
Locate 1 , 1
Lcd "#"
End If

If Pind.3 = 0 Then
Locate 1 , 16
Lcd "#"
End If

Do



If Pind.2 = 0 And X = 0 Then
  Set X
  Portb.0 = 0
  Start Timer0
End If


If Pind.3 = 0 And Y = 0 And X = 1 Then
  Stop Timer0
  Portb.0 = 1
  Reset X
  Set Y
End If


If Y = 1 Then
  Cls
  Incr Czujka
Us = 10us * 0.0001
  Mps = 1 / Us
  Fps = Mps * 3.2808399
  Napmps = Fusing(mps , "#.##")
 Napfps = Fusing(fps , "#.##")
  Lcd Napfps ; " f/s";
  Locate 1 , 15
  Lcd Czujka
  Lowerline
  Lcd 10us ; " " ; Napmps ; " m/s"

  Reset Y
  10us = 0
End If

Loop
End


Czas:
Incr 10us
Load Timer0 , 15
Return

Wszystko działa.

LEcz dalej będe musiał zwiększyć szybkość sprawdzania, teraz mam dokładność około 3 stóp na sekundę (fps), a chcę zejść do jednej.

Takie dosyć szybkie testy są potrzebne by dość dokładnie wyliczyć szybkość poruszającego się obiektu o średnicy 6mm i prędkości około 120m/s(wartość wyliczana) na odcinku 10cm...

Bzdura, nic do Ciebie nie dotarło. Wybrałeś najgłupszy możliwy sposób pomiaru czasu. No nie, jednak przesadzam, mogłeś jeszcze użyć Delay.

Jak zapewnisz, że odliczane jest dokładnie 10us? Poprzez przeładowywania timera w przerwaniu? Żałosne. Ani czas reakcji procesora na przerwanie nie jest deterministyczny ani też nie wiesz co procesor rzeczywiście robi od momentu zgłoszenia przerwania do przeładowania timera. Gdyby timer miał odliczać 10us to musiałby liczyć w cyklu o długości 160 taktów. To oznacza, że musiałbyś ładować do niego -160 (czyli 0x60) bo przecież liczy do przodu. Ty ładujesz 15 - ok, każdemu wolno tam wpisać cokolwiek, ale niezależnie co tam załadujesz i tak czas będzie odliczany mocno przypadkowo. Tę stałą dobrałeś eksperymentalnie??

Timer ma specjalny tryb CTC - jeśli już uparłeś się na liczenie tak krótkich odcinków czasu przerwaniem - w którym wystarczy raz wpisać do odpowiedniego rejestru okres. Przerwania dostajesz wtedy dokładnie co tyle czasu ile chciałeś.

Zwiększanie częstotliwości sprawdzania to zły pomysł. Jeśli robisz jakieś urządzenie pomiarowe to przestań się wygłupiać z tymi przerwaniami liczącymi czas i poczytaj o timerach. Z pewnością szybko odkryjesz inne, ciekawsze tryby pracy niż "Timer" a gdy doczytasz do końca to będziesz też wiedział do czego służy wejście IC i cały mechanizm sprzętowego pomiaru długości impulsów.

Rozumiem, że jesteś początkujący i masz prawo do błędów, ale bez wyciągania wniosków z porażek daleko nie zajedziesz. Brnąc tą ścieżką dalej będziesz miał coraz większe lagi związane z czasem reakcji na przerwanie a za chwilę powiesz, że trudno, nic się nie da zrobić, mam za wolny procesor. A możesz nim dokładnie mierzyć impulsy z rozdzielczością poniżej 100ns - masz do tego specjalny moduł w timerze - tylko się postaraj.

Timer nie liczy 10us tylko 1us, stąd wartość wpisywana to 15.

Nie uparłem się na przerwania, jak na moje pomiar może być dokonywany nawet za pomocą wróżki, byle by był dokładny, wybrałem przerwania ze względu na to, iż rozumiem je po części i wiem mniej więcej jak działają.

Przerwania używam tylko do zliczania ilości przepełnień, nie wiem na ile jest to dokładne, bo skąd mam wiedzieć takie szczegóły?

Timer jest stratowany zaraz po wykryciu stanu niskiego na pind.2 i sprawdzeniu 2 innych warunków, procesor nic w tym czasie innego nie robi, tak samo czeka na zatrzymanie timera tylko na pind.3 tyle że tu już wchodzą przerwania od timera0

Jak jest inna możliwość startowania i zatrzymywania timera, tak aby startował i stopował na określony stan na określonym pinie to chętnie się dowiem jaki to sposób.

Tryb CTC? w swojej literaturze odnośnie BASCOMA nie było nawet wzmianki, w necie troszę poczytałem, że służy do generowania impulsów o określonym czasie i na tym się kończy.

Co do możliwości uC, wiem jak duże są, wiem, że taki układ jaki ja próbuję zrobić już istnieje, i to w dodatku na taktowaniu 4MHz, i chyba na przerwaniach, bo czujniki podpięte są pod porty INT.

Możliwości tego uC są wielkie, lecz niestety moje już takie nie są.

Ależ oczywiście, że są. Potrzebujesz tylko trochę czasu na ogarnięcie tematu - jak każdy.

Nie wiem z jakich materiałów korzystasz, dla mnie wyrocznią w temacie AVR są dane katalogowe ATMELa, bo w końcu kto jak kto, ale producent zwykle wie najlepiej co zrobił w środku scalaka. Na pewno po polsku też coś jest dostępne i Koledzy jakieś tytuły podrzucą. Spróbuj się przez timery AVR przegryźć właśnie z uwzględnieniem trybów pracy. To bardzo fajny blok i może potężnie wesprzeć procesor w zadaniach związanych nie tylko z pomiarem czasu, ale także w generacji impulsów czy ich pomiarze.

Rzeczywiście - metoda start-stopowa wydaje się najbardziej oczywista i możesz ją zastosować, ale tak jak napisałem wcześniej, żądanie od procesora by obsługiwał przerwania co 1 lub nawet co 10us jest bez sensu.

Wróćmy do tego, czego potrzebujesz. Masz bazę pomiarową 10cm i przez nią przelatuje obiekt z prędkością 120m/s. Jak rozumiem, jedna fotokomórka jest na wejściu a druga na wyjściu bazy. Masz zatem do zmierzenia czas między zboczem np. opadającym na jednym wejściu a zboczem opadającym na drugim wejściu. Przy podanej prędkości będzie to ok. 830us. Porównajmy to z prędkością 8MHz procesora: długość jego cyklu zegara wynosi wtedy 125ns i z takim cyklem może w nim liczyć timer. Do ilu zliczy? Do 6640 - to proste. Przy tej rozdzielczości błąd kwantyzacji będzie wynosił ±125ns a więc jakieś setne procenta pełnej skali pomiaru. Jeżeli puścisz timer przez prescaler 1/8, będzie liczył pełne mikrosekundy i wtedy zliczy do 830 z błędem ±1us - też bardzo fajnie, bo jest to wciąż około promila pomiaru.

Jak taki czas zmierzyć? Najprościej wykorzystać timer na tyle długi, by pomieścił cały wynik pomiaru. Jeśli 16-bitowy timer 1 puścisz z zegarem 8MHz to możesz liczyć czasy do ponad 8ms - powinno wystarczyć zatem nic nie stoi na przeszkodzie, by tak zrobić. Zapomnij o przerwaniu od Timera. Po prostu wyzeruj i startuj go po wykryciu pierwszego sygnału a zatrzymuj po sygnale stop. Błąd jaki powstanie będzie brał się z tego, że nie wiesz ile czasu upłynie od rzeczywistego pojawienia się sygnału z fotokomórki do puszczenia i/lub do zatrzymania timera. Jeżeli się na to godzisz, to najprostszy sposób. Zauważ, że nie ma tu mowy o przerwaniach z jakąś straszną częstotliwością. To timer ma szybko liczyć i do tego jest zrobiony. Ty go tylko puszczasz i zatrzymujesz - być może w przerwaniach od wejść start/stop.

O metodzie z wejściem InputCapture - dającej największą możliwą dokładność - przeczytaj sam. A najpierw spróbuj tego co opisałem. Jeżeli rozrzut wyników będzie mieścił się w granicach założonych błędów (jakie one są?) - może tak zostać.

  • Lubię! 1

No własnie, że też wcześniej o tym nie pomyślałem...

16MHz + timer 16'sto bitowy i mamy naprawdę wspaniałą dokładność(62,5ns +- jakiś tam błąd) taka dokładność mi w 100% wystarcza.

Najprostsze rozwiązanie okazało się wystarczające.

Dzięki wielkie za pomoc, naprawdę nawet nie pomyślałem o odczytywaniu wartości z timera, jeszcze raz dzięki wielkie.

Pozdrawiam 😉

Taką masz rozdzielczość pomiaru - tyle będzie trwał jeden okres zegara timera. To nie jest dokładność. Dokładność będzie dużo gorsza, bo:

1. Sygnał wejściowy - zanim trafi w ogóle do jądra procesora będzie zsynchronizowany z zegarem, czyli: ± 1 zegar.

2. Procesor "nic nie robiąc" i czekając na przerwanie kręci się w pętli programowej wykonując w kółko np. instrukcję skoku do samego siebie. Taka instrukcja (najkrótsza) kosztuje 2 cykle zegara a są takie co i 3. Instrukcja jest niepodzielna więc opóźnienie reakcji jądra AVR na przerwanie może być 1, 2 lub nawet 3 zegary.

3. Potem wszystko jest deterministyczne w czasie aż do startu timera.

4. To samo co w pkt.1 i 2 będzie się działo przy wyłączaniu timera.

Tak więc moim zdaniem nie możesz oczekiwać dokładności lepszej niż 4 takty zegara - taki będzie losowy rozrzut wyników nawet gdybyś do wejść doprowadzał wielokrotnie te same impulsy wzorcowe np. z jakiegoś superstabilnego generatora. Oczywiście wszystko znacznie się pogorszy gdy tylko zaczniesz obsługiwać jakieś inne przerwania - np. od UARTa, klawiatury itp.

Naprawdę polecam temat wykorzystania wejścia InputCapture - ono właśnie do pomiarów czasu jest.

Acha, i warto jednak zasadzić się na przerwanie od przepełnienia timera - przynajmniej będziesz mógł ustawić jakąś flagę sygnalizacji przekroczenia maksymalnego czasu pomiaru, zatrzymać wszystko i napisać coś lub zapiszczeć.

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...