Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.
Pokazywanie postów oznaczonych etykietą Mikrokontrolery ARM. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Mikrokontrolery ARM. Pokaż wszystkie posty

środa, 6 kwietnia 2011

Zaawansowana obsługa klawiatury ARM, AVR, PIC i inne

Autor: Deucalion
Redakcja: Dondu

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


Tym razem artykuł prezentujący kod sterownika w języku C, służący do zaawansowanej obsługi klawiatury na praktycznie dowolny mikrokontroler wraz z przykładem dla LPC1114.

Oprócz zamieszczenia kodów źródłowych, szczegółowo zostały opisane funkcje oraz przedstawione zostały przykłady jak z tych funkcji korzystać.

Możliwości sterownika:
  • debouncing czyli eliminacja drgań styków,
  • dynamiczna autorepetycja z możliwością blokady i zmiany międzyczasów,
  • dowolny typ klawiatury – pojedyncze klawisze, klawiatura matrycowa,
  • obsługa naciśnięć kombinacji klawiszy,
  • pomiar czasu wciśnięcia klawisza lub kombinacji,
  • implementacja niezależna od typu procesora. Można więc dostosować niniejszą bibliotekę także do innych mikrokontrolerów np. AVR, PIC, itp.


Wszelkie pytania dot. tej biblioteki możesz zadawać na forum w temacie:
MegaSuperDebounce - Biblioteka zaawansowanej obsługi przycisków AVR, ARM PIC ...



Biblioteka

Poniżej pliki keyb.h i keyb.c z programem sterownika klawiatury (znajdziesz je także w spakowanym pliku na końcu artykułu).

MegaSuperDebounce ™


keyb.h


keyb.c



Definiowanie klawiszy

Do przedstawionego kodu należy dopisać własną wersję funkcji unsigned int GetKeybSample( void ). Funkcja ma zwracać stan klawiszy, np. jeśli klawiatura posiada 4 klawisze, to ich stan ma znajdować się w zwracanej wartości i wciśnięte klawisze mają reprezentować ustawione bity. Nie ma znaczenia, który bit będzie reprezentował dany klawisz.

Inną czynnością jest przyporządkowanie określonym bitom odpowiednich klawiszy w pliku keyb.h. Dla przykładu przypuśćmy, że klawisze znajdują się na pinach PD1, PD3, PD6, PD7 i tym klawiszom przypisane są następujące funkcje PD1 - KEY_UP, PD3 – KEY_ENTER, PD6 – KEY_DOWN, PD7 – KEY_ESC. Nasze deklaracje powinny wyglądać w ten sposób:

Tych deklaracji będziemy używać w naszym programie. W tym przypadku funkcja GetKeybSample( ) może wyglądać w ten sposób:


Kolejną czynnością jaką musimy zrobić to wywoływać gdzieś w programie funkcję KeybProc() dokładnie co 10ms. Można to zrobić z przerwania lub gdzieś w głównej pętli programu – pełna dowolność. Jeśli zdecydujemy się na wywoływanie z przerwania należy wziąć pod uwagę dwa fragmenty kody z tej biblioteki (opis w komentarzach), które muszą być wykonywane atomowo, czyli nieprzerywalnie oraz funkcja GetKeybSample() powinna wykonywać się możliwie jak najszybciej. To wszystko co trzeba na ten moment zrobić, aby móc korzystać w programie z tej biblioteki. Teraz kilka przykładów jak jej używać.



1. Reakcja na naciśnięcie dowolnego klawisza



Funkcja GetKeys() zwraca stan klawiatury, ale tylko co czas określony w tabeli czasów autorepetycji, czyli standardowo po naciśnięciu przycisku po 30ms, 1030ms, 1530ms, 2030ms, 2530ms, 3030ms, 3130ms, 3230ms, 3330ms itd. co kolejne 100ms, aż do jakiejkolwiek zmiany stanu klawiatury.



2. Reakcja na naciśnięcie wybranego klawisza



3. Reakcja na naciśnięcie dowolnego z dwóch klawiszy lub ich kombinacji



4. Reakcja w zależności od wciśniętego przycisku lub ich kombinacji


Przy braku możliwości wyłączenia autorepetycji i dłuższym przytrzymaniu klawisza mogłoby dojść do sytuacji gdzie stracilibyśmy kontrole nad urządzeniem.
Co zrobić, jeśli nie chcemy aby w danym momencie działa autorepetycja, np. kolejne naciśnięcia klawisza KEY_ENTER powodują wejście i wyjście z menu lub kolejne wybieranie podmenu? W takim przypadku należy skorzystać z funkcji ClrKeyb().

