Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

ATTINY13 - Obsługa przerwania od Timer0 (BASCOM)

greg_matrix 27 Feb 2006 20:25 4905 6
  • #1
    greg_matrix
    Level 16  
    Drodzy koledzy,
    Programując uC ATTINY13 natknąłem się na pewien problem związany z obsługą przerwania od czasomierza. Poniżej krótko co program mniej więcej ma robić.

    Do jednego z wejść mikrokontrolera doprowadzony jest sygnał. Pojawiają się tam impulsy o różnej długości i na dodatek w różnych odstepach czasu. Zadaniem programu ma być wygenerowanie krótkiego impulsu o konkretnie zadanej długości w momencie zakończenia impulsu na wejściu. Program wygląda nastepująco:

    $regfile = "attiny13.dat"
    $crystal = 6400000

    Config Pinb.2 = Input
    Reset Portb.2
    We Alias Pinb.2

    Config Pinb.0 = Output
    Wy Alias Portb.0

    Dim Pomocn As Bit At &H8C

    Config Timer0 = Timer , Prescale = 64



    On Timer0 Koniec_impulsu
    Enable Interrupts
    Enable Timer0


    Do

    Pomocn = 1

    'Czekam na impuls wejściowy:
    Do
    If We = 1 Then
    If We = 0 Then
    Exit Do
    End If
    End If
    Loop

    'Impuls na we skonczony, rozpoczynam impuls na wy
    Start Timer0
    Set Wy
    Load Timer0 = 57

    'Impuls trwa, ta petla wykonuje sie az zmienna "Pomocn" zostanie wyzerowana,
    'a to ma zostac zrobione w podprogramie obslugi przerwania Timer0

    Do
    If Pomocn = 0 Then
    Exit Do
    End If
    Loop

    Loop
    End

    Koniec_impulsu:
    Reset Wy
    Pomocn =0
    Counter0 = 1
    Stop Timer0
    Return


    Domyślam się, że problem może tkwić w nastepującej sprawie- obsługa przerwania odbywa się w taki sposób, żeby po powrocie z niego stan mikrokontrolera był taki sam. I tutaj jest problem, bo u mnie zmienna "Pomocn" miałaby zostać zmieniona w podprogramie obsługi przerwania, co powinno być widoczne w programie głównym po powrocie z obsługi... Nie jestem pewnien czy dobrze myslę i czy tak jest rzeczywiście. Jeśli tak, to jak mozna z tej sytuacji wybrnąć??

    Z góry dziekuje za jakiekolwiek rady!
    Kamery 3D Time of Flight - zastosowania w przemyśle. Darmowe szkolenie 16.12.2021r. g. 10.00 Zarejestruj się
  • #2
    M. S.
    Level 34  
    Zmienna pomocnicza zmodyfikowana w programie obsługi przerwania powinna być widoczna w całym programie.

    Nie bawiłem się nigdy Tiny 13, więc moje spostrzeżenia mogą być mylne lecz uważam, że kolega zapomniał o istnieniu przerwań zewnętrznych INT. Gdybym pisał ten program to podałbym impulsy wejściowe na wejście INT uC. Przerwanie zenętrzne ustawiłbym (za pomocą Config) jako wyzwalane zboczem opadającym (lub rosnącym wg potrzeb) stanowiącym zakończenie impulsu wejściowego. W wyniku obsługi przerwania wykonałbym jakąś pętlę odmierzającą czas do wygenerowania impulsu wyjściowego, lub uruchomiłbym któryś z timerów (tu trzeba pamiętać o włączeniu przerwań po rozpoczęciu obsługi aby dać możliwość reakcji na przerwanie od timera).

    A tak dla porządku to kolega ładuje wartość zliczaną przez timer po jego uruchomieniu.

    Code:
    Start Timer0
    
    Set Wy
    Load Timer0 = 57


    Chyba należało by to zrobić przed instrukcją uruchamiajacą timer. Ponadto należy pamiętać, że instrukcja Load Timer = x powoduje przerwanie po x impulsach. Timer ładowany jest pojemnością timera - x.
  • #3
    guteczek
    Level 11  
    a nie lepiej byłoby po wykryciu końca impulsu użyć tajmera jako licznika i nie korzystać z przerwania, zresetować Wy i po osiągnięciu zadanej liczby którą ma odliczyć ustawić ponownie w stan wysoki wyjście?
  • #4
    greg_matrix
    Level 16  
    Mój pierwszy pomysł był właśnie taki, żeby zwyczajnie zmienić stan na wyjściu, odczekać i zmienić go ponownie. W takim przypadku nie potrzeba przerwań, nie ma też potrzeby zatrzymywania zegara. Fragment kodu generujący impuls o długośi 0,58 ms:

    Set Wy
    Counter0 = 0

    Do
    Licznik = Counter0
    If Licznik > 57 Then
    Reset Wy
    Exit Do
    End If
    Loop


    Nie chce to u mnie działać, dlatego zacząłem kombinować z przerwaniami... Efekt jest taki, że stan na wyjściu pozostaje wysoki. Domyślam się, że program NIE WYSKAKUJE z pętli.

    Co do wcześniejszych uwag
    1. odnośnie zmiennej pomocniczej- zmienne deklarowane przez Dim są globalne, czyli widoczne w całym programie. Innym problemem jest to, czy zmienną używaną w programie głównym można zmienić z podprogramu obsługi przerwania. W książkach piszą, że po wyjściu z podprogramu obsł przerw stan uC ma być IDENTYCZNY jak przed obsługa przerw. Tak więc sobie pomyślałem, że być może robienie czegokolwiek ze zmiennymi jest bezcelowe, skoro ich wartości i tak są przywracane przed powrotem do programu głównego?
    2. Zatrzymywanie zegara było mi potrzebne dlatego, że impulsy na wejściu są w odstępach czasu takich, że zostałoby wygenerowanych kilkanaście przerwań pomiędzy nimi. Co do ustawiania licznika po jego zatrzymaniu, to przyznam się, że zrobiłem to nieświadomie- być może tak nie mozna, dzięki za zwrócenie uwagi! Sprawdzę to.

    Przy okazji nasuwa mi się pewne pytanie- czy częstotliwość wewnętrznego generatora jest całkiem dowolna?? Wymyśliłem akurat 6400000 Hz, bo przy podziale zegara przez 64 daje to wygodną jednostkę. Czy ktoś coś wie na ten temat??
  • Helpful post
    #5
    szymtro
    Level 30  
    a nie mozesz użyć goto zamiast do loop?

    np takie pętle:
    Code:
    petla_oczekiwanie_:
    
    ....
    if warunek then goto petla_program_1_
    ...
    goto petla_oczekiwanie_

    petla_program_1_:
    ....
    if warunek then goto petla_oczekiwanie_
    ....
    goto petla_program_1_

    Coś takiego powinno zadziałać.

    P.S.
    Ten stan poprzedni oznacza tylko wartości głównych rejestrów a zmienne w ram nigdzie nie sa zachowywane. Chodzi tu o sytuacje kiedy przychodzi przerwanie a programista w tym czasie przesuwa jakieś dane w R16 - i teraz chodzi o to że gdybyś chciał wykorzystać R16 do czegos w przerwaniu to bez zachowania po zakończeniu obsługi przerwania byłyby inne dane i program "by sie powiesił" w najlepszym wypadku.

    P.S.2
    Jeszcze tak sobie pomyslałem - definiujesz zmienną pod konkretnym adresem - dlaczego? nie mozesz "pozwolić" kompilatorowi aby sam ja gdzieś umieścił?
  • #6
    greg_matrix
    Level 16  
    Problem rozwiązany! :-)

    Dziękuję wszystkim, którzy wzięli udział w dyskusji- zaszczepiła mnie ona twórczo. Próba użycia zegara "na wprost", to znaczy sprawdzania stanu licznika z niewiadomych przyczyn w dalszym ciągu u mnie nie działa. Wygląda to tak, jakby nie było dostępu do stanu licznika ani przez zmienną COUNTER0 ani przez TIMER0 (wg instrukcji BASCOM-a te zmienne właśnie do tego powinny służyć...) Obmyśliłem za to strategię zastępczą. Z uzyciem przerwania tworzę zmienną, która reprezentuje czas i używam jej już tak samo jak wspomnianych zmiennych systemowych :-) Dodatkowa korzyść jest taka, że ta zmienna moż być typu "Word", a nie "Byte" jak w liczniku Timer0, więc nawet lepiej.

    Code:
    $CRYSTAL=4800000
    
    Config Timer0 = Timer , Prescale = 1
    Dim Czas As Word

    On Timer0 Przerwanie
    Enable Interrupts
    Enable Timer0

    /......... program ....../

    Przerwanie:
    Load Timer0, 48
    Czas = Czas + 1
    Return

    W programie głównym użycie zmiennej "Czas" działą już bez zarzutu :-)
    Code:
    Do
    
    /.... czekanie na koniec impulsu na WE .../
    Set wy
    Czas=0
    Do
    If Czas>57 Then
    Exit Do
    End If
    Loop
    Reset Wy
    Loop
    End

    Z dwoma tylko zastrzeżeniami:
    1. Nie wiem jak inne mikrokontrolery, ale AtTiny13 NIE MOŻE być taktowany dowolną częstotliwością. Nie wiem dlaczego, ale dyrektywa "$CRYSTAL" tak naprawdę nie wpływa na częstotliwość zegara wewnętrznego- jego szybkość trzeba ustawić FUSE BITAMI. Do wyboru są 4,8 i 9,6 MHz (i jeszcze jakaś w KHz), więc wyboru nie ma za wiele...
    2. Trzeba wyłączyć "Clock Devide By 8" w fusach albo wziąć ten podział częstotliwości pod uwagę. Ja wyłączyłem, dlatego ładuję do licznika wartość 48.

    Pozdrawiam!
  • #7
    guteczek
    Level 11  
    Jedna uwaga.
    Do zwiększania zmiennej czas użyj polecenia incr czas, zawsze tak rób gdy chcesz zwiększać zmienną o 1 bo to polecenie działa szybciej niż czas=czas+1
    Jeśli czas generowania impulsu na wyjściu nie jest krytyczny mógłbyś po wykryciu zakończenia impulsu wejściowego wykonać polecenie
    set port..x waitus,czas i reset port..x
    po waitus podajesz czas(1-65535)w mikrosekundach,zadziała tak samo.
    Najlepiej byłoby gdyby przerwanie było wyzwalane z pinu procka do tego celu przeznaczonego , tak byłoby najbardziej profesjonalnie ale myślę że w tym przypadku wystarczyłoby to z poleceniem waitus
pcbway logo