środa, 6 kwietnia 2011

ARM: LPC1114 - Uruchomienie i inicjalizacja

Autor: Deucalion
Redakcja: Dondu

Artykuł jest częścią cyklu: Kurs ARM: Spis treści

Ta część kursu i następne będą już o samym LPC1114 i o jego programowaniu. Od czasu do czasu podłączymy jakieś peryferia, bo przecież ten mikrokontroler musi czymś sterować, a opisywanie peryferii LPC1114 na sucho bez kodu większego pożytku nie przyniesie.




LPC1114 niczym puzzle 

Mikrokontroler składa się z bloków niczym układanka z puzzli , które spełniają określone funkcje. Przyjrzyjmy się schematowi blokowemu.



Z grubsza widać co znajduje się w mikrokontrolerze, jest rdzeń Cortex-M0, pamięci FLASH, RAM i ROM, timery, interfejsy szeregowe, porty, przetwornik ADC... Co niektórych, niepokój mogą wzbudzić dwa bloki - AHB-LITE BUS i AHB TO APB BRIDGE oraz nieoznaczona na tym schemacie magistrala APB (lub PPB). AHB to szybka magistrala zaprojektowana pod kątem dużej przepustowości , która łączy rdzeń z pamięciami i szybkimi peryferiami.

Peryferia które nie potrzebują dużej przepustowości transferu danych są dołączone do magistrali APB. Magistrala APB jest mniej skomplikowana od magistrali AHB kosztem szybkości transferu danych. Obie magistrale połączone są mostem AHB TO APB BRIDGE i każda może być taktowana zegarem o różnej prędkości. Rolą mostu oprócz połączenia magistral AHB i APB jest uaktywnienie odpowiedniego peryferia w zależności od adresu oraz wstrzymanie magistrali AHB na czas potrzebny do dokonania transakcji na APB.

W przypadku tego procesora możemy regulować częstotliwość taktowania tylko magistrali AHB, APB pracuje z tą samą częstotliwością. Istnieją procesory, które mają regulowaną częstotliwości taktowania także magistrali APB. Regulowana częstotliwość zegara również pozwala na redukcję poboru energii przez procesor. Słowo LITE w nazwie AHB-LITE oznacz, że do magistrali może być przyłączony tylko jeden master, w tym przypadku rdzeń jest takim masterem. W praktyce podczas programowania procesora nie będziemy wiedzieć o ich istnieniu poza konfiguracją wspomnianego sygnału taktującego te magistrale.


Pierwsze takty…

Po podaniu zasilania, mikroprocesor wykonuje kilka czynności zanim uruchomi główny program. Na tym rysunku najlepiej to widać:



Z powyższego rysunku widać, że warunkiem poprawnego startu LPC1114 jest osiągnięcie odpowiedniego poziomu napięcia przez źródło zasilania. Jeśli tylko zasilanie przekroczy próg 1.8V, w ciągu 80us zostaje uruchomiony wewnętrzny oscylator 12MHz , z którego będzie taktowany procesor. Po kolejnych około 100us zostaje zwolniony wewnętrzny sygnał RESET i mikroprocesor przechodzi do wykonywania programu bootloadera. Bootloader na uruchomienie głównego programu potrzebuje 55us.

W czasie tych 55us sprawdza kilka warunków, które muszą być spełnione, aby móc uruchomić główny program, oraz wstępnie konfiguruje procesor. Projektując urządzenie należy pamiętać o tym opóźnieniu, i wziąć pod uwagę, że przez kilkaset mikrosekund wyjścia są wejściami.


Co dalej?

Procesor po wstępnym skonfigurowaniu przez bootloader zaczyna wykonywać program użytkownika zawarty w pamięci FLASH. Na początku pamięci FLASH znajduje się tablica wektorów. Wektor to nic innego jak adres wskazujący na jakąś komórkę w pamięci. Prawie wszystkie wektory to adresy funkcji obsługujących przerwania lub wyjątki. Pierwszy wektor w tej tablicy znajdujący się pod adresem 0x00000000 jest adresem wierzchołka stosu i podczas resetu jest ładowany do głównego wskaźnika stosu (są dwa). Drugi wektor spod adresu 0x00000004 ( każdy wektor jest 32 bitowy więc zajmuje 4 bajty) zawiera adres funkcji, od której procesor zaczyna wykonywanie programu i nie chodzi tutaj o funkcję main(). ARM traktuje reset jako wyjątek.