Funkcja jako parametr przyjmuje dwie zdeklarowane wartości KBD_LOCK i KBD_NOLOCK. KBD_LOCK powoduje zablokowanie klawiatury, aż do momentu zwolnienia wszystkich klawiszy i analogicznie KBD_NOLOCK nie powoduje takiego zablokowania, wszystko zaczyna się od nowa, tak jak od naciśnięcia klawisza.

Funkcja ClrKeyb() w ogólnej postaci służy do resetowania klawiatury. Może się tak zdarzyć, że program będzie wykonywał czasochłonną operację trwająca kilka lub więcej sekund. Trzymanie w tym czasie wciśniętego klawisza powoduje naliczanie czasów autorepetycji. Gdy program wznowi pobieranie stanu klawiatury może dojść do takiego zachowania jakby został wielokrotnie naciśnięty przycisk. Można temu zapobiec poprzez zresetowanie klawiatury po wykonaniu takiej czasochłonnej operacji. Użycie funkcji ClrKeyb( KBD_LOCK) w takiej postaci powoduje również zatrzymanie autorepetycji.



5. Reakcja w zależności od wciśniętego przycisku lub ich kombinacji z zatrzymaniem autorepetycji na wybranych klawiszach.



6. Sprawdzenie czy naciśnięty dowolny klawisz niezależnie od czasów autorepetycji, ale z uwzględnieniem eliminacji drgań styków.


Funkcja unsigned int IsKey( unsigned int mask ) zwraca zamaskowany stan klawiszy. Jako parametr przyjmuje maskę, która przysłania stany nieistotnych klawiszy. Jeśli wywołamy funkcję w postaci IsKey( KEY_ENTER ) to funkcja zwróci tyko stan klawisza KEY_ENTER i jeśli klawisz ten będzie wciśnięty zostanie również zwrócona wartość KEY_ENTER. Jeśli chcemy sprawdzić stan kilku klawiszy wywołujemy tą funkcję podając ich nazwy: IsKey( KEY_ENTER | KEY_ESC )



7. Sprawdzenie czy naciśnięty którykolwiek ze wskazanych klawiszy niezależnie od czasów autorepetycji, ale z uwzględnieniem eliminacji drgań styków.


Funkcja unsigned int IsKeyPressed( unsigned int mask ) działa podobnie do funkcji IsKey() z tą tylko różnicą, że nie jest uwzględniony czas drgań styków tzn. funkcja zwraca stan klawiszy zgodny z podaną maską, jeśli w tym czasie będą drgania styków to funkcja również je przeniesie do programu.



8. Reakcja programu w zależności od czasu naciskania klawisza.


Wersja druga:


Funkcja unsigned int KeysTime( void ) zwraca czas jaki upłynął od ostatniej zmiany na klawiaturze, jeśli żaden klawisz nie jest wciśnięty funkcja zwraca 0. Jednostką czasu jest 10ms czyli jeśli chcemy sprawdzić czy klawisz jest wciśnięty dłużej niż 1s zapisujemy to w taki oto sposób:


Autorepetycja jest to funkcjonalność, która symuluje wciskanie klawisza co określony czas jeśli klawisz jest cały czas wciśnięty. Dzięki temu mamy możliwość „wciśnięcia” klawisza kilka, kilkanaście a nawet kilkadziesiąt razy w ciągu sekundy. Opcja ta przydaje się gdy mamy np. do przestawienia jakąś wartość o kilkadziesiąt lub kilkaset jednostek za pomocą tylko dwóch klawiszy. Odstępy czasów mogą być stałe lub zmieniać się w czasie.

W przypadku tej biblioteki do obsługi klawiatury mamy sporą dowolność. Sterownik posiada domyślne odstępy czasów, ale można również w trakcie programu je zmieniać. Po naciśnięciu klawisza, domyślnie za pomocą funkcji GetKeys() kody klawiszy udostępniane są po 30ms, 1030ms, 1530ms, 2030ms, 2530ms, 3030ms, 3130ms, 3230ms, 3330ms itd. co kolejne 100ms, aż do jakiejkolwiek zmiany stanu klawiatury. Za pomocą funkcji void KeybSetAutoRepeatTimes( unsigned short * AutoRepeatTab ) można sterownikowi dostarczyć tablicę opisującą inną sekwencję odstępów czasów.

