Kurs XMEGA: timer i przerwania (12)

|

Aby zapoznać się z podstawami pracy timerów, napiszemy prosty program demonstrujący działanie timera TCC0 oraz jego rejestru PER i systemu przerwań. Po włączeniu zasilania, rejestr CNT timera TCC0 rozpocznie zliczanie w górę, aż do osiągnięcia wartości PER, która domyślnie jest ustawiona na 65535. Wartość tę można będzie zmienić przyciskiem FLIP, który jest na płytce X3-DIL64 z Leon Instruments. Aby było wiadomo, jakie aktualnie wartości są w rejestrach licznika CNT oraz PER, będziemy odczytywać je z wyświetlacza LCD (jak podłączyć wyświetlacz LCD z XMEGA). Dodatkowo, każde zrównanie się rejestrów CNT oraz PER wywoływać będzie przerwanie przepełnienia OVF, co spowoduje zmianę stanu diody podłączonej do pinu E0.



Program zaczynamy, jak zwykle, od dołączenia potrzebnych bibliotek. Bibliotekę hd44780 można ściągnąć razem z kodami źródłowymi niniejszego odcinka kursu.
Pliki do pobrania:

#define  F_CPU 2000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "hd44780.h"
 
Jako że nie będziemy używać żadnych funkcji ani zmiennych globalnych, od razu przechodzimy do pisania funkcji main(), w której na początku ustawimy wejście i wyjścia zgodnie z częścią 4 kursu o tym, jak ustawić piny IO w XMEGA oraz inicjalizujemy wyświetlacz LCD.