Przerwania i wyjątki.

Między przerwaniami i wyjątkami wielkiej różnicy nie ma, gdyż przerwania też zaliczają się do wyjątków. Wyjątki pochodzą od rdzenia, a przerwania od peryferii. Niektórych wyjątków nie da się wyłączyć i nie są konfigurowalne. Więcej o przerwaniach w kolejnych częściach.



Inicjalizacja

Inicjalizacja mikrokontrolera jest pierwszą czynnością jaką powinien wykonać program. Nie jest to czynność standardowa i zależy od tego z jakich funkcji mikrokontrolera będzie korzystało urządzenie. W niektórych mikrokontrolerach inicjalizację przeprowadza się już na etapie programowania za pomocą tak zwanych FUSE bitów np. w AVR.

W przypadku LPC11xx czegoś takiego nie ma! Wszystko możemy skonfigurować z poziomu programu i w każdej chwili zmienić, dzięki czemu nie ma możliwości zablokowania sobie mikrokontrolera, jak to zdarza się czasami np. w AVR.

Mikrokontroler składa się z bloków, które spełniają określone funkcje i każdy taki blok ma swoją procedurę inicjalizacyjną. Kolejność inicjalizacji w przypadku jednych bloków jest ważna, a w przypadku innych dowolna. Wynika to głównie z zależności pomiędzy blokami i układu, w którym pracuje mikrokontroler, np. przed inicjalizacją interfejsów szeregowych trzeba skonfigurować blok generacji sygnałów zegarowych CGU i porty GPIO.


Teoretycznie inicjalizację mikrokontrolera powinno zacząć się od inicjalizacji watchdoga, jeśli ma być używany w programie, ale tak się składa, że watchdog również korzysta z CGU więc na inicjalizacji tego bloku się teraz skupimy.


CGU – Clock Generation Unit

Blok generacji sygnału zegarowego odpowiedzialny jest za sterowanie źródłami tego sygnału oraz za rozdzielenie go do poszczególnych peryferii. Selektywne sterowanie sygnałem zegarowym doprowadzanym do peryferii pozwala na obniżenie poboru mocy przez mikrokontroler.



Rejestry do sterownia blokiem CGU znajdują się w bloku rejestrów o nazwie System Control, który znajduje się w przestrzeni peryferii, czyli dołączony jest do opisywanej wcześniej magistrali APB. W bibliotece CMSIS, wskaźnik na blok System Control ma nazwę LPC_SYSCON i za pomocą tego wskaźnika będziemy odwoływać się do jego rejestrów.

Przyład:
LPC_SYSCON->PDRUNCFG &= ~SYSOSC_PD;    // Włączenie oscylatora kwarcowego


CGU może być taktowany z trzech źródeł sygnału zegarowego:

  • IRC oscillator - wewnętrzny oscylator RC o częstotliwości 12MHz
  • System oscillator – oscylator kwarcowy do którego można podłączyć rezonatory kwarcowe od 1MHz do 25MHz
  • Watchdog oscillator – wewnętrzny oscylator RC, którego częstotliwość można regulować w zakresie od 7,8kHz do 1,7MHz.


Jako czwarte źródło sygnału zegarowego można uznać pętlę fazową PLL. Pętla PLL do działania wymaga referencyjnego sygnału zegarowego (minimum 10MHz), którym może być IRC oscillator lub System oscillator. Zadaniem PLL jest zwielokrotnienie referencyjnego sygnału zegarowego.

Konfigurując PLL należy pamiętać, aby częstotliwość generowanego sygnału zegarowego nie przekroczyła 50MHz.