Przykładowa tablica:

i przekazanie jej do sterownika:

Efektem w/w przykładu będzie symulacja naciśnięć klawisza w czasach 30ms, 530ms, 560ms, 590ms itd. co 30ms, co da nam około 33 pseudo naciśnięcia na sekundę. Tablica może mieć dowolną ilość odstępów czasów nie mniejszą niż 2. Pierwsza, przedostatnia i ostatnia pozycja mają specyficzne znaczenie. Pierwsza nie powinna być mniejsza niż 3 ( czyli 30ms), jest to czas na eliminację drgań styków. Ostatnia musi mieć wartość 0 i sygnalizuje koniec tablicy czasów. Natomiast wartość przedostatniej jest czasem autorepetycji nieskończonej. Należy pamiętać, że jednostką czasu w tablicy jest 10ms.


Podsumowanie

Aby móc zastosować tą bibliotekę w swoim projekcie należy:
  • skopiować i dołączyć do drzewa projektu pliki keyb.h i keyb.c,
  • utworzyć odpowiednie deklaracje w pliku keyb.h lub dołączyć własny plik z takimi deklaracjami,
  • napisać własną wersję funkcji GetKeybSample( ), która jest pomostem między sterownikiem a sprzętem,
  • umieścić w programie wywołanie funkcji KeybProc(). Funkcja musi być wywoływana co 10ms.


Wszelkie pytania dot. tej biblioteki możesz zadawać na forum w temacie:
MegaSuperDebounce - Biblioteka zaawansowanej obsługi przycisków AVR, ARM PIC ...





Przykład implementacji dla LPC1114.

Poniżej przykładowy film prezentujący działanie tego sterownika klawiatury oraz cały projekt dla procesora LPC1114 utworzony na bazie kursu o ARM.

Plik: Keyb.zip









Podziękowania

Ode mnie podziękowania za podzielenie się biblioteką z początkującymi. Dzięki takim osobom jak Deucalion mają oni znacznie łatwiej.  Mam nadzieję, że korzystający z tej biblioteki mega superdebounce przyłączą się i miłym słowem skomentują dobry uczynek kolegi :D :D

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

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

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

ARM: LPC1114 - Podłączenie

Autor: Deucalion
Redakcja: Dondu

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

W tej części zdobędziemy niezbędne minimum wiedzy jaka potrzebna jest nam do uruchomienia naszego mikrokontrolera, a więc jak go podłączyć i zaprogramować.

Teoretycznie do ożywienia naszego mikrokontrolera wystarczy podłączenie do niego napięcia zasilania. LPC1114 może pracować w zakresie napięć od 1,8V do 3,6V. Są to graniczne wartości, których lepiej unikać i przyjąć, że bezpiecznie jest zasilać napięciem z przedziału od 2V do 3,4V.

Każdy układ cyfrowy na zasilaniu powinien mieć kondensatory blokujące (filtrujące) zakłócenia. W tym przypadku jest dokładnie tak samo. LPC1114 posiada dwa piny zasilające (VDD) i dwie masy (VSS) - obudowa LQFP48. Obydwa piny zasilające muszą być podłączone do zasilania i każdy z tych pinów powinien mieć swój kondensator blokujący o wartości 100nF.




Przy wyższych częstotliwościach taktowania procesora (>25MHz) można równolegle ze 100nF dołożyć kondensatory tego samego typu o wielokrotnie mniejszych wartościach np. 10nF, co będzie skutkować jeszcze lepszymi parametrami zasilania mikrokontrolera i tym samym zakłócenia generowane przez mikrokontroler będą miały mniejszy wpływ na resztę układu.

Więcej na temat filtrowania zakłóceń możesz dowiedzieć się tutaj: Zasilanie mikrokontrolera





Po podaniu zasilania mikrokontroler zaczyna pracować mimo, że nie ma w nim jeszcze naszego programu. To co zaczyna w nim pracować to bootloader. Jest to program umożliwiający wgranie właściwego programu.

Bootloader wgrywany jest przez producenta na etapie produkcji mikrokontrolera i nie da się go wykasować, zmienić ani wyłączyć. Jest uruchamiany za każdym razem po podaniu zasilania lub po resecie mikrokontrolera. Sprawdza kilka warunków i jeśli są spełnione przekazuje sterowanie do głównego programu lub przechodzi w tryb programowania mikrokontrolera.

