Skocz do zawartości

[Kurs] Programowanie ARM LPC1114 cz.3 - zegary i przerwania


Elvis

Pomocna odpowiedź

Poprzednia część kursu.

[Kurs] Programowanie ARM LPC1114 cz.3 - zegary i przerwania

Na początek dobra wiadomość. Rdzenie Cortex mają znacznie poprawioną obsługę przerwań w porównaniu do starszej rodziny ARM7TDMI. Inżynierowie firmy ARM wzięli sobie do serca krytykę poprzednich procesorów i dodali do rdzenia znacznie poprawioną obsługę przerwań. Przed lekturą tego artykułu zachęcam do przeczytania piątej części poprzedniego kursu, w której poruszona została pokrewna tematyka.

Najpierw zajmiemy się przerwaniami od układów licznikowych, tzw. timerów. Procesor LPC1114 jest wyposażony w 5 timerów. Dwa są 16-bitowe, dwa 32-bitowe oraz jeden specjalny, który ma rozdzielczość 24-bitów. Rejestry 16 i 32 bitowe są to liczniki tzw. ogólnego zastosowania. Oferują bogatą funkcjonalność, w szczególności mogą działać jako PWM. Ostatni timer (SysTick timer) ma specjalne zastosowanie - służy do zapewniania podstawy czasu. Jest to bardzo wygodne rozwiązanie dla każdego projektanta systemu.

Timer systemowy

Najpierw musimy napisać procedurę obsługi przerwania. Jest to nadzwyczaj łatwe zadanie. Wystarczy napisać funkcję o nazwie SysTick_Handler. Co powinna robić taka funkcja? Na początek może po prostu zwiększać globalny licznik czasu. Mamy więc kod:

volatile uint32_t global_timer = 0;

void SysTick_Handler(void)
{
global_timer++;
}

Chcemy, aby zmienna global_timer była zwiększana co powiedzmy, 10 ms.

Konfiguracja jest bardzo łatwa dzięki funkcji SysTick_Config zdefiniowanej w pliku core_cm0.h (plik został dołączony wraz z biblioteką CMSIS). Aby uruchomić przerwanie piszemy:

SysTick_Config(480000); // 10ms

Wartość 480 000 wynika z prędkości działania procesora - jest on skonfigurowany do pracy z częstotliwością 48MHz. Więc 48 000 000 / 480 000 daje nam 100Hz, czyli oczekiwane 10ms. W pliku program09.zip znajdziemy przykład wykorzystania timera do domierzania czasu zapalania i gaszenia diod.

Dla dociekliwych

Ktoś może zapytać, jak to się dzieje, że procesor działa z prędkością 48MHz. Odpowiedź znajdziemy w pliku system_LPC11xx.c znajdującym się w bibliotece CMSIS. Znajdziemy tam funkcję SystemInit. Uruchamia ona pętlę PLL, domyślnie wykorzystuje zewnętrzny kwarc 12MHz i konfiguruje procesor do pracy z podaną prędkością. Zmieniając plik system_LPC11xx.c możemy łatwo zmienić konfigurację systemu.

Kolejne pytanie to nazwa funkcji - skąd bierze się akurat SysTick_Handler. Aby się o tym przekonać możemy otworzyć plik cr_startup_lpc11.c - został on automatycznie dodany do naszego projektu. W pliku tym znajdziemy domyślne funkcje obsługujące przerwania. W szczególności funkcję obsługi interesującego nas przerwania. Jest ona oznaczona słówkiem WEAK, dzięki czemu nasza funkcja niejako zastąpi tę domyślną. Warto jeszcze przyjrzeć się funkcji ResetISR. Jest ona wywoływana po resecie (więc i przy uruchomieniu) mikrokontrolera. Ta funkcja najpierw wywołuje SystemInit, a następnie nasz program - funkcję main(). W pliku cr_startup_lpc11.c znajdziemy również domyślne funkcje obsługi pozostałych przerwań. Możemy je przedefiniować, aby zapewnić własne funkcje obsługi przerwań.

Timery ogólnego przeznaczenia

Mamy już globalny licznik czasu, teraz poznamy 16-bitowy licznik czasu. W pliku program10.zip znajduje się opisywany przykład. Program uruchamia 2 timery. Timer systemowy oraz 16 bitowy timer 0. Ten timer musimy skonfigurować sami. W tym celu wykorzystujemy wskaźnik LPC_TMR16B0. Pełny opis rejestrów znajdziemy w dokumentacji, następujący kod uruchamia timer:

LPC_SYSCON->SYSAHBCLKCTRL |= 0x80; // CT16B0 clock enable
LPC_TMR16B0->TCR = 2; // timer reset
LPC_TMR16B0->MCR = 3; // reset and interrupt on MR0
LPC_TMR16B0->MR0 = 48000; // 1 ms
LPC_TMR16B0->PR = 9; // prescaler 10
LPC_TMR16B0->TCR = 1; // timer enable
NVIC_EnableIRQ(TIMER_16_0_IRQn); // interrupt enable

Pierwsza linijka jest konieczna, aby moduł timera zaczął działać. Aby oszczędzać prąd procesor początkowo ma uruchomione tylko niezbędne moduły. Za pomocą rejestru MCR ustawiamy tryb działania timera - chcemy aby liczył do wartości MR0, następnie wywoływał przerwanie i liczył od początku. Do MR0 wprowadzamy wartość 48000, więc przerwanie byłoby wywoływane co 1ms. Wykorzystujemy preskaler w rejestrze PR aby zamiast co 1ms wywoływać przerwanie co 10ms. Do rejestru PR wpisujemy wartość o 1 mniejszą niż oczekiwane „spowolnienie” timera. Funkcja NVIC_EnableIRQ uruchamia nasze przerwania. Pozostaje jeszcze dodać funkcję obsługi przerwania. Podobnie jak poprzednio wystarczy napisać funkcję o nazwie TIMER16_0_IRQHandler.

void TIMER16_0_IRQHandler(void)
{
static uint8_t bit = 0;
if (bit) {
	GPIOClear(LPC_GPIO2, 0x80);
	bit = 0;
} else {
	GPIOSet(LPC_GPIO2, 0x80);
	bit = 1;
}
LPC_TMR16B0->IR = 0x1f; // clear interrupt flag
}

Funkcja na pinie P2_7 generuje sygnał prostokątny. Jeśli mamy pod ręką oscyloskop, możemy zobaczyć jak działa nasze przerwanie. Ostatnia linijka funkcji jest bardzo ważna. W niej zerujemy flagę przerwania. Inaczej zaraz po zakończeniu funkcji zostałaby ona wywołana ponownie.

Kolejna część naszego kursu, w której omówiony zostanie PWM pojawi się już za tydzień.

program09.zip

program10.zip

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.