Rejestry związane z konfiguracją CGU
SYSPLLCTRL – konfiguracja pętli PLL
SYSPLLSTAT – status pracy pętli PLL
SYSOSCCTRL – konfiguracja oscylatora systemowego
WDTOSCCTRL – konfiguracja oscylatora watchdoga
IRCCTRL – dostrajanie wewnętrznego oscylatora 12MHz
SYSPLLCLKSEL – selekcja źródła zegara dla PLL
SYSPLLCLKUEN – potwierdzenie zmiany źródła zegara dla PLL
MAINCLKSEL – selekcja źródła zegara systemowego
MAINCLKUEN – potwierdzenie zmiany źródła zegara systemowego
SYSAHBCLKDIV – dzielnik zegara taktującego magistralę AHB
SYSAHBCLKCTRL – włączanie taktowania peryferiów
SPI0CLKDIV – dzielnik sygnału taktującego interfejs szeregowy SPI0
SPI1CLKDIV – dzielnik sygnału taktującego interfejs szeregowy SPI1
UARTDIVCLK – dzielnik sygnału taktującego interfejs szeregowy UART
WDTCLKSEL – wybór źródła sygnału taktującego watchdoga
WDTCLKUEN – potwierdzenie zmiany źródła sygnału taktującego watchdoga
WDTCLKDIV – dzielnik sygnału taktującego watchdoga
CLKOUTCLKSEL – selekcja źródła sygnału taktującego wyjście CLKOUT
CLKOUTUEN – potwierdzenie zmiany źródła sygnału taktującego wyjście CLKOUT
CLKOUTCLKDIV – dzielnik sygnału taktującego wyjście CLKOUT
Jest to całkiem pokaźna grupa rejestrów sterująca tylko sygnałem zegarowym, ale nie należy się przejmować tą ilością, ponieważ niektóre rejestry zawierają jeden lub kilka bitów, a poza tym, zazwyczaj używa się ich tylko podczas inicjalizacji procesora i często nie wszystkie są używane.

OK, teraz do sprawy podejdziemy bardziej praktycznie, bo opis rejestrów jest w User Manualu procesora więc nie ma sensu go przepisywać. Postawimy sobie dwa zadania. Obydwa zadania będą dotyczyć inicjalizacji zegara systemowego w procesorze. Celem pierwszego zadania będzie wygenerowanie na porcie P0.2 przebiegu o częstotliwości 1Hz na razie bez użycia pętli PLL, ale z wykorzystaniem oscylatora systemowego.



Krok po kroku…

Po włączeniu zasilania procesor jest taktowany z wewnętrznego oscylatora 12MHz. Musimy przełączyć procesor na oscylator systemowy, ale przed przełączeniem trzeba ten oscylator włączyć. Oscylator włączamy poprzez wyzerowanie bitu SYSOSC_PD (5 bit – 0x20) w rejestrze PDRUNCFG.

LPC_SYSCON->PDRUNCFG &= ~SYSOSC_PD;

Po włączeniu oscylatora trzeba chwilę poczekać w celu ustabilizowania się jego pracy. Można to zrealizować poprzez pętle odczytującą jakiś rejestr.

Zostanie odczytany 1000 razy rejestr PDRUNCFG

for( int i = 0; i < 1000; i++)
{
    LPC_SYSCON->PDRUNCFG;
}

Zegar systemowy nie może być dołączony bezpośrednio do oscylatora systemowego. Po drodze jest jeszcze jeden multiplekser, którego zadaniem jest przełączanie źródła zegara referencyjnego dla PLL między wewnętrznym oscylatorem IRC i oscylatorem systemowym. Do wyjścia tego multipleksera można dołączyć również zegar systemowy. Tym sposobem mamy dwie możliwości dołączenia wewnętrznego oscylatora IRC do zegara systemowego.

Przełączenie tego multipleksera na oscylator systemowy przebiega dwuetapowo. W pierwszej kolejności należy przełączyć multiplekser na oscylator systemowy, a w drugiej potwierdzić tą operację.
Przełączenie na oscylator systemowy realizujemy poprzez ustawienie rejestru SYSPLLCLKSEL na 1. W dołączonym do projektów pliku syscon.h znajdują się deklaracje bitów rejestrów należących do bloku System Control. Deklaracji SYSPLLCLKSEL_SYSOSC przypisania jest wartość 1.

LPC_SYSCON->SYSPLLCLKSEL = SYSPLLCLKSEL_SYSOSC;

Potwierdzenie przełączenia wymaga przestawienia bitu ENA (bit 0 => 0x01) w rejestrze SYSPLLCLKUEN z wartości 0 na 1. Dla pewności bit ten należy wcześniej wyzerować


LPC_SYSCON->SYSPLLCLKUEN = 0;
LPC_SYSCON->SYSPLLCLKUEN = SYSPLLCLKUEN_ENA;