Za pomocą funkcji bootloadera można przeprogramować mikroprocesor z głównego programu (funkcje IAP). Bootloader odpowiada też za zabezpieczenie układu przed odczytem.

LPC1114 może pracować bez zewnętrznego rezonatora kwarcowego, wtedy źródłem sygnału zegarowego może być wewnętrzny oscylator RC 12MHz lub wewnętrzny oscylator RC watchdoga, którego częstotliwość może być programowana w zakresie od 7,8kHz do 1,7MHz. Po resecie mikrokontroler taktowany jest z wewnętrznego oscylatora RC 12MHz


Zewnętrzny oscylator

Zewnętrzny oscylator jest wymagany jeśli wymagane jest stabilne źródło sygnału zegarowego. Do zewnętrznego oscylatora można podłączyć rezonatory kwarcowe o częstotliwości od 1MHz do 25MHz. Jeśli będzie wykorzystywana pętla PLL zwielokrotniająca częstotliwość oscylatora, to wtedy rezonator kwarcowy nie może mieć mniej niż 10 MHz.

Dobierając rezonator należy wziąć pod uwagę to, że pętla PLL zwielokrotnia jego częstotliwość i jeśli zastosujemy jakąś popularną wartość takiego rezonatora np. 12MHz to maksymalną prędkością będzie 48MHz przy 50MHz maksymalnie dozwolonych. W większości przypadków 2MHz nie zrobią większej różnicy, ale mogą trafić się projekty, gdzie każdy MIPS jest ważny i wtedy trzeba wziąć to pod uwagę.


Programowanie LPC1114

Układ można zaprogramować poprzez interfejs SWD albo poprzez UART za pomocą bootloadera. SWD jest interfejsem do debugowania i wymaga odpowiednich przystawek oraz środowiska więc może być dla niektórych osób opcją nieosiągalną i kosztowną.

Programowanie za pomocą UARTa nie wymaga specjalnych przystawek, wystarczy zwykły konwerter poziomów RS232/LVTTL np. MAX3232 lub konwerter USB<->RS232-LVTTL np. FT232RL lub PL2303HX.

Wejście procesora w tryb programowania możliwe jest poprzez zwarcie do masy portu P0.1 w trakcie podawania zasilania lub resetu procesora. Tym samym projektując układ należy zadbać, aby w trakcie normalnego użytkowania urządzenia z tym procesorem, port P0.1 nie był w stanie niskim w momencie włączania lub resetu, ponieważ uniemożliwi to uruchomienie głównego programu.

Przykładowe schematy układów umożliwiających programowanie LPC1114:


Interfejs RS232
Wersja z manualnym wywołaniem trybu bootloadera. Wejście w tryb bootloadera następuje poprzez przytrzymanie przycisku S2 w trakcie podawania zasilania lub resetu wywołanego przyciskiem S1



  
Wersja z automatycznym wywołaniem trybu bootloadera poprzez program Flash Magic.




Interfejs USB z układem FT232RL
Wersja minimalna z automatycznym wywołaniem trybu bootloadera. Wadą tego rozwiązania jest brak możliwości wykorzystania portów P0.0 i P0.1 do innych celów niż programowanie procesora. Interfejs może pracować jako odrębny układ programatora.



Wersja rozszerzona o układ separacji sygnałów ISP_RESET i ISP_ENTER od procesora. Dzięki temu rozwiązaniu możliwe jest wykorzystanie portów P0.0 i P0.1 do innych celów.





Interfejs USB z układem PL2303HX
Wersja minimalna z automatycznym wywołaniem trybu bootloadera. Wadą tego rozwiązania jest brak możliwości wykorzystania portów P0.0 i P0.1 do innych celów niż programowanie procesora. Interfejs może pracować jako odrębny układ programatora. Wykorzystując przejściówki USB od telefonów wykonane na tym układzie należ podłączać je według tego schematu.




Wersja rozszerzona o układ separacji sygnałów ISP_RESET i ISP_ENTER od procesora. Dzięki temu rozwiązaniu możliwe jest wykorzystanie portów P0.0 i P0.1 do innych celów.





