Znamy już generatory RC oraz kwarcowe, opisane w poprzednich odcinkach kursu mikrokontrolerów XMEGA. Niniejsza część, podobnie jak wszystkie pozostałe o sygnałach zegarowych, będzie wykorzystywać schemat z rysunku 1 i te same pliki źródłowe, które można pobrać na dole strony. Układ demonstracyjny wykorzystuje moduł prototypowy X3-DIL64 z Leon Instruments, dostępny w ofercie KAMAMI.
Rys. 1. Schemat układu demonstracyjnego
Układ PLL
PLL, czyli pętla synchronizacji fazy, jest przeciwieństwem preskalera i służy do zwiększania częstotliwości sygnału. Układy PLL dotychczas stosowane były w mikrokontrolerach z wyższej półki oraz w FPGA, ale w XMEGA to rozwiązanie dostępne jest we wszystkich modelach, nawet w tych najtańszych za kilka złotych!
Mamy możliwość pomnożenia częstotliwości wybranego generatora do 31 razy. Do wyboru mamy następujące generatory:
- wbudowany RC 2 MHz,
- wbudowany RC 32 MHz, ale wstępnie podzielony przez 4, czyli 8 MHz,
- kwarcowy 0,4–16 MHz,
- zewnętrzny sygnał zegarowy.
Układ PLL nie może współpracować z generatorami 32 kHz.
Zwróć uwagę, że niewłaściwie konfigurując układ PLL można mocno przetaktować mikrokontroler, co może prowadzić do jego niestabilnej pracy. Pamiętaj, że rdzeń procesora może pracować z sygnałem takującym o częstotliwości 32 MHz. Częstotliwość na wyjściu układu PLL nie powinna być niższa niż 10 MHz, ani wyższa niż 200MHz. W razie potrzeby możesz wykorzystać preskalery (widoczne na rysunku 1 w 7 części kursu), by zmniejszyć częstotliwość zegara.
Napiszemy program, który pozwala zmienić konfigurację układu PLL podczas pracy procesora. Wciskając przycisk podłączony do pinu F4 na płytce X3-DIL64 z Leon Instruments, będziemy zwiększać mnożnik PLL o 1 aż do 31, a po kolejnym wciśnięciu mnożnik ustawi się na 1, by móc go znów zwiększać. Jako źródło sygnału wykorzystany generator 2 MHz, a częstotliwości uzyskane dzięki PLL będą sięgać nawet 62 MHz, co daleko przekracza dopuszczalny limit! Zmiany sygnału zegarowego będziemy obserwować dzięki mrugającej diodzie, a dodatkową informacją będzie wyświetlenie częstotliwości na wyświetlaczu LCD.
Prześledźmy, co dzieje się w funkcji OscPLL. Pierwszym krokiem jest uruchomienie generatora, który będzie źródłem sygnału dla PLL i ustawienie go jako źródła. Trzeba w tym momencie wyraźnie zaznaczyć, że nie można zmieniać konfiguracji układu PLL podczas, gdy jest on uruchomiony, a tym bardziej kiedy jest źródłem sygnału zegarowego.
1 2 3 4 5 6 7 |
void OscPLL(uint8_t pllfactor) { // uruchomienie generatora 2MHz i ustawienie go jako źródła zegara OSC.CTRL = OSC_RC2MEN_bm; // włączenie oscylatora 2MHz while(!(OSC.STATUS & OSC_RC2MRDY_bm)); // czekanie na ustabilizowanie się generatora CPU_CCP = CCP_IOREG_gc; // odblokowanie zmiany źródła sygnału zegarowego CLK.CTRL = CLK_SCLKSEL_RC2M_gc; // zmiana źródła sygnału zegarowego na RC 2MHz |
Następnie, możemy przystąpić do konfiguracji układu PLL. Jednak jeśli jest on już włączony, to koniecznie musimy go najpierw wyłączyć. W przeciwnym razie próba zmiany konfiguracji będzie nieskuteczna. Kluczowy w tym fragmencie jest rejestr OSC.PLLCTRL, którego opis przedstawiono na rysunku 2. Ustawić w nim musimy źródło sygnału, współczynnik mnożący (zmienna pllfactor jest argumentem funkcji OscPLL), a opcjonalnie możemy częstotliwość sygnału wyjściowego podzielić przez dwa.
1 2 3 4 5 6 7 8 9 |
// wyłączenie PLL OSC.CTRL &= ~OSC_PLLEN_bm; // konfiguracja PLL OSC.PLLCTRL = OSC_PLLSRC_RC2M_gc | // wybór RC 2MHz jako źródło sygnału dla PLL pllfactor; // mnożnik częstotliwości (od 1 do 31) // uruchomienie PLL OSC.CTRL = OSC_PLLEN_bm; // włączenie układu PLL |
Rys. 2. Fragment dokumentacji rejestru PLLCTRL
Podobnie jak w przypadku innych generatorów, poczekać musimy aż sygnał zegarowy się ustabilizuje, poprzez sprawdzanie czy już został ustawiony odpowiedni bit w rejestrze statusowym. Dopiero wtedy możemy przełączyć źródło sygnału taktującego mikrokontroler.
1 2 3 4 5 |
// czekanie na ustabilizowanie się generatora while(!(OSC.STATUS & OSC_PLLRDY_bm)); CPU_CCP = CCP_IOREG_gc; // odblokowanie zmiany źródła sygnału zegarowego CLK.CTRL = CLK_SCLKSEL_PLL_gc; // wybór źródła sygnału zegarowego PLL |
Układ PLL może stracić synchronizację fazy, jeśli sygnał zegarowy będzie zbyt wolny, zbyt szybki lub z jakiegoś powodu będzie niestabilny. Na szczęście mikrokontrolery XMEGA mają możliwość monitorowania układu PLL, podobnie jak w przypadku generatora kwarcowego. W razie stwierdzenia nieprawidłowości, automatycznie uruchomi się wbudowany generator 2 MHz oraz zostanie wygenerowane przerwanie OSC_OSCF_vect.
1 2 3 |
// układ nadzorujący PLL CPU_CCP = CCP_IOREG_gc; // odblokowanie modyfikacji ważnych rejestrów OSC.XOSCFAIL = OSC_PLLFDEN_bm; // włączenie układu detekcji błędu sygnału zegarowego |
Podczas niniejszych ćwiczeń przetaktowaliśmy rdzeń procesora prawie dwukrotnie. Zgodnie z danymi firmy Atmel, układ powinien być taktowany w zakresie od 10 MHz (minimalna częstotliwość wyjściowa PLL) do 32 MHz (maksymalna częstotliwość rdzenia). Ciekawy jestem, czy czytelnicy zauważyli jakieś nieprawidłowości w działaniu mikrokontrolera poza tym zakresem. W moim przypadku wszystko działało bez zarzutu. Mimo to, w normalnych zastosowaniach nigdy nie należy przekraczać dopuszczalnych zakresów podanych przez producenta układu!
Oto kod całego programu, demonstrującego możliwości układu generowania i dystrybucji sygnałów zegarowych.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
#define F_CPU 62000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include "hd44780.h" void Osc2MHz(void) { OSC.CTRL = OSC_RC2MEN_bm; // włączenie oscylatora 2MHz while(!(OSC.STATUS & OSC_RC2MRDY_bm)); // czekanie na ustabilizowanie się generatora CPU_CCP = CCP_IOREG_gc; // odblokowanie zmiany źródła sygnału zegarowego CLK.CTRL = CLK_SCLKSEL_RC2M_gc; // zmiana źródła sygnału zegarowego na RC 2MHz LcdClear(); // czyszczenie wyświetlacza Lcd("RC 2MHz"); // komunikat o uruchomieniu generatora } void Osc32MHz(void) { OSC.CTRL = OSC_RC32MEN_bm; // włączenie oscylatora 32MHz while(!(OSC.STATUS & OSC_RC32MRDY_bm)); // czekanie na ustabilizowanie się generatora CPU_CCP = CCP_IOREG_gc; // odblokowanie zmiany źródła sygnału zegarowego CLK.CTRL = CLK_SCLKSEL_RC32M_gc; // zmiana źródła sygnału zegarowego na RC 32MHz LcdClear(); // czyszczenie wyświetlacza Lcd("RC 32MHz"); // komunikat o uruchomieniu generatora } void OscXtal(void) { // konfiguracja generatora kwarcowego OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | // wybór kwarcu od 12 do 16 MHZ OSC_XOSCSEL_XTAL_16KCLK_gc; // czas na uruchomienie generatora OSC.CTRL = OSC_XOSCEN_bm; // uruchomienie generatora kwarcowego // czekanie na ustabilizowanie się generatora for(uint8_t i=0; i<255; i++) { if(OSC.STATUS & OSC_XOSCRDY_bm) { CPU_CCP = CCP_IOREG_gc; // odblokowanie zmiany źródła sygnału zegarowego CLK.CTRL = CLK_SCLKSEL_XOSC_gc; // wybór źródła sygnału zegarowego na XTAL 16MHz LcdClear(); // czyszczenie wyświetlacza Lcd("XTAL"); // komunikat o uruchomieniu generatora // układ nadzorujący kwarc CPU_CCP = CCP_IOREG_gc; // odblokowanie modyfikacji ważnych rejestrów OSC.XOSCFAIL = OSC_XOSCFDEN_bm; // włączenie układu detekcji błędu sygnału zegarowego return; // wyjście z funkcji jeśli generator się uruchomił } _delay_us(10); } // komunikat w przypadku braku uruchomienia generatora kwarcowego LcdClear(); Lcd("Brak XTAL"); } void OscPLL(uint8_t pllfactor) { // uruchomienie generatora 2MHz i ustawienie go jako źródła zegara OSC.CTRL = OSC_RC2MEN_bm; // włączenie oscylatora 2MHz while(!(OSC.STATUS & OSC_RC2MRDY_bm)); // czekanie na ustabilizowanie się generatora CPU_CCP = CCP_IOREG_gc; // odblokowanie zmiany źródła sygnału zegarowego CLK.CTRL = CLK_SCLKSEL_RC2M_gc; // zmiana źródła sygnału zegarowego na RC 2MHz // wyłączenie PLL OSC.CTRL &= ~OSC_PLLEN_bm; // konfiguracja PLL OSC.PLLCTRL = OSC_PLLSRC_RC2M_gc | // wybór RC 2MHz jako źródło sygnału dla PLL pllfactor; // mnożnik częstotliwości (od 1 do 31) // uruchomienie PLL OSC.CTRL = OSC_PLLEN_bm; // włączenie układu PLL // czekanie na ustabilizowanie się generatora while(!(OSC.STATUS & OSC_PLLRDY_bm)); // przełączenie źródła sygnału zegarowego CPU_CCP = CCP_IOREG_gc; // odblokowanie zmiany źródła sygnału zegarowego CLK.CTRL = CLK_SCLKSEL_PLL_gc; // wybór źródła sygnału zegarowego PLL // układ nadzorujący PLL CPU_CCP = CCP_IOREG_gc; // odblokowanie modyfikacji ważnych rejestrów OSC.XOSCFAIL = OSC_PLLFDEN_bm; // włączenie układu detekcji błędu sygnału zegarowego // wyświetlenie komunikatu LcdClear(); Lcd("PLL "); LcdDec(pllfactor*2); // *2 bo generator RC ma 2MHz Lcd("MHz"); } int main(void) { // zmienna uint8_t pll = 4; // diody PORTE.DIR = PIN0_bm; // dioda LED // przyciski PORTA.DIRCLR = PIN0_bm; // przycisk - RC 2MHz PORTA.PIN0CTRL = PORT_OPC_PULLUP_gc; // podciągnięcie do zasilania PORTE.DIRCLR = PIN5_bm; // przycisk FLIP - RC 32MHz PORTE.PIN5CTRL = PORT_OPC_PULLUP_gc; // podciągnięcie do zasilania PORTE.DIRCLR = PIN6_bm; // przycisk - XTAL PORTE.PIN6CTRL = PORT_OPC_PULLUP_gc; // podciągnięcie do zasilania PORTF.DIRCLR = PIN4_bm; // przycisk - PLL PORTF.PIN4CTRL = PORT_OPC_PULLUP_gc; // podciągnięcie do zasilania // wyświetlacz LCD LcdInit(); // komunikat o źródła sygnału zegarowego LcdClear(); Lcd("RC 2MHz"); // włączenie przerwań sei(); while(1) { PORTE.OUTTGL = PIN0_bm; _delay_ms(50); if(!(PORTA.IN & PIN0_bm)) Osc2MHz(); if(!(PORTE.IN & PIN5_bm)) Osc32MHz(); if(!(PORTE.IN & PIN6_bm)) OscXtal(); if(!(PORTF.IN & PIN4_bm)) { pll++; // zwiększ zmienną pll if(pll > 31) pll = 1; // jeśli pll większe od 31 to ustaw na 1 OscPLL(pll); // funkcja konfigurująca PLL } } } ISR(OSC_OSCF_vect) { // przerwanie w razie awarii oscylatora OSC.XOSCFAIL |= OSC_XOSCFDIF_bm; // kasowanie flagi przerwania LcdClear(); Lcd("Awaria!"); } |
Temat generatorów sygnału zegarowego w mikrokontrolerach XMEGA zajął aż cztery odcinki, to jednak nie wyczerpują one tematu. Oprócz tego mamy do dyspozycji jeszcze:
- układ synchronizujący DFLL,
- zegar czasu rzeczywistego RTC,
- generatory energooszczędne,
- generator sygnału zegarowego do USB,
- kalibrację generatorów.
Te możliwości zostały opisane w książce Tomasza Francuza AVR. Praktyczne projekty , którą szczerze polecam.
|
Dominik Leon Bieczyński