Teraz możemy się przełączyć na oscylator systemowy ( w tym przypadku 10MHz) i wyłączyć wewnętrzny oscylator.
Przełączenie źródła taktowania na zewnętrzny oscylator również przebiega dwuetapowo. W pierwszej kolejności należy ustawić rejestr MAINCLKSEL na 1

LPC_SYSCON->MAINCLKSEL = MAINCLKSEL_PLLIN;


i potwierdzić zmianą bitu ENA (bit 0 => 0x01) w rejestrze MAINCLKUEN z 0 na 1

LPC_SYSCON->MAINCLKUEN = 0;
LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA;

Teraz nasz procesor pracuje już z oscylatora systemowego i możemy wyłączyć wewnętrzny oscylator. Wyłączamy go w rejestrze PDRUNCFG poprzez ustawienie bitu IRC_PD (bit 1 => 0x02)

LPC_SYSCON->PDRUNCFG |= IRC_PD;


Teoretycznie na tym kończy się te zadanie, ale dla sprawdzenia wygenerujemy 1Hz na porcie P0.2. Kod programu realizujący to sprawdzenie wykracza poza tę część kursu więc pozostanie bez opisu.

Cały kod realizujący to zadanie:

#include "LPC11xx.h"
#include "syscon.h"

//***************************************************************************
//                       Funkcja inicjalizująca procesor
//***************************************************************************
void
LPC_Init( void )
{
    // Włączenie oscylatora systemowego
    LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_SYSOSC_PD;

    // Zwłoka na ustabilizowanie oscylatora poprzez wielokrotny odczyt rejestru
    for( int i = 0; i < 1000; i++)
    {
        LPC_SYSCON->PDRUNCFG;
    }

    // Przełączenie źródła sygnału taktującego PLL na oscylator systemowy
    LPC_SYSCON->SYSPLLCLKSEL = SYSPLLCLKSEL_SYSOSC;

    // Potwierdzenie przełączenia źródła taktowania
    LPC_SYSCON->SYSPLLCLKUEN = 0;
    LPC_SYSCON->SYSPLLCLKUEN = SYSPLLCLKUEN_ENA;

    // Przełączenie źródła zegara systemowego
    LPC_SYSCON->MAINCLKSEL = MAINCLKSEL_PLLIN;

    // Potwierdzenie przełączenia źródła taktowania
    LPC_SYSCON->MAINCLKUEN = 0;
    LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA;

    // Wyłączenie wewnętrznego oscylatora
    LPC_SYSCON->PDRUNCFG |= PDRUNCFG_IRC_PD;

    //~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-//
    // Dodatkowe inicjalizacje nie związane z zadaniem  //
    //~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-//
    
    #define LED_PIN (1<<2)
    
    // Włączenie taktowania portów i bloku konfiguracji portów
    LPC_SYSCON->SYSAHBCLKCTRL |= AHBCLKCTRL_IOCON | AHBCLKCTRL_GPIO;

    // Port P0.2 jako wyjście
    LPC_GPIO0->DIR |= LED_PIN;

    // Konfiguracja timera systemowego SysTick

    // Przerwanie co 500ms (dla 10MHz)
    SysTick->LOAD = 5000000UL;

    // Włączenie timera
    SysTick->CTRL = 7;

    // Odblokowanie przerwań od timera SysTick
    NVIC_EnableIRQ(SysTick_IRQn);
}


//***************************************************************************
//                    Funkcja obsługi przerwania od timera SysTick
//***************************************************************************
void 
SysTick_Handler( void )
{
    // Zmiana stanu portu P0.2 na przeciwny
    LPC_GPIO0->DATA ^= LED_PIN;
}


//***************************************************************************
//                            Funkcja pętli głównej
//***************************************************************************

int
main( void )
{
    LPC_Init();

    while( 1 )
    {
        // Kod pętli głównej
    }
}
Plik z projektem zadania: projekt_zad1.zip

Zadanie drugie będzie rozszerzeniem pierwszego zadania. W tym zadaniu skonfigurujemy PLL na 50MHz i przełączymy procesor na te źródło sygnału zegarowego.

Różnica w stosunku do pierwszego zadania polega na tym, że przed dołączeniem zegara systemowego należ PLL skonfigurować, włączyć i poczekać na synchronizację pętli. Dodatkowo należy ustawić czas dostępu do pamięci FLASH, gdyż pamięć ta może być odczytywana z maksymalną prędkością 20MHz. Zanim to wszystko zrobimy, kilka słów o samej pętli PLL.