Flash Magic jest darmowym (tylko do zastosowań niekomercyjnych) programem umożliwiającym programowanie chyba wszystkich mikrokontrolerów NXP w tym LPC11xx. Program można pobrać ze strony http://www.flashmagictool.com. Instalacja tego programu nie wymaga specjalnego komentarza, wystarczy tylko potwierdzać kolejne kroki. Po zainstalowaniu i uruchomieniu Flash Magica należy go skonfigurować.

Główne okno programu podzielone jest na 5 części oznaczonych jako Step 1 do Step 5.




W pierwszej części STEP1 – Communications ustalamy typ procesora, parametry transmisji, interfejs i częstotliwość oscylatora.

W drugiej Step2 – Erase zaznaczamy, które bloki pamięci Flash mają być wymazane przed zaprogramowaniem. Można zaznaczyć indywidualnie poszczególne bloki lub zaznaczając odpowiednie checkboxy wymazać całą pamięć ewentualnie tylko te bloki pamięci flash, które mają pokrycie w pliku hex.

W trzeciej części Step – 3 Hex File wskazujemy plik hex, który jest naszym skompilowanym programem.

W czwartej części Step 4 – Options wybieramy za pomocą checkboxów czy po zaprogramowaniu program ma zweryfikować zawartość pamięci flash oraz czy wszystkie nieużywane komórki pamięci flash mają być wypełnione wartością 0x00.

Część Step 5 – Start to tylko przycisk rozpoczynający proces programowania.

Przed rozpoczęciem programowania należy jeszcze w opcjach zaawansowanych na zakładce Hardware Config ustawić czy linie DTR i RTS uczestniczą w procesie programowania i jeśli tak to je odpowiednio skonfigurować.


  




Jeśli wszystko mamy już skonfigurowane i nasz mikrokontroler podłączony jest do komputera możemy rozpocząć programowanie. Wcześniej dobrze jest sprawdzić komunikację poprzez próbę odczytania sygnatury procesora poprzez wybranie opcji Read Device Sygnature … menu ISP.




Jeśli łączność zostanie nawiązana powinniśmy zobaczyć taki wynik:




Jeśli pojawi się inny komunikat oznacza to, że program nie może połączyć się z procesorem i wtedy należy sprawdzić sam układ lub zmienić prędkość komunikacji na mniejszą. Bootloader po resecie procesora nie ma ustawionej jakieś konkretnej prędkości transmisji, ma za to funkcje automatycznego wykrywania prędkości transmisji. Do prawidłowego działania tej funkcji wymagane jest taktowanie procesora sygnałem o częstotliwości co najmniej 10MHz.


Pierwszy program – LED Blink.
Mając już skonfigurowane środowisko oraz procesor podłączony do komputera możemy przystąpić do stworzenia pierwszego programu na ARMa i załadowania go do procesora. Będzie to bardzo nieoryginalny program, a mianowicie miganie diodą LED :-).

Diodę z rezystorem ograniczającym prąd podłączamy do portu P0.2 i +3,3V lub masę zachowując odpowiednią polaryzację.



Pobieramy nowy szablon projektu: arm_podlaczenie_projekt.zip
i rozpakowujemy go gdzieś na dysku. Szablon ten w porównaniu do poprzednich został rozszerzony o interfejs CMSIS. Po rozpakowaniu zmieniamy nazwę katalogu z Projekt na LedBlink. Należy starać się unikać spacji w nazwach, ponieważ może to powodować problemy z dostępem do plików.

Z głównego katalogu projektu otwieramy plik projektu PN - Projekt.pnproj. Po lewej stronie powinniśmy ujrzeć drzewo z katalogami i plikami .

Zmieniamy nazwę projektu na LedBlink.




Otwieramy plik Projekt.mk i zmieniamy linijkę PROJECT = Projekt na PROJECT = LedBlink.
Zapisujemy i zamykamy ten plik.



Otwieramy plik main.c. Podmieniamy kod, który w nim jest na ten poniższy i zapisujemy.


#include "LPC11xx.h"

#define LED_PORT    (1 << 2) 

int
main( void )
{
    // Konfiguracja portu P0.2 
    LPC_GPIO0->DIR |= LED_PORT;    // Port P0.2 jako wyjście 
   
    // Pętla główna
    while( 1 )
    {
        for(int i = 0; i < 100000UL; i++ )
        {
            LPC_GPIO0->DATA;    // Odczyt portu jako dodatkowe opóźnienie
        }
        LPC_GPIO0->DATA ^= LED_PORT;    // Zmiana stanu portu na przeciwny
    }
}