int main(void) {
    
    // przycisk
    PORTE.DIRCLR      =    PIN5_bm;                    // pin E5 jako wejście (przycisk FLIP)
    PORTE.PIN5CTRL    =    PORT_OPC_PULLUP_gc;         // podciągnięcie do zasilania
    
    // dioda LED
    PORTE.DIRSET      =    PIN0_bm;                    // pin E0 jako wyjście

    // wyświetlacz
    LcdInit();
 
Najpierw powinniśmy skonfigurować wszystkie ustawienia, a dopiero na końcu uruchomić timer. Kolejność następujących poleceń nie ma znaczenia, byle wyboru preskalera dokonać na samym końcu.


Ustawimy kontroler przerwań PMIC. Jak działają przerwania w XMEGA opisałem w odcinku 6. Podobnie jak w każdym innym typie przerwania, możemy ustalić priorytet niski (LO), średni (MED) lub wysoki (HI). Pamiętaj, że po wybraniu priorytetu dla przerwania przepełniania OVF, musisz również uaktywnić przerwania o tym priorytecie, wpisując PMIC_xxLVLEN_bm do rejestru CTRL kontrolera przerwań PMIC.

    // konfiguracja przerwań
    TCC0.INTCTRLA     =    TC_OVFINTLVL_LO_gc;         // przepełnienie ma generować przerwanie LO
    PMIC.CTRL         =    PMIC_LOLVLEN_bm;            // odblokowanie przerwań o priorytecie LO
    sei();                                             // globalne odblokowanie przerwań
 
Przejdźmy do skonfigurowania timera TCC0. W rejestrze CTRLB wybieramy normalny tryb pracy. Do rejestru CTRLF nie możemy wpisać danych wprost, jak do innych dotychczas używanych. Można wpisywać do niego wartości poprzez rejestry pomocnicze CTRLFSET oraz CTRLFCLR. Działa to podobnie jak maska bitowa przy ustawianiu portów, co robiliśmy na początku programu. Bity wpisane do rejestru SET zostaną wpisane do rejestru CTRLF, a jedynki wpisane do rejestru CLR zostaną w CTRLF wyzerowane. Dzięki takiemu rozwiązaniu nie musimy stosować operatorów |= ani &=. Przy pomocy bitu TC0_DIR_bm w rejestrze CTRLF decydujemy, czy timer ma liczyć w górę czy w dół. Ostatnim etapem konfiguracji timera jest wybranie źródła sygnału. Wybieramy sygnał zegarowy o częstotliwości wstępnie podzielonej przez 1024 przy pomocy stałej TC_CLKSEL_DIV1024_gc. Po tym poleceniu timer zostaje uruchomiony.

    // konfiguracja timera
    TCC0.CTRLB        =    TC_WGMODE_NORMAL_gc;        // tryb normalny
//  TCC0.CTRLFSET     =    TC0_DIR_bm;                 // liczenie w dół
    TCC0.CTRLA        =    TC_CLKSEL_DIV1024_gc;       // ustawienie preskalera i uruchomienie timera
 
W pętli głównej program będzie wyświetlał bieżącą wartość przechowywaną w rejestrach CNT oraz PER. Dodatkowo, jeśli przycisk FLIP zostanie wciśnięty, to rejestr PER będzie zwiększał się o 1000.
 
   while(1) {
        // wyświetlenie aktualnej wartości licznika CNT i PER
        // CNT = ...
        // PER = ...
        LcdClear();
        Lcd("CNT = ");
        LcdDec(TCC0.CNT);
        Lcd2;
        Lcd("PER = ");
        LcdDec(TCC0.PER);
        _delay_ms(100);                                // czekanie 100ms
        
        if(!(PORTE.IN & PIN5_bm)) {                    // jeżeli przycisk FLIP wciśnięty
            TCC0.PER    +=    1000;                    // zwiększ PER o 1000
        }
    }
 
Do opisania pozostała tylko procedura przerwania przepełnienia TCC0_OVF_vect. Składa się z zaledwie jednej linijki, która zamienia stan pinu E0. Jeśli przez zgłoszeniem dioda się świeciła, to zgaśnie, a jeśli była wygaszona, to się zaświeci.

ISR(TCC0_OVF_vect) {                                   // przerwanie przepełnienia TCC0
    PORTE.OUTTGL    =    PIN0_bm;                      // zamiana stanu diody
}
 
Działanie programu przedstawia film.


Kurs XMEGA:Moduły prototypowe:

#define  F_CPU    2000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "hd44780.h"

int main(void) {
    
    // przycisk
    PORTE.DIRCLR      =    PIN5_bm;                    // pin E5 jako wejście (przycisk FLIP)
    PORTE.PIN5CTRL    =    PORT_OPC_PULLUP_gc;         // podciągnięcie do zasilania
    
    // dioda LED
    PORTE.DIRSET      =    PIN0_bm;                    // pin E0 jako wyjście
    
    // wyświetlacz
    LcdInit();
    
    // konfiguracja przerwań
    TCC0.INTCTRLA     =    TC_OVFINTLVL_LO_gc;         // przepełnienie ma generować przerwanie LO
    PMIC.CTRL         =    PMIC_LOLVLEN_bm;            // odblokowanie przerwań o priorytecie LO
    sei();                                             // globalne odblokowanie przerwań
    
    // konfiguracja timera
    TCC0.CTRLB        =    TC_WGMODE_NORMAL_gc;        // tryb normalny
//  TCC0.CTRLFSET     =    TC0_DIR_bm;                 // liczenie w dół
    TCC0.CTRLA        =    TC_CLKSEL_DIV1024_gc;       // ustawienie preskalera i uruchomienie timera
    
    while(1) {
        // wyświetlenie aktualnej wartości licznika CNT i PER
        // CNT = ...
        // PER = ...
        LcdClear();
        Lcd("CNT = ");
        LcdDec(TCC0.CNT);
        Lcd2;
        Lcd("PER = ");
        LcdDec(TCC0.PER);
        _delay_ms(100);                                // czekanie 100ms
        
        if(!(PORTE.IN & PIN5_bm)) {                    // jeżeli przycisk FLIP wciśnięty
            TCC0.PER    +=    1000;                    // zwiększ PER o 1000
        }
    }
}

ISR(TCC0_OVF_vect) {                                   // przerwanie przepełnienia TCC0
    PORTE.OUTTGL      =    PIN0_bm;                    // zamiana stanu diody
}
 

3 komentarze :

Mick g pisze...

Czy jest szansa pojawienia się odpowiedników dla X E5 tam sa TC4 i 5 troche inaczej chyba się to konfiguruje.
Dzięki z góry

Rafał Kry pisze...

Próbuje uruchomić przykład na xmega 256a3bu xplained jednak rejest CNT ma zawsze wartość 0 a PER 255. Po instrukcji TCC0.PER += 1000; wartość rejestru nie zmienia się. Może ma ktoś pomysł co robię nie tak.

Dominik Leon Bieczyński pisze...

Trudno powiedzieć coś sensownego, kiedy nie ma się całego kodu programu.

Prześlij komentarz

Skomentuj!

Sklep Leon Instruments