Mikrokontrolery AVR XMEGA w praktyce, część 7. Sygnały zegarowe
W mikrokontrolerach ATmega i ATtiny układ dystrybucji sygnałów zegarowych był tak prosty, że wręcz aż prymitywny. W szczególności w procesorach starej generacji, takich jak ATmega8, mogliśmy wybrać źródło sygnału zegarowego przy pomocy fusebitów i podczas pracy procesora w żaden sposób nie można było go zmienić. Ponadto, błędne ustawienie fusebitów mogło prowadzić do zablokowania procesora. Najwyższy czas, by porzucić poczciwą ósemkę i przejść na mikrokontrolery nowej generacji!
W XMEGA układ zegarowy jest zdecydowanie bardziej rozbudowany. Mamy do dyspozycji różne źródła sygnału zegarowego, takie jak wbudowany generator szybki 32 MHz, energooszczędny 32 kHz oraz normalny 2 MHz, który uruchamia się zawsze po włączeniu zasilania. Możemy te częstotliwości podzielić preskalerem lub pomnożyć wbudowanym układem PLL. Oprócz tego, możemy oczywiście podłączyć różne kwarce, a w razie uszkodzenia kwarcu, procesor samoczynnie przełączy się na wbudowany generator. Mało tego – podczas pracy możemy zmieniać nie tylko częstotliwość zegara, ale również źródło sygnału. Różne peryferia mogą być taktowane różnymi zegarami, a niektóre z nich mogą pracować nawet z częstotliwością 128 MHz!
Uproszczony schemat układu dystrybucji sygnałów zegarowych przedstawiono na rysunku 1. Po wybraniu jednego z pięciu dostępnych źródeł, mamy do dyspozycji aż trzy preskalery, umożliwiające taktowanie poszczególnych peryferiów mikrokontrolera różnymi zegarami. CLKCPU to zegar dla rdzenia procesora i może mieć maksymalnie 32MHz. CLKPER taktuje większość peryferiów. CLKPER2 i CLKPER4 służą do taktowania peryferiów zdolnych do pracy z zegarem szybszym od CLKCPU. Oprócz tego, mamy jeszcze osobne zegary dla RTC i USB, jeśli mikrokontroler jest wyposażony w te peryferia.
Rys. 1. Układ dystrybucji sygnałów zegarowych
Sposób wyboru źródła sygnału zegarowego sprowadza się do trzech punktów:
- Konfiguracja i uruchomienie generatora,
- Oczekiwanie na stabilizację generatora,
- Przełączenie źródła.
W tej części kursu napiszemy kilka funkcji, umożliwiających przełączenie źródła sygnału taktującego oraz obserwację efektów tej zmiany przy pomocy migającej diody i wyświetlacza LCD ze sterownikiem HD44780. Wykorzystamy wewnętrzny generator RC 2 MHz, 32 MHz, a także zewnętrzny generator kwarcowy i układ PLL. Schemat układu, wykorzystywanego podczas ćwiczeń, przedstawiono na rysunku 2, a jego zdjęcie jest na rysunku 3.
Rys. 2. Schemat układu demonstracyjnego
Rys. 3. Układ demonstracyjny zbudowany na płytce stykowej
Zacznijmy od przeanalizowania funkcji main().
#define F_CPU 62000000UL #include#include #include #include "hd44780.h" 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łe 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 } } }
Zwróćmy uwagę na funkcję opóźniającą _delay_ms(50); Co w niej jest nie tak? Funkcja powoduje, że procesor kręci się w pustej pętli nic nie robiąc, aż upłynie żądany czas. Jednak funkcja _delay_ms() oblicza ilość potrzebnych cykli na podstawie definicji #define F_CPU 62000000UL. W przypadku kiedy częstotliwość taktowania się zmienia, to pamiętajmy, że standardowe funkcje opóźniające nie uwzględniają aktualnej częstotliwości, w związku z czym odmierzony czas nie będzie prawidłowy. Zaobserwujemy ten problem w naszym programie testowym – dioda podłączona do E0 będzie mrugać z różną częstotliwością, mimo że w pętli głównej jest _delay_ms(50) ze stałym argumentem równym 50.
Pliki źródłowe omawianego programu można pobrać na dole strony – będziemy używać biblioteki do obsługi wyświetlacza autorstwa Radosława Kwietnia, którą opisałem w 5 części kursu. W kolejnych odcinkach przedstawię, jak działają poszczególne generatory i jak skonfigurować ich rejestry kontrolne.
Dominik Leon Bieczyński