Wszystko jest już gotowe i możemy skompilować nasz projekt poprzez naciśniecie przycisku F9. Jeśli wszystko zostało wykonane według powyższego opisu w katalogu bin/ powinny znajdować się 3 pliki, a wśród nich LedBlink.hex. Jest to plik z programem, który trzeba wgrać do procesora za pomocą programu Flash Magic. Po zaprogramowaniu procesora i uruchomieniu dioda powinna migać z częstotliwością około 4Hz.





Startup.c
W startupie znajduje się kilka funkcji, które odpowiadają za wstępną inicjalizację procesora zaraz po uruchomieniu, a następnie wywołaniu funkcji main(). Są to standardowe inicjalizacje wymagane przez standard języka C.

Poza tym w startupie ulokowana jest tablica wektorów przerwań wraz z domyślnymi funkcjami dla niektórych wyjątków. W AVR taki starup również się znajduje, jest on częścią AVR-Libc i jest dołączany w momencie konsolidacji. W przypadku AVR wpływ na startup możemy mieć jedynie za pomocą funkcji umieszczanych w odpowiednich sekcjach Init.

CMSIS - Cortex Microcontroller Software Interface Standard
Jest to standard biblioteki dla mikrokontrolerów z rdzeniami Cortex stworzony przez ARM w celu ułatwienia tworzenia programów na te rdzenie. W skład tych bibliotek wchodzą:
  • Definicje rejestrów
  • Definicje wektorów przerwań
  • Funkcje umożliwiające dostęp do peryferiów rdzenia
  • Funkcje umożliwiające wykorzystanie specyficznych instrukcji asemblerowych, z których nie korzystają kompilatory języka C/C++

Zobacz także:

Kurs ARM: Spis treści

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

ARM: Środowisko programistyczne

Autor: Deucalion
Redakcja: Dondu

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

1. Kompilator C/C++ dla ARM

Producent mikrokontrolera, na którym będziemy wchodzić w świat ARMów zadbał o darmowe środowisko o nazwie LPCXpresso. Jest to bardzo rozbudowane środowisko oparte na platformie Eclipse, które jest edytorem, kompilatorem i debuggerem w jednym. Nauka tego środowiska to jest temat na odrębny artykuł.

Ja na razie wybrałem inne rozwiązanie, które w większym stopniu pozwala na zrozumienie działania wszystkich mechanizmów biorących udział przy kompilowaniu kodu źródłowego do pliku wykonalnego oraz jest bardziej uniwersalne i pozwala na korzystanie z mikrokontrolerami innych producentów. W jednym z kolejnych artykułów podepniemy się pod standardowego Eclipsa


Za kompilator posłuży nam Sourcery CodeBench Lite Edition for ARM





Na tej stronie umieszczona jest również dokumentacja do tego kompilatora i bibliotek.



Średnio co pół roku ( wiosna i jesień ) wychodzi nowa wersja tego kompilatora, warto więc czasami zajrzeć na tę stronę w nadziei, że znajdziemy coś nowego.




2. Edytor programisty.

Drugim ważnym narzędziem jest edytor. Do edycji kodu może posłużyć nam najzwyklejszy Notatnik, ale to rozwiązanie nie jest najlepszym pomysłem. Idealne jest wspomniane wyżej środowisko Eclipse z wtyczką CDT, ale ja nie mam na razie zamiaru przysłaniać sedna tematu omawianiem  tego środowiska. Jako edytor wykorzystamy prosty edytor ze specjalnym ukierunkowaniem na programowanie – Programmer’s Notepad pobierz w wersji stabilnej.






Instalacja.
Kompilator instalujemy z domyślnymi ustawieniami, więc podczas instalacji tylko potwierdzamy. Jak ktoś pracuje na wielu kontach użytkowników to powinien podczas kroku „Add to PATH?”




ustawić opcje jak poniżej:





Kompilator standardowo instaluje się w katalogu C:\Program Files\CodeSourcery\Sourcery G++ Lite\, ale jak ktoś chce, to nic nie stoi na przeszkodzie, żeby zmienić ścieżkę instalacyjna na inną.

Programmer’s Notepada także instalujemy z domyślnymi ustawieniami.



