Elektroda.pl
Elektroda.pl
X
Elektroda.pl
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania

p.kaczmarek2 02 Kwi 2020 01:20 948 0
  • Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Witajcie moi drodzy
    W tym temacie poznamy podstawy przerwań i timerów w PIC18F2550. Na początek będzie krótki wstęp teoretyczny, a potem wszystko przerobimy dokładnie na praktycznych przykładach.
    Do lektury tematu przyda się ogólna wiedza na temat przerwań i programowania w C, choćby taka z Arduino.
    Temat oczywiście nie zastąpi w pełni czytania noty katalogowej PIC18F2550 gdzie wszystko jest dokładnie opisane, ale na pewno zapewni kilka dobrych przykładów co i jak użyć w praktyce, wraz z pełnym kodem, wytłumaczeniem i prezentacją działania.
    Wszystkie przykłady będą napisane w języku C pod darmowy kompilator SDCC, do każdego przykładu zamieszczę pełny kod i plik projektu do pobrania.

    Spis części (osobnych tematów) tutoriala
    Tutorial podzielony jest na osobne tematy i tutaj znajdują się do nich linki.
    Część 1 - Konfiguracja środowiska pracy
    https://www.elektroda.pl/rtvforum/viewtopic.php?p=18304424#18304424
    Część 2 - Hello world, piny IO, cyfrowe wejścia i wyjścia
    https://www.elektroda.pl/rtvforum/viewtopic.php?t=3647884&highlight=
    Część 3 - Ustawienia oscylatora. Oscylator wewnętrzny, zewnętrzny, rezonator kwarcowy, PLL
    https://www.elektroda.pl/rtvforum/viewtopic.php?t=3657704&highlight=
    Część 4 - Timery, przerwania
    https://www.elektroda.pl/rtvforum/viewtopic.php?p=18580858#18580858
    Część 5 - Obsługa wyświetlacza siedmiosegmentowego
    https://www.elektroda.pl/rtvforum/topic3676650.html
    Spis treści będzie uzupełniany wraz z pisaniem przeze mnie kolejnych części.

    Ten temat to jest część 4, czyli Timery, przerwania. Zaczynamy.

    Czym jest Timer?
    W wielkim skrócie: timery sprzętowe to gotowe mechanizmy w mikrokontrolerze do zliczania. Zliczać mogą czas (poprzez zliczanie cyklów z oscylatora) lub np. impulsy (na wybranym pinie).
    Timery mogą służyć do wykonywania danej akcji co ustalony czas.
    Timerowi ustawia się ile ma zliczyć cykli, i po zliczeniu wszystkich następuje przepełnienie i Timer wywołuje przerwanie.
    Oczywiście timera można używać też bez przerwania - po prostu samodzielnie odczytywać z jego rejestrów ile zliczył.
    Okres timera konfigurujemy przede wszystkim poprzez dwa ustawienia:
    - ustawienie preskalera timera (dzielnika zegara wejściowego), by z grubsza określić jego częstotliwość działania
    - ustawienie licznika przepełnienia timera, by dokładnie określić kiedy timer wywoła przerwanie
    Szczegóły konfiguracji zależą już od konkretnego Timera. Timery mogą oferować różne ustawienia preskalera, źródła zegara, itp.

    Czym jest przerwanie?
    Przerwanie jest to mechanizm pozwalający tymczasowo przerwać wykonywanie głównego programu, obsłużyć jakieś zdarzenie (ang. event), i wrócić z powrotem do wykonywania głównego kodu.
    Od strony assemblera przerwanie wygląda tak, że w momencie pojawienia się zewnętrznego zdarzenia bieżący stan wykonywania kodu jest zapisywany, potem dokonywany jest skok w wyznaczone miejsce w programie, wykonywane są instrukcje stamtąd, a potem następuje powrót.
    Na szczęście my programując w C nie musimy o to zadbać - więc obsługa przerwań dla nas będzie naprawdę bardzo łatwa.
    Przerwania mogą mieć różne źródła, przykładowo:
    - przerwanie wewnętrzne, np. od Timera - wywoływane co co jakiś czas, np. co 100ms
    - przerwanie zewnętrzne - wywoływane np. gdy wykryje się narastające zbocze na określonym pinie
    Poniżej dokładnie przyjrzymy się przerwaniom i timerom w PIC18F2550.

    Timery w PIC18F2550
    PIC18F2550 oferuje aż 4 timery:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Mamy w nim kolejno:
    - Timer0 - wewnętrzny timer/zewnętrzny licznik impulsów, 16 bitowy
    - Timer1 - wewnętrzny timer/zewnętrzny licznik impulsów, 16 bitowy
    - Timer2 - wewnętrzny timer, 8 bitowy
    - Timer3 - wewnętrzny timer/zewnętrzny licznik impulsów, 16 bitowy
    Każdy z nich może wywoływać przerwanie w momencie swojego przepełnienia. Trzy z nich (mogą być) 16 bitowe, czyli mogą zliczać 2^16 (65536) impulsów. Dwa z nich obsługują zliczanie impulsów z innego źródła niż zegara wewnętrznego (te źródła są na określonych pinach). Timery te obsługują też ustawienie preskalera (osobno dla każdego timera), co pozwala zliczać np. co czwarty impuls z danego źródła.

    Przerwania w PIC18F2550
    PIC18F2550 obsługuje przerwania z różnych źródeł. Należą do nich m. in:
    - przerwania od Timerów (przepełnienie się licznika Timera 0, 1, 2, 3)
    - przerwania zewnętrzne (na pinach INT0, INT1, INT2, np. wciśnięcie przycisku)
    - przerwanie od UART (wysłanie lub odbiór znaku)
    - przerwania od ADC (gdy ADC zakończy konwersje)
    - przerwanie od USB (moduł sprzętowego USB)
    - przerwania CCP (Compare/Capture/PWM)
    Rejestry związane z przerwania w PIC18F nazywane są wedle określonego nazewnictwa, a mianowicie:
    - bit xxxIE, tzw. 'interrupt enable', jeśli ustawimy go na 1 to dane przerwanie zostaje włączone. Przykładowo dla Timera 0 jest to TMR0IE z rejestru INTCON
    - bit xxxIF, tzw. 'interrupt flag', jest on automatycznie ustawiany na 1 jeśli dane zdarzenie miało miejsce, musi zostać ustawiony na 0 w programie w funkcji obsługującej przerwanie. Przykładowo dla Timera 0 jest to TMR0IF z rejestru INTCON.
    Ponadto mamy kilka globalnych ustawień które kontrolują przerwania:
    - bit GIE, tzw. 'global interrupt enable', który powinien być ustawiony na 1 jeśli chcemy by przerwania się wykonywały. Znajduje się on w rejestrze INTCON
    - bit IPEN, tzw. 'interrupt priorities enable', jeśli jest ustawiony na 1 to pozwala na obsługę dwóch priorytetów przerwań (niskiego i wysokiego)
    - bit PEIE, tzw. 'peripheral interrupt enable', jest to osobny bit który pozwala włączyć przerwania zewnętrzne
    Oprócz tego potrzebna jest też oczywiście funkcja która obsłuży przerwanie. Funkcją tą w PIC18F nazywamy ISR, czyli 'interrupt service routine'. Funkcję tą oznacza się nieco inaczej w zależności od kompilatora, ale w SDCC służy do tego słowo kluczowe __interrupt. Na przykład:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Sama nazwa funkcji tutaj nie ma znaczenia. Liczba 1 oznacza tutaj numer przerwania.
    To w tej funkcji musimy obsłużyć poprawnie flagę xxxIF (wyłapać ją by rozpoznać jakie przerwanie nastąpiło i ustawić na 0):
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Na przykład, dla Timera 0:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Poniżej przedstawię działanie przerwań w PIC18F2550 i SDCC na praktycznych przykładach.
    Zainteresowanych dokładniejszymi informacji odysłam do noty katalogowej PICa:

    Kalkulator Timerów dla PIC
    Timerów dla PIC18F2550 nie trzeba liczyć ręcznie - jest do tego dostępne darmowe, niezależne od użytego IDE/kompilatora narzędzie.
    Nazywa się ono Timer Calculator i pochodzi od Mikroelektroniki.
    Wersja dla Windowsa jest do pobrania stąd:
    https://www.mikroe.com/timer-calculator
    A właściwie to stąd, bo na czas pisania tego tematu powyższa strona ma przekierowanie na LibStock:
    https://libstock.mikroe.com/projects/view/398/timer-calculator
    Kopia zapasowa wersji 4.0 z linku powyżej (na wypadek gdyby wygasł):
    398_timer_....0.0.0.zip Download (1.8 MB)
    Timer Calculator jest w stanie wygenerować poprawny kod ustawienia Timera dla danego oscylatora i danego okresu wywoływania przerwania. Czyli np. podajemy, że chcemy timer 1 co 100ms przy zegarze 8MHz i z tego narzędzia uzyskujemy gotowy kod:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    UWAGA: Ale ten gotowy kod nie jest w pełni kompatybilny z SDCC, należy podmienić nieco zapis rejestrów na inny (ale analogiczny do pokazanego). W razie wątpliwości zapraszam do zapoznania się z przykładami z tego tematu, dla każdego Timera dam przykład. Natomiast samych wartości hex z kalkulatora (ustawienia rejestrów konfiguracji i liczników) można używać bez zmian. Sam będę opierać się na tym kalkulatorze tworząc ten temat.
    Timer Calculator wspiera różne rodzaje PICów i nie tylko.
    Niekompletna lista mikrokontrolerów wspieranych przez Timer Calculator:
    - praktycznie wszystkie PICe, PIC16F, PIC18F, dsPIC, PIC24, PIC32
    - różne AVRy, ATMega, ATTiny, ATXMega
    - STM32
    - AT90
    Pełna lista dostępna w programie. Zapraszam do pobierania.

    Przypomnienie - schemat układu testowego
    Przed przejściem już do praktycznych przykładów chciałbym przypomnieć, że wszystkie pokazane tu programy są uruchamiane na takim układzie złożonym na płytce stykowej:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Układ ten obejmuje minimum elementów potrzebnych do uruchomienia PICa, podłączenie do programatora PICKIT3 i zasilania, itp.
    Schemat całości:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Więcej informacji (jak również opis jak wgrywać wsad na PICa) znajduje się w poprzednich częściach tutoriala.

    Timer0 - 8/16 bitowy timer
    Timer 0 może działać w dwóch trybach: 8 i 16 bitowym. Timer 0 może działać w roli licznika impulsów na pinie T0CKI (pin RA4). Timer 0 zlicza impulsy (z zegara lub z pinu) i w momencie przepełnienia się jego licznika wywołuje przerwanie.
    Z Timerem 0 powiązane są rejestry:
    - T0CON - tutaj konfigurujemy działanie Timera 0 (włączamy go, ustawiamy prescaler)
    - TMR0L - jest to niższy bajt licznika Timera 0
    - TMR0H - jest to wyższy bajt licznika Timera 0
    Zawartość rejestru T0CON pokazuje obrazek:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    W rejestrze T0CON są następujące bity:
    - TMR0N - flaga pozwala na włączenie Timera 0
    - T08BIT - flaga określająca, czy Timer 0 jest w trybie 8-bitowym czy 16-bitowym
    - T0CS - 'clock source', źródło zegara Timera 0 (czy zliczamy impulsy na pinie T0CKI czy z wew. zegara PICa)
    - T0SE - 'source edge', określa czy zliczamy na narastającym czy opadającym zboczu
    - PSA - 'prescaler assigment bit', pozwala nam włączyć lub wyłączyć prescaler dla tego timera. 1 oznacza, że prescaler jest pomijany.
    - T0PS2, T0PS2, T0PS1 - prescaler Timera 0
    Wartość prescalera dla danej kombinacji bitów T0PS* przedstawia tabela:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Zaraz przekonamy się jak to działa w praktyce.

    Timer0 - przykładowy kod (blink led)
    Na bazie wiedzy z poprzednich akapitów (i być może też noty katalogowej PIC18F2550 oraz kalkulatora Timerów od Mikro C) bardzo łatwo jest opracować prosty program, który miga diodą właśnie z pomocą Timera i przerwania.
    Tutaj zaprezentuję takich kilka, ten będzie pierwszy i najdokładniej opisany.
    Program ten będzie wywoływać przerwanie co 1 sekundę i w funkcji jego obsługi zmieniać stan diody LED.
    Użyjemy do tego Timera 0.
    Co musimy w tym celu zrobić?
    1. ustawić bit TMR0ON z T0CON by włączyć timer 0
    2. ustawić bit GIE z INTCON by włączyć globalnie przerwania
    3. ustawić bit T0IE z INTCON by włączyć przerwania od timera 0
    4. jeśli chcemy, to ustawić odpowiednio wartość prescalera z rejestru T0CON (tu możecie posłużyć się gotowym kalkulatorem Timer Calculator)
    5. ustawić odpowiednio wartości TMR0H i TMR0L by dobrać ilość impulsów po której nastąpi przepełnienie (tu możecie posłużyć się gotowym kalkulatorem Timer Calculator)
    6. dodać funkcję obsługującą przerwanie (w SDDC z dopiskiem __interrupt ) i wyłapać z pomocą if w niej odpowiednią flagę przerwania (tutaj T0IF z INTCON) i po obsłudze przerwania ustawić ją na 0
    Tylko tyle trzeba aby pomigać diodą LED z pomocą Timera 0.
    Jak określić ręcznie (bez kalkulatora od Mikro C) okres Timera?
    1. Potrzebujemy znać częstotliwość instrukcji PICa, jest ona równa częstotliwości zegara podzielonej na 4.
    Jeśli używamy wewnętrznego oscylatora 1MHz (domyślnego), to wynosi ona 1MHz/4 = 250kHz
    2. Potrzebujemy dobrać sobie dowolny prescaler do dodatkowego podziału tej częstotliwości. Jest on trzybitowy, bity T0PS2,T0PS1,T0PS0. Weźmy na przykład ustawienie 001, czyli preskaler 1:4.
    250kHz/4 = 62.5kHz czyli impuls co 0.016ms
    3. Teraz zostało wybrać co ile tych impulsów o okresie 0.016ms chcemy mieć przerwanie. Ustawia się to w TMR0H i TMR0L.
    1 sekunda to jest 1000ms
    1000ms/0.016ms = 62500
    Wychodzi nam, że chcemy mieć przerwanie co 62500 zliczonych impulsów przez Timer 0.
    4. Teraz należy pamiętać, że Timer 0 z każdym impulsem inkrementuje wartość TMR0L, a nie dekrementuje. Nie możemy po prostu ustawić tam naszego 62500.
    Musimy policzyć, ile od 62500 brakuje do pełnych dwóch bajtów, czyli 2^16
    2^16 = 65536
    65536 - 62500 = 3036
    5. Musimy ustawić rejestry TMR0L/TMR0H na 3036. W tym celu użyjemy notacji szesnastkowej.
    3036 w postaci szesnastkowej zapisujemy tak: 0x0BDC
    6. Pamiętając, że młodszy (niższy) bajt jest po prawej, możemy to podzielić na:
    TMR0H = 0x0B;
    TMR0L = 0xDC;
    Gotowe! Tyle wystarczy, by mieć przerwanie co ustaloną przez nas sekundę.
    UWAGA: jeśli w punkcie 3 okaże się, że policzona przez ilość impulsów jest większa od 2^16, to po prostu musimy się cofnąć do pkt 2 i dobrać inny prescaler. A w skrajnych przypadkach może być potrzeba zliczania impulsów ręcznie w kodzie, bo też nie każda wartość prescalera jest dostępna.

    Poniższy kod pokazuje pełna implementację tego co powyżej omówiłem:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Powyższy kod wymaga jeszcze podkreślenia paru rzeczy.
    - TMR0H i TMR0L musi być ustawiane przez nas na pożądaną ilość impulsów ZA KAŻDYM RAZEM, bo licznik te rejestry modyfikuje. Dlatego są one ustawiane i w funkcji przerwania i w uruchomieniu Timera
    - Te tajemnicze 0x81 wpisane do T0CON to jest po prostu 1000 0001, czyli jak spojrzymy do dokumentacji T0CON jest to równoznaczne z wpisaniem 1 do TMR0ON i 1 do T0PS0.
    - przypomnę, że powyższy kod działa na domyślnym ustawieniu wewnętrznego oscylatora, czyli 1MHz. Przy zmianie tej częstotliwości zmieni się oczywiście też okres Timera 0 i jeśli dalej będziemy chcieli mieć 1 sekundowy okres przerwania, to trzeba będzie poprawić konfigurację Timera.
    Jak działa miganie diodą z pomocą przerwania w tym kodzie?
    Program wykonuje się normalnie, ustawia tryb pinu C0 na wyjście a potem wywołuje funkcję InitTimer0. Funkcja ta uruchamia Timer 0, a program wykonuje się dalej i wchodzi do nieskończonej pętli while(1). W tej pętli możemy wykonywać dowolne operacje jakie chcemy - obliczenia, obsługuję logiki programu, itp. a Timer 0 będzie już działać w tle.
    Dopiero w momencie gdy Timer 0 się przepełni, to w sposób niewidoczny do nas wykonywanie głównej pętli programu zostaje przerwanie i zacznie wykonywać się funkcja myTimerInterrupt. To właśnie nazywa się przerwaniem. W tej funkcji nastąpi zmian stanu pinu od diody LED. Po wykonaniu tej funkcji program wróci do funkcji main i wznowi wykonywanie tego co robił wcześniej.

    Filmik pokazuje wyżej przedstawiony kod w akcji:

    Jak widać, wszystko działa.
    Pełna paczka p18f2550_blink_tmr0_intOsc1MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_b...sc1MHz.zip Download (12.54 kB)Punkty: 0.5 dla użytkownika

    Timer1 - 16 bitowy timer
    Timer 1 działa w trybie 16 bitowym. Timer 1 może działać w roli licznika impulsów na pinie T13CKI (pin RC0). Timer 1 pozwala na podłączenie osobnego oscylatora na pinach T1OSO (RC0) oraz T1OSI (RC1). Timer 1 zlicza impulsy (z zegara lub z pinu) i w momencie przepełnienia się jego licznika wywołuje przerwanie.
    Jeśli chcemy użyć Timera 1 w połączeniu z zewnętrznym oscylatorem kwarcowym to używamy tych pinów:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Z Timerem 1 powiązane są rejestry:
    - T1CON - tutaj konfigurujemy działanie Timera 1 (włączamy go, ustawiamy prescaler)
    - TMR1L - jest to niższy bajt licznika Timera 1
    - TMR1H - jest to wyższy bajt licznika Timera 1
    Zawartość rejestru T1CON pokazuje obrazek:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania

    W rejestrze T1CON są następujące bity:
    - TMR1ON - flaga pozwala na włączenie Timera 1
    - T1RUN - flaga określająca, czy zegar Timera 1 pochodzi z oscylatora Timera 1 czy innego źródła
    - TMR1CS - 'clock source', źródło zegara Timera 1 (czy zliczamy impulsy na pinie T13CKI czy z wew. zegara PICa). Tutaj nie ma możliwości wyboru tego czy naliczanie odbywa się na zboczu rosnącym czy opadającym tak jak w Timerze 0, dla Timera 1 zliczanie jest zawsze na zboczu rosnącym
    - T1OSCEN - 'oscillator enable', pozwala włączyć lub wyłączyć oscylator skojarzony z Timerem 1 (piny T1OSO i T1OSI)
    - T1SYNC - flaga określa czy synchronizacja dla zewnętrznego zegara Timera 1 jest włączona. Ma znaczenie tylko gdy TMR1CS = 1, w przeciwnym razie jest ignorowana.
    - T1CKPS1, T1CKPS0 - prescaler Timera 1
    W przeciwieństwie do Timera 0, tutaj mamy tylko 4 kombinacje (2 bity, a nie 3).
    Wartość prescalera dla danej kombinacji bitów T1CKPS* przedstawia tabela:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Jak widać, Timer 1 jest podobny do Timera 0, nazwy rejestrów i ich działanie jest zorganizowane w sposób analogiczny, ale istnieją pewne różnice w funkcjonalności i ograniczenia (np. węższy wybór prescalera, brak możliwości wyboru zbocza przy ustawieniu TMR1CS), co sprawia że każdy timer nadaje się do nieco innych zastosowań.
    Zapraszam do zapoznania się z przykładem migania diodą z pomocą Timera 1 poniżej:

    Timer1 - przykładowy kod (blink led)
    Analogicznie tak jak w przypadku Timera 0, z pomocą Timera 1 możemy wywoływać przerwanie i mrugać diodą LED.
    Konfiguracja Timera 1 tutaj też pochodzi z Timer Calculatora i myślę, że nie wymaga tłumaczenia.
    Pełny kod programu:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Program został sprawdzony w praktyce, działa poprawnie i miga diodą LED.
    Pełna paczka p18f2550_blink_tmr1_intOsc1MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_b...sc1MHz.zip Download (12.66 kB)Punkty: 0.5 dla użytkownika

    Timer2 - 8 bitowy timer
    Timer 2 działa podobnie jak Timery 1 i 0, z tą różnicą, że jest ośmiobitowy.
    Nie oferuje więc rejestrów "TMR2H i TMR2L", po prostu takie rejestry nie istnieją - mamy za to rejestr PR2 i TMR2.
    Tutaj jest nieco inaczej - przerwanie nie wywołuje się w momencie przepełnienia TMR2, lecz wywołuje się w momencie kiedy TMR2 jest równy PR2.
    Timer 2 konfigurujemy w rejestrze T2CON:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    W rejestrze T2CON są następujące bity:
    - TMR2ON - pozwala włączyć Timer 2
    - T2OUTPS3:T2OUTPS0 - czterobitowe słowo postscalera dla Timera 2
    - T2CKPS1:T2CKPS0 - dwa bity określające prescaler Timera 2
    Tabela wartości T2OUTPS* (dostępne ustawienia postscalera Timer 2):
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Tabela wartości T2CKPS* (dostępne ustawienia prescalera Timer 2):
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Widać, że działanie Timera 2 jest podobne do pozostałych Timerów. Szczegółowe informacje dostępne są w nocie katalogowej PICa.

    Timer2 - przykładowy kod (blink led)
    Tutaj też migamy diodą, ale z pomocą Timera 2.
    Ten przykład jest nieco inny niż w przypadku Timera 0 i Timera 1, gdyż Timer 2 jest już tylko ośmiobitowy, odpowiedzialny za jego licznik rejestr nazywa się PR2.
    Tutaj też ze względu na to, że timer ma tylko 8 bitów okres timera został wybrany na 0.25s a nie na 1s - po prostu nie dałoby się uruchamiać go co 1 sekundę, można to zrealizować jedynie programowo (zliczać 0.25s cztery razy).
    Sam Timer Calculator od Mikro C poprawnie wykrywa ten przypadek:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Reszta tak jak w poprzednich przykładach, oto pełny kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Program został sprawdzony w praktyce, działa poprawnie i miga diodą LED.
    Pełna paczka p18f2550_blink_tmr2_intOsc1MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_b...sc1MHz.zip Download (12.38 kB)Punkty: 0.5 dla użytkownika

    Timer3 - 16 bitowy timer
    Timer 3 to ostatni z czterech Timerów oferowanych przez PIC18F2550.
    Działa on analogicznie do timerów 0 i 1.
    Operuje na rejestrach TMR3H i TMR3L.
    Konfigurujemy go poprzez rejestr T3CON:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Do Timera 3 można też podłączyć zewnętrzny oscylator powiązany z Timerem 1, czyli ten na pinach T1OSO/T1OSI. Włącza się go z pomocą bitu TMR3CS.

    Timer3 - przykładowy kod (blink led)
    Przykład migania diodą z pomocą Timeru 3 jest analogiczny do Timerów 0 i 1, więc nie wymaga raczej ponownego tłumaczenia.
    Pełny kod przykładu:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Program został sprawdzony w praktyce, działa poprawnie i miga diodą LED.
    Pełna paczka p18f2550_blink_tmr3_intOsc1MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_b...sc1MHz.zip Download (12.64 kB)Punkty: 0.5 dla użytkownika

    Przerwania zewnętrzne - INT0 - przycisk
    Teraz zaprezentuję działanie przerwania zewnętrznego INT0 w PIC18F2550.
    Przerwanie to odbywa się w momencie gdy stan na pinie INT0 ulegnie zmianie - to od nas zależy, czy wybierzemy zmianę w postaci zbocza rosnącego (rising edge) czy opadającego (falling edge).
    Pin INT0 dzieli swoją funkcję z RB0/AN12 i znajduje się tutaj:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    W ramach tego przykładu zrealizujemy na nim przycisk.
    Przyciśnięcie przycisku będzie skutkować wywołaniem przerwania, w którym zmienimy stan diody LED.
    Tym razem wybrałem microswitch który ma tylko dwie nóżki (będzie go łatwiej umieścić na płytce stykowej bez stosowania dodatkowych zworek):
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    I umiejscowiony na płytce:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Co zrobimy w kodzie?
    - ustawimy RB0/INT0 jako wejście (rejestr TRISB)
    - wyłączymy funkcje analogowe dla AN12 (bo są na tym samym pinie; w rejestrze ADCON1)
    - włączymy rezystory pull-up wbudowane dla portu B (w rejestrze INTCON2)
    - włączymy przerwania od INT0 (rejestr INTCON)
    - włączymy ogólnie przerwania
    - wybierzemy, że chcemy przerwanie na wzrastającym zboczu
    - w obsłudze przerwania zmienimy stan diody LED
    Oto i gotowy kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Teraz odpalimy całość i przetestujemy.
    Filmik z działania (a raczej pokazujący niepoprawne działanie):

    Jak widać, coś jest zdecydowanie nie tak. Nie zawsze na skutek naciśnięcia przycisku zmienia się poprawnie stan diody LED. A właściwie to zmienia się - ale kilka razy - na skutek drgania styków.
    W ten sposób wraca do nas temat debouncingu.
    Tym razem rozwiążemy go sprzętowo. Na pin INT0 dodamy kondensator 100nF:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    I po umieszczeniu na płytce:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Filmik z działania po dodaniu sprzętowego debouncingu:

    O wiele lepiej! Przycisk działa poprawnie a dodany kondensator 100nF w dostateczny sposób rozwiązuje problem drgania styków.
    Pełna paczka p18f2550_blink_int0_intOsc1MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_b...sc1MHz.zip Download (12.7 kB)Punkty: 0.5 dla użytkownika

    Przerwania zewnętrzne - INT1 - przycisk
    Ten przykład jest analogiczny do poprzedniego. Ochotników zachęcam do próby samodzielnego napisania tego kodu i dopiero potem porównania swojej wersji z moim rozwiązaniem.
    Przycisk tym razem podłączony będzie na pinie INT1 (RB1, AN10):
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Reszta pozostaje bez zmian.
    Ja niestety musiałem jeszcze zmienić przycisk, bo.... ten stary miał za krótkie piny.
    Nowy przycisk:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Nowe podłączenie na płytce stykowej:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Zaktualizowany kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    I standardowo - filmik z działania:

    Wszystko działa poprawnie (bez kondensatora 100nF do sprzętowego debouncingu znów byłyby problemy).
    Pełna paczka p18f2550_blink_int1_intOsc1MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_b...sc1MHz.zip Download (12.7 kB)Punkty: 0.5 dla użytkownika


    Timer0 - jako licznik impulsów
    Teraz zaprezentuję w prosty sposób jak Timer 0 może posłużyć za licznik impulsów (tryb Counter).
    W celu skonfigurowania Timer 0 jako licznik impulsów po prostu musimy przełączyć jego źródło z wewnętrznego zegara na zegar zewnętrzny, czyli ustawić bit T0CS z rejestru T0CON na wartość 1:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Taka konfiguracja Timera pozwala na zliczanie impulsów na pinie T0CKI, czyli w przypadku obudowy DIP28 i PIC18F2550 na pinie RA4:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Rejestr T0CON pozwala nam również zdecydować czy zwiększać Timer chcemy na zboczu opadającym czy rosnącym. Odpowiada za to bit T0SE:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Na bazie tej wiedzy przygotuję dwa kolejne przykłady.

    Timer0 - jako licznik impulsów - prosty przykład
    Na początek prosty przykład. Tutaj źródłem impulsów będzie przycisk, chociaż oczywiście tak nie musi być, z pomocą tego samego mechanizmu można nawet wykonać pomiar częstotliwości.
    Podłączymy go do pinu T0CKI wraz z rezystorem pull-up i kondensatorem zapewniającym debouncing:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Tak jak na schemacie (w niebieskiej obwódce to co dodaliśmy):
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Cały układ po podłączeniu:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania

    Wartości TMR0H i TMR0L ustawimy na 0xFF, by licznik przepełniał się z pierwszym wciśnięciem microswitcha.
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Pełny kod przykładu:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Przykład wgrałem na PICa i przetestowałem. Oczywiście działa w pełni poprawnie, każde wciśnięcie przycisku zmienia stan diody LED.
    Filmik przedstawia działanie programu w praktyce:

    Jak widać na filmiku, każde naciśnięcie przycisku zmienia stan diody LED.

    Timer0 - jako licznik impulsów - przerwanie co kilka impulsów
    Oczywiście nic nie stoi na przeszkodzie by generować przerwanie co kilka impulsów. Taka jest właśnie idea liczników.
    Możemy łatwo zmodyfikować kod przykładu powyżej, by wykonywać funkcje myTimerInterrupt np. co trzy wciśnięcia przycisku.
    W tym celu zaktualizujemy ustawienie TMR0L/TMR0H:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Przypominam jedynie, że zliczamy "od tyłu", bo każdy cykl Timera zwiększa liczniki TMR0H/TMR0L a przerwanie wywołuje się w momencie jego przepełnienia. Dlatego 0xFD, a nie 0x03.
    Filmik z działania programu:

    Jak widać na filmiku, co trzecie wciśnięcie przycisku zmienia stan diody LED.

    Pełna paczka p18f2550_counterBlink_intOsc8MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_c...sc8MHz.zip Download (12.87 kB)Punkty: 0.5 dla użytkownika

    Timer0 - jako licznik impulsów - przykład z UART
    Już mniej więcej zapoznaliśmy się z funkcją licznika w Timerze 0, więc teraz pora na coś bardziej zaawansowanego.
    Będziemy zliczać impulsy i wysyłać ich bieżącą liczbę po UART do komputera.
    A dokładniej - będziemy wysyłać zawartość rejestrów TMR0H i TMR0L do komputera, by zobrazować sobie co tak dokładnie dzieje się w PICu.
    W tym celu skorzystamy z kodu komunikacji UART który umieściłem już w poprzednim temacie z cyklu:
    https://www.elektroda.pl/rtvforum/viewtopic.php?p=18453286#18453286
    Oczywiście, do całego układu podłączymy przejściówkę USB-UART (ja użyłem HW-597, ale może być dowolna) by móc jakoś odebrać dane i wyświetlić je np. w Realterm:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Do tego przyda nam się funkcja zamieniająca liczbę całkowitą (typ integer) na string (tablicę charów), ale taką funkcję można łatwo znaleźć w sieci, np tutaj:
    https://www.geeksforgeeks.org/implement-itoa/
    Funkcja tam dostępna jest napisana w stylu C++ i trzeba ją przenieść do C, ale nie wymaga to dużej ilości zmian. M. in. trzeba rozwiązać kwestie tego, że w C nie ma 'bool', 'true', 'false', itp. Reszta bez problemu skompiluje się nawet na 8-bitowym PICu.

    Z pomocą informacji powyżej opracowałem pełny kod przykładu, zapraszam do zapoznania się:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Program co chwila wysyła do komputera zawartość rejestrów TMR0H i TMR0L. W momencie wciśnięcia przycisku do rejestru TMR0L zostaje dodana wartość 1, co widać dobrze w konsoli:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Sama dioda LED wcale nie miga wtedy, gdyż funkcja przerwania myTimerInterrupt wywołana zostanie dopiero w momencie przepełnienia TMR0L i TMR0H, a przepełnienie tych dwóch bajtów wymagałoby wciśnięcia przycisku jakieś... 2^16 razy.
    Pełna paczka p18f2550_counter_intOsc8MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_c...sc8MHz.zip Download (37.11 kB)Punkty: 0.5 dla użytkownika

    Kilka timerów w jednym programie
    Myślę, że jeszcze w ramach uzupełnienia przydałoby się pokazać jak korzystać z kilku timerów/przerwań w obrębie jednego programu.
    Tak jak wcześniej wspomniałem, funkcja obsługi przerwania będzie tutaj dalej jedna - jedynie w tej funkcji będziemy sprawdzać które przerwanie/zdarzenie ją wywołało z pomocą flag xxxIF.
    Czyli coś w stylu:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Samo działanie czterech timerów zwizualizujemy oczywiście z pomocą czterech diod LED, podłączonych wraz z rezystorami na osobnej płytce:
    Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
    Diody połączyłem tutaj katodami i podłączyłem do masy układu, ale na odwrót też oczywiście by zadziałało.
    Dla timerów przyjąłem poszczególne okresy:
    - Timer 0 - 2 sekundy
    - Timer 1 - 1.5 sekundy
    - Timer 2 - 0.25 sekundy
    - Timer 3 - 1 sekunda
    I użyłem kalkulatora od Mikro C by szybko policzyć ich preskalery/wartości TMR*.
    Przyjąłem, że każdy timer w obsłudze swojego przerwania zmienia stan jednego pinu.
    - Timer 0 - pin C0
    - Timer 1 - pin C1
    - Timer 2 - pin C2
    - Timer 3 - pin C6
    Zapraszam do zapoznania się z gotowym kodem:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    I standardowo; rezultat działania na filmiku:

    Pełna paczka p18f2550_blink4x_tmrs_intOsc1MHz.zip z przykładem do pobrania (kod w C, skompilowany .hex, .bat do kompilacji w SDCC):
    p18f2550_b...sc1MHz.zip Download (16.73 kB)Punkty: 0.5 dla użytkownika

    Przydatne materiały do zapoznania się
    Mój temat powinien stanowić tylko mały wstęp do przygody z przerwaniami i timerami w PIC18F. Wszystko na ich temat jest opisane dokładniej w dokumentach od Microchipa, które dla Was tutaj zebrałem i umieściłem do pobrania:

    Nota katalogowa naszego PICa - PIC18F2550 datasheet:
    pic18f2550...asheet.pdf Download (6.96 MB)
    Tutorial o timerach od Microchipa, niestety dla innego kompilatora, ale też wartościowy - Timers: Timer0 Tutorial (Part 1):
    Timers-Tim...ial-p1.pdf Download (425.55 kB)
    Część druga tutoriala od Microchipa - Timers: Timer0 Tutorial (Part 2):
    Timers-Tim...ial-p2.pdf Download (604.31 kB)
    Fragment PIC Midrange Manual - Section 8. Interrupts:
    midrange-f...rrupts.pdf Download (68.76 kB)
    Nota aplikacyjna Using the PORTB Interrupt on Change as an External Interrupt:
    Using the ...errupt.pdf Download (98.87 kB)

    Powiązane projekty i tematy które będą jeszcze omówione
    Wszystkie przykłady pokazane w tym temacie są bardzo proste, mogą stanowić dobry punkt wyjścia do tworzenia własnych kodów, lecz nie pokazują w pełni do czego możemy wykorzystać timery i przerwania w praktyce.
    W związku z tym w przyszłości planuję osobne części tutoriala które zademonstrują jak używa się timerów i przerwań w praktycznych zastosowaniach. Części te obejmą m. in:
    - obsługę wyświetlacza 7-segmentowego z kilkoma cyframi z pomocą timera i przerwania (sami posterujemy segmentami przez tranzystory i rezystory)
    - prosty system pomiaru częstotliwości z pomocą sprzętowego licznika PIC18F (zrobimy własny miernik częstotliwości i zweryfikujemy jego działanie np. poprzez pomiar częstotliwości z sieci przez transformator)
    Jak ktoś ma jeszcze jakieś pomysły na praktyczne projekty używające przerwań i timerów to zapraszam do dawania sugestii.

    Podsumowanie
    Tutaj przedstawiłem podstawy podstaw timerów i przerwań w PIC18F na najprostszych przykładach. Każdy przykład został przeze mnie przetestowany w praktyce i opisany, wszystkie kody są dostępny do pobrania. Bardziej zainteresowanych tematem i bardziej szczegółowymi informacjami zapraszam do lektury źródeł umieszczonych w poście.
    Temat będzie jeszcze redagowany, w razie błędów/uwag proszę o sugestie na PW.
    Następna część tutoriala niedługo - skupię się na niej na wyświetlaczach siedmiosegmentowych.

    Fajne! Ranking DIY
    Potrafisz napisać podobny artykuł? Wyślij do mnie a otrzymasz kartę SD 64GB.
    O autorze
    p.kaczmarek2
    Poziom 24  
    Offline 
  • e-miernikie-mierniki