Pętla PLL

Pętla PLL jest generatorem VCO (generator sterowany napięciem), którego częstotliwość może zmieniać się w granicach od 156MHz do 320MHz. Częstotliwość ta za pomocą dzielnika jest zmniejszana od 2 do 16 razy i dopiero wtedy może być wykorzystana jako zegar systemowy, dla przykładu, gdy częstotliwość generatora wynosi 200MHz musi ona zostać podzielona przez co najmniej 4 co da nam w efekcie 50MHz, czyli maksimum dla tego procesora.




No dobrze, ale jak ustawić częstotliwość generatora właśnie na te przykładowe 200MHz? Bezpośrednio nie mamy na to wpływu, generator przestrajany jest napięciem, a my nie mamy możliwości regulacji tego napięcia. To pętla PLL sama ustala to napięcie. Napięcie regulowane jest w układzie, który porównuje fazy dwóch sygnałów zegarowych. Jeden sygnał zegarowy jest sygnałem referencyjnym, który musi być bardzo stabilny i mieścić się w pewnym zakresie częstotliwości. Dla tego procesora ten zakres wynosi od 10MHz do 25MHz.

Drugi sygnał zegarowy jest zwrotnym sygnałem pochodzącym z tego generatora podzielonym przez dzielnik, czyli dokładnie ten sam, który wykorzystywany jest do taktowania procesora. Jeśli fazy tych dwóch sygnałów nie zgadzają się ze sobą, pętla PLL reguluje napięcie powodując zmiany częstotliwości generatora, a tym samym częstotliwości i fazy sygnału zwrotnego.

Gdy częstotliwości i fazy sygnałów zegarowych zrównają się, wtedy pętla PLL jest zsynchronizowana i ustawiany jest bit LOCK (bit 0) w rejestrze statusu pętli PLL - SYSPLLSTAT . Dopiero po zsynchronizowaniu się pętli można z niej taktować procesor.


Zadanie 2.

W pierwszej kolejności należy skonfigurować pętlę PLL w rejestrze SYSPLLCTRL. Cała konfiguracja polega na odpowiednim ustawieniu dwóch dzielników. Należy zacząć od obliczenia dzielnika M, czyli tego, który dzieli zegar z którego taktowany jest procesor, a w drugiej kolejności dobrać wartość dzielnika P tak, aby częstotliwość VCO nie wychodziła poza dozwolony zakres. Dzielnik M może dzielić przez wartości z zakresu od 1 do 32, a dzielnik P przez 2, 4, 8 lub 16. Wpisywane wartości do rejestru SYSPLLCTRL nie odpowiadają rzeczywistym wartościom, z tego powodu w pliku syscon.h dostępne są deklaracje, które ułatwiają konfigurację rejestru SYSPLLCTRL. Dla dzielnika M należy posługiwać się deklaracjami PLL_M_DIV_x, gdzie x przyjmuje wartości od 1 do 32, a dla dzielnika P deklaracjami PLL_P_DIV_y, gdzie y przyjmuje jedną z czterech wartości - 2, 4, 8, 16 ( PLL_M_DIV_5, PLL_P_DIV_4).

Wartości dzielników zaczynamy od ustalenia dzielnika M. Wartość dzielnika M ustalamy poprzez podzielnie częstotliwości jaką chcemy taktować procesor (np. 50MHz) przez częstotliwość referencyjną dostarczaną do pętli PLL (np kwarc 10MHz).


Z powyższego wyliczenia wynika, że do dzielnika M należy wpisać PLL_M_DIV_5. Nie można wpisać bezpośrednio liczby 5, ponieważ rzeczywistą liczbą wpisywaną do tego dzielnika jest wartość x-1, czyli 4.

Ustalając wartość dzielnika M należy pamiętać o tym, że częstotliwość taktowania procesora może być tylko wielokrotnością częstotliwości referencyjnej oraz, że częstotliwość taktowania procesora nie może przekraczać maksymalnej częstotliwości z jaką może pracować procesor deklarowanej przez producenta (50MHz).

