Ten post chciałbym poświęcić na szybki opis sygnałów zegarowych oraz sposobu ich deklaracji w mikrokontrolerach z rodziny ATXmega.
Mikrokontrolery z tej serii mają (tak przynajmniej mi się wydaje) jedno wejście pozwalające na podłączenie kwarcu. Dlatego należy wybrać jakiego rodzaju on będzie czy zwykły do taktowania całego układu, czy też zostawienie go dla zegara czasu rzeczywistego. Druga opcja jest zdecydowanie lepsza bo zegary wewnętrzne zdeklarowane zakresy pracy posiadają tylko dla temperatury pokojowej. Dla pozostałych zakresów te wartości mogą znacznie (kilka procent) odbijać od zdeklarowanej.
Kwarc zewnętrzny należy podłączyć wraz z kondensatorami, których wartości są udostępnione przez producenta. Jego wartość może maksymalnie wynosić 16MHz:
- 0.4 - 2 MHz - 100pF
- 2 - 9 MHz - 15pF
- 9 - 12 MHz - 15pF
- 12 - 16 MHz - 10pF
Jeśli by się zdarzyło tak, że kwarc uległby uszkodzeniu to układ automatycznie zostanie przełączony na zegar wewnętrzny. Jego taktowanie można dynamicznie zmieniać w programie, oraz z różnych źródeł taktować poszczególne jego bloki.
Z dostępnych zegarów wewnętrznych można korzystać z (pod tym linkiem znajduje się nota od producenta):
- Energooszczędny 32kHz (dokładność 1% przy 25 st. C oraz zasilaniu 3V);
- Szybki 32MHz (dokładność 1% przy 25 st. C oraz zasilaniu 3V);
- Domyślny 2MHz (dokładność 1% przy 25 st. C oraz zasilaniu 3V);
- Zegar dla RTC (32.768kHz)
Każdy z nich można następnie podać na kilka dzielników:
- A - od 1 do 512;
- B - od 1 do 4;
- C - od 1 do 2;
Schemat sygnałów zegarowych znajduje się na rysunku 1:
Rys. 1. Schemat sygnałów zegarowych [Atmel]
Jak można zaobserwować na rysunku, różne bloki mikrokontrolera są taktowane różnymi sygnałami z poszczególnych wyjść dzielników. CPU and peripheral jest podawany na rdzeń mikrokontrolera. Jego wartość może wynosić do 32MHz. Zegar 2x oraz 4x służy do podania taktowania peryferiów, które mogą pracować z szybszym zegarem. Osobne zegary zostały przygotowane dla RTC oraz USB.
Zegar dla USB jest uruchamiany poprzez włączenie zegara 2MHz. Kolejnym krokiem jest konfiguracja automatycznej kalibarcji DFLL czyli Digital Frequency Lock Loop z zegara 32 kHz wewnętrznego lub zewnętrznego. Następnie konfiguruje się sam moduł USB tak aby pracował z wewnętrznym zegarem 32 MHz. Kolejnym etapem jest jego kalibracja do wartości 48MHz przy użyciu USB Start of Frame (SOF) jako wartości referencyjnej.
Konfiguracja każdego z zegarów sprowadza się do skonfigurowania oraz uruchomienia generatora. Następnie należy odczekać aż zostanie on ustabilizowany po czym należy odblokować możliwość zmiany jego źródła. Kolejnym krokiem jest jego ustawienie.
Wybór źródła sygnału dokonuje się w rejestrze kontrolnym OSC_CRTL gdzie na trzech bitach ustawia się jeden z dostępnych źródeł sygnału taktującego czyli:
- 000 - 2MHz domyślnie
- 001 - 32MHz
- 010 - 32KHz
- 011 - Zewnętrzny oscylator
- 100 - PLL
Poniżej włączenie kilku rodzajów sygnałów.
- void HSE_16MHz_On()
- {
- //W rejestrze kontrolnym ustawione zostaje wybor kwarcu od 12 do 16
- //Drugi parametr okresla czas na odczekanie do wlaczenia
- OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc;
- //Uruchomienie generatora
- OSC.CTRL |= OSC_XOSCEN_bm;
- //Odczekanie az sygnal zostanie ustabilizowany
- while(!(OSC_STATUS & OSC_XOSCRDY_bm));
- //Odblokowanie mozliwosci zmiany rodzaju sygnalu
- CPU_CCP = CCP_IOREG_gc;
- //Wybranie kwarcu zewnetrznego
- CLK.CTRL = CLK_SCLKSEL_XOSC_gc;
- //Odblokowanie mozliwosci zmiany rodzaju sygnalu
- CCP = CCP_IOREG_gc;
- //Wlaczenie ukladu detekcji bledu sygnalu
- OSC.XOSCFAIL = OSC_XOSCFDEN_bm;
- }
- void Inter_32MHz()
- {
- //Wlaczenie 32MHz
- OSC.CTRL |= OSC_RC32MEN_bm;
- //Odczekanie na ustabilizowanie ukladu
- while(!(OSC_STATUS & OSC_RC32MRDY_bm));
- //Odblokowanie możliwosci zmiany sygnalu
- CPU_CCP = CCP_IOREG_gc;
- //Ustawienie zrodla na 32MHz
- CLK_CTRL = CLK_SCLKSEL_RC32M_gc;
- }
- void Inter_2MHz(void)
- {
- //Wlaczenie 2MHz
- OSC.CTRL = OSC_RC2MEN_bm;
- //Czekanie na stabilizacje generatora
- while(!(OSC.STATUS & OSC_RC2MRDY_bm));
- //Odblokowanie mozliwosci zmiany sygnalu
- CPU_CCP = CCP_IOREG_gc;
- //Ustawienie 2MHz jako zegara ukladu
- CLK.CTRL = CLK_SCLKSEL_RC2M_gc;
- }
- void PLL_Clock_On_2MHz(uint8_t value)
- {
- //value od 1 do 31
- if(value <= 0 || value >32) { value = 31; }
- //Wlaczenie generatora 2MHz
- OSC.CTRL = OSC_RC2MEN_bm;
- //Czekanie na stabilizacje
- while(!(OSC.STATUS & OSC_RC2MRDY_bm));
- //Odblokowanie mozliwosci zmiany sygalu
- CPU_CCP = CCP_IOREG_gc;
- //Wlaczenie 2MHz
- CLK.CTRL = CLK_SCLKSEL_RC2M_gc;
- //Wyczyszczenie rejestru
- OSC.CTRL &= ~OSC_PLLEN_bm;
- //Ustawienie PLL
- OSC.PLLCTRL = OSC_PLLSRC_RC2M_gc | value;
- //Wlaczenie PLL
- OSC.CTRL = OSC_PLLEN_bm;
- //Stabilizowanie
- while(!(OSC.STATUS & OSC_PLLRDY_bm));
- //Odblokowanie mozliwosci zmiany
- CPU_CCP = CCP_IOREG_gc;
- //Wybor PLL jako źrodlo
- CLK.CTRL = CLK_SCLKSEL_PLL_gc;
- //Odblokowanie
- CPU_CCP = CCP_IOREG_gc;
- //Wlaczenie ukladu detekcji bledu
- OSC.XOSCFAIL = OSC_PLLFDEN_bm;
- }
No i ostatnia już funkcja czyli PLL z zewnętrznego kwarcu:
- void PLL_Clock_On_HSE(uint8_t value)
- {
- if(value <= 0 || value >32) { value = 31; }
- OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc;
- OSC.CTRL |= OSC_XOSCEN_bm;
- while(!(OSC_STATUS & OSC_XOSCRDY_bm));
- CPU_CCP = CCP_IOREG_gc;
- CLK.CTRL = CLK_SCLKSEL_XOSC_gc;
- //Wyczyszczenie rejestru
- OSC.CTRL &= ~OSC_PLLEN_bm;
- //Ustawienie PLL
- OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | value ;
- //Wlaczenie PLL
- OSC.CTRL = OSC_PLLEN_bm;
- //Stabilizowanie
- while(!(OSC.STATUS & OSC_PLLRDY_bm));
- //Odblokowanie mozliwosci zmiany
- CPU_CCP = CCP_IOREG_gc;
- //Wybor PLL jako źrodlo
- CLK.CTRL = CLK_SCLKSEL_PLL_gc;
- //Odblokowanie
- CPU_CCP = CCP_IOREG_gc;
- //Wlaczenie ukladu detekcji bledu
- OSC.XOSCFAIL = OSC_PLLFDEN_bm;
- }