Gdy już wszystko zainstalujemy, do szczęścia przydadzą nam się jeszcze dwa niewielkie programy z pakietu cygwina – echo i touch. Programy te najlepiej umieścić w katalogu z binariami kompilatora:
C:\Program Files\CodeSourcery\Sourcery G++ Lite\bin\

Może zdarzyć się, że przy próbie kompilacji wyskoczą komunikaty o braku bibliotek cygwina, żeby nie szukać i błądzić wszystkie biblioteki i dodatki znajdują się w tej paczce którą przygotowałem dla Was: arm_dodatki.zip

Plik najlepiej rozpakować do wspomnianego wcześniej katalogu  z binariami.


Wszystkie narzędzia, które są potrzebne do tworzenia programu mamy już zainstalowane. Zostało nam jeszcze skonfigurowanie edytora.

1. Otwieramy Programmer’s Notepada





2. Otwieramy okno konfiguracyjne Menu -> Tools -> Options




3. Na drzewie klikamy w opcję Tools, a następnie dodajemy dwa polecenia naciskając przycisk Add




4. Wypełniamy opcje poleceń według wzorów:






Po tej operacji powinniśmy mieć skonfigurowane 2 polecenia:


Zamykamy okno konfiguracji opcji.




W Menu -> Tools pojawiły się dwie nowe pozycje: 






5. Tworzymy nowy projekt.









6. Do katalogu z projektem wgrywamy pliki z tej paczki arm_szablon-v1.0.zip.
Pliki te i katalogi tworzą szablon startowy dla tego projektu. Po wgraniu plików folder powinien tak wyglądać:




Jak widać, wśród tych plików są dwa pliki wsadowe bat. Jeśli po uruchomieniu build.bat zobaczymy ekran jak poniżej oznacza to, że mamy poprawnie zainstalowany i skonfigurowany kompilator. Drugi plik clean.bat powoduje odwrócenie działania kompilatora, tzn. czyści wszystkie pliki, które wygenerował kompilator









Pozostałe pliki:

ARM-Pierwsze_kroki.pnproj – plik projektu edytora PN.
Makefile – skrypt dla programu cs-make.exe, jest to swojego rodzaju przepis na wykonanie wsadu do procesora. Skrypt ten modyfikujemy jeśli dodajemy nowe pliki źródłowe oraz zmieniam typ rdzenia.
lpc1114.ld – skrypt dla konsolidatora (linkera) - kolejny przepis. Modyfikujemy go tylko w przypadku zmiany procesora i jeśli potrzebne są inne sekcje.

i katalogi:
bin – katalog w którym po kompilacji znajdują się pliki które ładujemy do procesora
inc – katalog z plikami nagłówkowymi
src – katalog z plikami źródłowymi
tmp – katalog z plikami pomocniczymi, przydatnymi do szukania błędów i podglądania wyników pracy kompilatora.


7. Kolejnym krokiem jest zaciągnięcie do projektu  struktury potrzebnych dla nas plików. Na nazwie projektu klikamy  prawym przyciskiem myszy i wybieramy pozycję Add Magic Folder…










Czynność powtarzamy dla folderu „inc”

Wszystkie pliki które pojawię się w tych folderach i będą spełniały kryteria filtru zostaną automatycznie dodane do danego folderu. Czasami wymagane jest odświeżenie widoku poprzez wybranie opcji Refresh w menu katalogu.

Po tej  operacji i otwarciu pliku main.c powinniśmy mieć taki widok:




Po naciśnięciu klawisza F9 program powinien skompilować się, a efekty kompilacji powinny być widoczne w dolnej części okna Programmer's Notepada:





Jak widać, nasz program zajął 588 bajtów pamięci FLASH i 200 bajtów pamięci RAM. Naciśnięcie F10 spowoduje wyczyszczenie wszystkich plików wygenerowanych przez kompilator.

Tutaj możesz pobrać gotowy szablon z plikami projektu PN  szablon_pn-v1.1.zip

W następnym artykule zajmiemy się częścią sprzętową i zapędzimy nasze 32 bity do mrugania diodą ;-)


Jeśli napotkałeś najmniejsze problemy ze zrozumieniem tego poradnika lub zauważyłeś jakieś braki to daj znać, postaramy się to uzupełnić. 

Miło byłoby gdybyś podzielił się swoją opinią, będzie to dla nas wskazówką, że warto coś takiego tworzyć i dalej rozwijać.   

Kurs ARM: Spis treści
Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.