poniedziałek, 7 listopada 2016

[2] ATXmega - 128A3U - Sygnały zegarowe

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. 

  1. void HSE_16MHz_On()
  2. {
  3.         //W rejestrze kontrolnym ustawione zostaje wybor kwarcu od 12 do 16
  4.         //Drugi parametr okresla czas na odczekanie do wlaczenia
  5.         OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc;
  6.         //Uruchomienie generatora
  7.         OSC.CTRL |= OSC_XOSCEN_bm;
  8.         //Odczekanie az sygnal zostanie ustabilizowany
  9.         while(!(OSC_STATUS & OSC_XOSCRDY_bm));
  10.            
  11.         //Odblokowanie mozliwosci zmiany rodzaju sygnalu
  12.         CPU_CCP = CCP_IOREG_gc;
  13.         //Wybranie kwarcu zewnetrznego
  14.         CLK.CTRL = CLK_SCLKSEL_XOSC_gc;    
  15.         //Odblokowanie mozliwosci zmiany rodzaju sygnalu
  16.         CCP = CCP_IOREG_gc;
  17.         //Wlaczenie ukladu detekcji bledu sygnalu
  18.         OSC.XOSCFAIL = OSC_XOSCFDEN_bm;  
  19. }

  1. void Inter_32MHz()
  2. {
  3.     //Wlaczenie 32MHz
  4.     OSC.CTRL |= OSC_RC32MEN_bm;
  5.     //Odczekanie na ustabilizowanie ukladu
  6.     while(!(OSC_STATUS & OSC_RC32MRDY_bm));
  7.    
  8.     //Odblokowanie możliwosci zmiany sygnalu
  9.     CPU_CCP = CCP_IOREG_gc;
  10.     //Ustawienie zrodla na 32MHz
  11.     CLK_CTRL = CLK_SCLKSEL_RC32M_gc;
  12. }

  1. void Inter_2MHz(void)
  2. {
  3.     //Wlaczenie 2MHz
  4.     OSC.CTRL =  OSC_RC2MEN_bm;
  5.     //Czekanie na stabilizacje generatora
  6.     while(!(OSC.STATUS & OSC_RC2MRDY_bm));
  7.     //Odblokowanie mozliwosci zmiany sygnalu
  8.     CPU_CCP  =  CCP_IOREG_gc;
  9.     //Ustawienie 2MHz jako zegara ukladu
  10.     CLK.CTRL =  CLK_SCLKSEL_RC2M_gc;                                   
  11. }

  1. void PLL_Clock_On_2MHz(uint8_t value)
  2. {
  3.     //value od 1 do 31
  4.     if(value <= 0 || value >32) { value = 31; }
  5.    
  6.     //Wlaczenie generatora 2MHz
  7.     OSC.CTRL        =   OSC_RC2MEN_bm;
  8.     //Czekanie na stabilizacje
  9.     while(!(OSC.STATUS & OSC_RC2MRDY_bm));
  10.     //Odblokowanie mozliwosci zmiany sygalu
  11.     CPU_CCP         =   CCP_IOREG_gc;
  12.     //Wlaczenie 2MHz
  13.     CLK.CTRL = CLK_SCLKSEL_RC2M_gc;
  14.    
  15.     //Wyczyszczenie rejestru
  16.     OSC.CTRL &= ~OSC_PLLEN_bm;
  17.            
  18.     //Ustawienie PLL
  19.     OSC.PLLCTRL = OSC_PLLSRC_RC2M_gc | value;                  
  20.                        
  21.     //Wlaczenie PLL
  22.     OSC.CTRL = OSC_PLLEN_bm;
  23.    
  24.     //Stabilizowanie       
  25.     while(!(OSC.STATUS & OSC_PLLRDY_bm));
  26.     //Odblokowanie mozliwosci zmiany
  27.     CPU_CCP = CCP_IOREG_gc;
  28.     //Wybor PLL jako źrodlo
  29.     CLK.CTRL = CLK_SCLKSEL_PLL_gc;
  30.            
  31.     //Odblokowanie
  32.     CPU_CCP = CCP_IOREG_gc;
  33.     //Wlaczenie ukladu detekcji bledu
  34.     OSC.XOSCFAIL = OSC_PLLFDEN_bm;
  35. }

No i ostatnia już funkcja czyli PLL z zewnętrznego kwarcu:

  1. void PLL_Clock_On_HSE(uint8_t value)
  2. {
  3.     if(value <= 0 || value >32) { value = 31; }
  4.     OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc;
  5.     OSC.CTRL |= OSC_XOSCEN_bm;
  6.     while(!(OSC_STATUS & OSC_XOSCRDY_bm));
  7.     CPU_CCP = CCP_IOREG_gc;
  8.    
  9.     CLK.CTRL = CLK_SCLKSEL_XOSC_gc;
  10.     //Wyczyszczenie rejestru
  11.     OSC.CTRL &= ~OSC_PLLEN_bm;
  12.    
  13.     //Ustawienie PLL
  14.     OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | value ;
  15.    
  16.     //Wlaczenie PLL
  17.     OSC.CTRL = OSC_PLLEN_bm;
  18.    
  19.     //Stabilizowanie      
  20.     while(!(OSC.STATUS & OSC_PLLRDY_bm));
  21.     //Odblokowanie mozliwosci zmiany
  22.     CPU_CCP = CCP_IOREG_gc;
  23.     //Wybor PLL jako źrodlo
  24.     CLK.CTRL = CLK_SCLKSEL_PLL_gc;
  25.     //Odblokowanie
  26.     CPU_CCP = CCP_IOREG_gc;
  27.     //Wlaczenie ukladu detekcji bledu
  28.     OSC.XOSCFAIL = OSC_PLLFDEN_bm;
  29. }