Wartość dzielnika P ustalamy poprzez znalezienie takiej liczby z czterech możliwych ( 2, 4, 8, 16), która po pomnożeniu przez oczekiwaną częstotliwość pracy procesora ( np. 50MHz) da nam wartość z przedziału od 156MHz do 320MHz. Na przykład dla częstotliwości 50MHz jedyną taką liczba jest 4, więc do dzielnika P należy wpisać PLL_P_DIV_4. W rzeczywistości do dzielnika P wpisywane są liczby z zakresu od 0 do 3. Jeśli zdarzy się sytuacja, że żadne wartości dzielników nie pozwalają na uzyskanie oczekiwanej częstotliwości, należ spróbować dobrać inny rezonator kwarcowy.

Wartości obu dzielników wpisujemy do rejestru SYSPLLCTRL:

LPC_SYSCON->SYSPLLCTRL = PLL_M_DIV_5 | PLL_P_DIV_4;


Następnie uruchamiamy pętlę PLL w rejestrze PDRUNCFG poprzez wyzerowanie bitu SYSPLL_PD (7 bit = 0x80)

LPC_SYSCON->PDRUNCFG &= ~SYSPLL_PD;

i czekamy aż się zsynchronizuje co będzie zasygnalizowane ustawieniem bitu LOCK (bit 0) w rejestrze SYSPLLSTAT

while (!(LPC_SYSCON->SYSPLLSTAT & SYSPLLSTAT_LOCK));

Po zsynchronizowaniu się pętli nie można jeszcze przełączyć źródła taktowania procesora, trzeba zmienić czas dostępu do pamięci FLASH. Czas dostępu zmieniamy poprzez konfigurację bitów FLASHTIM[1:0] w rejestrze LPC_FLASHCFG w zależności od częstotliwości zegara systemowego według poniższej zasady:
[00] - SYSCLK do 20MHz (1 cykl zegarowy)
[01] – SYSCLK do 40MHz (2 cykle zegarowe)
[10] – SYSCLK powyżej 40MHz (3 cykle zegarowe)

Uwaga!
Pozostałe bity w rejestrze LPC_FLASHCFG mimo, że są niewykorzystane nie mogą być zmieniane, tzn. trzeba je zapisać z powrotem bez zmian.

Deklaracje i definicje dotyczące rejestru FLASHCFG znajdują się pliku flashcon.h
Do konfiguracji czasu dostępu należy używać poniższych deklaracji:
#define FLASHTIM_20MHz      0
#define FLASHTIM_40MHz      1
#define FLASHTIM_50MHz      2

Rejestr LPC_FLASHCFG należy do bloku kontrolera pamięci FLASH i ze względu, że jest jedynym rejestrem z tej grupy dostęp do niego jest bezpośredni.

LPC_FLASHCFG =  (LPC_FLASHCFG & ~FLASHTIM_MASK) | FLASHTIM_50MHz;   //  >40MHz

Dopiero po skonfigurowaniu czasu dostępu do pamięci FLASH można przełączyć źródło taktowania procesora na pętlę PLL ustawiając rejestr MAINCLKSEL i potwierdzając w rejestrze MAINCLKUEN.
LPC_SYSCON->MAINCLKSEL = MAINCLKSEL_PLLOUT; 
LPC_SYSCON->MAINCLKUEN = 0;
LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA;

Cały kod inicjalizacji pętli PLL i czasu dostępu do pamięci FLASH.

#include "LPC11xx.h"
#include "syscon.h"
#include "flashcon.h"

//***************************************************************************
//                       Funkcja inicjalizacja procesora
//***************************************************************************
void
LPC_Init( void )
{
    // Włączenie oscylatora systemowego
    LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_SYSOSC_PD;

    // Zwłoka na ustabilizowanie oscylatora poprzez wieleokrotny 
    // odczyt rejestru
    for( int i = 0; i < 1000; i++)
    {
        LPC_SYSCON->PDRUNCFG;
    }

    // Przełaczenie źródła sygnału taktującego PLL na zew. oscylator
    LPC_SYSCON->SYSPLLCLKSEL = SYSPLLCLKSEL_SYSOSC;

    // Potwierdzenie przełączenia źródła taktowania poprzez wygenerowania 
    // zmiany bitu z 0 na 1
    LPC_SYSCON->SYSPLLCLKUEN = 0;
    LPC_SYSCON->SYSPLLCLKUEN = SYSPLLCLKUEN_ENA;

    // Konfiguracja dzielników PLL
    LPC_SYSCON->SYSPLLCTRL = PLL_M_DIV_5 | PLL_P_DIV_4;

    // Uruchominie pętli PLL
    LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_SYSPLL_PD;

    // Oczekiwanie na synchronizacje pętli PLL
    while(!(LPC_SYSCON->SYSPLLSTAT & SYSPLLSTAT_LOCK));

    // Konfiguracja dostępu do pamięci FLASH (3 cykle)
    LPC_FLASHCFG = (LPC_FLASHCFG & ~FLASHTIM_MASK) |FLASHTIM_50MHz;

    // Przełączenie źródła zegara systemowego na PLL
    LPC_SYSCON->MAINCLKSEL = MAINCLKSEL_PLLOUT;

    // Potwierdzenie przełączenia źródła taktowania poprzez wygenerowania
    // zmiany bitu z 0 na 1
    LPC_SYSCON->MAINCLKUEN = 0;
    LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA;

    // Wyłączenie wewnętrznego oscylatora
    LPC_SYSCON->PDRUNCFG |= PDRUNCFG_IRC_PD;

    //~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-//
    // Dodatkowe inicjalizacje nie związane z zadaniem  //
    //~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-//

    #define LED_PIN (1<<2)

    // Włączenie taktowania portów i bloku konfiguracji portów
    LPC_SYSCON->SYSAHBCLKCTRL |= AHBCLKCTRL_IOCON | AHBCLKCTRL_GPIO;

    // Port P0.2 jako wyjście
    LPC_GPIO0->DIR |= LED_PIN;

    // Konfiguracja timera systemowego SysTick

    // Przerwanie co 250ms (dla PLL = 50MHz)
    SysTick->LOAD = 12500000UL;

    // Włączenie timera
    SysTick->CTRL = 7;

    // Odblokowanie przerwań od timera SysTick
    NVIC_EnableIRQ(SysTick_IRQn);
}


//**************************************************************************
//                    Funkcja obsługi przerwania od timera SysTick
//**************************************************************************
void 
SysTick_Handler( void )
{
    static int cnt;

    // Zmiana stanu portu P0.2 na przeciwny co 500ms
    LPC_GPIO0->MASKED_ACCESS[LED_PIN] = ++cnt << 1;
}


//**************************************************************************
//                            Funkcja pętli głównej
//**************************************************************************
int
main( void )
{
    LPC_Init();

    while( 1 )
    {
        // Kod pętli głównej
    }
}
Plik z projektem:  projekt_zad2.zip


Uwaga. 
Pliki w projektach ewoluują więc rada taka, aby przy okazji każdej następnej części kursu nie posługiwać się projektami ze starszych części kursu.


Ćwiczenia:
1. Mając dołączony kwarc 10MHz skonfiguruj częstotliwość taktowania procesora na 30MHz
2. Skonfiguruj procesor do pracy na częstotliwości bliskiej 43MHz
3. Skonfiguruj procesor do pray z oscylatora watchdoga ustawionego na 1,7kHz

Możecie przysyłać całe projekty z rozwiązanymi ćwiczeniami: adres email znajdziesz tutaj
Poprawne i najlepiej opracowane umieścimy na blogu.

Kurs ARM: Spis treści

3 komentarze:

  1. Piszesz "Oscylator włączamy oprzez wyzerowanie bitu SYSOSC_PD (5 bit – 0x20) w rejestrze PDRUNCFG."
    W Twoich includach jest
    #define PDRUNCFG_SYSOSC_PD 0x00000020
    czyli binarnie 00100000
    Czyli LPC_SYSCON->PDRUNCFG &= ~SYSOSC_PD; moim zdaniem zeruje 6 bit

    Czy mam rację ?


    OdpowiedzUsuń
  2. Wszystko zależy od tego jak liczysz bity. Ja i dokumentacja liczymy od 0 a nie od 1. Jeśli zajrzysz do dokumentacji to bit ten jest oznaczony jako 5. Jeśli zaczniesz liczyć o 0 to będziesz mógł korzystać z przesunięć bitowych w deklaracjach (1<<bit). (1<<5) == 00100000 == 0x20

    OdpowiedzUsuń
  3. Nie nadaje się dla początkuącego. Napisane w stylu bardzo badziewnym. Proszę o link do czegoś bardziej profesjonalnego.

    OdpowiedzUsuń