05 ARM SAM4 – Prosty timer

Jeżeli chcemy w naszym programie wywoływać jakąś funkcje co określony powtarzający się czas możemy wykorzystać do tego funkcję SysTick, która umożliwia nam wykonanie określonego kodu z powtarzających się odstępach czasu. Funkcja SysTick uruchamia wbudowany w procesor timer, który odlicza liczbę cykli zegara do zadanej wartości bez udziału głównego CPU. W poprzednich programach aby zapalać i gasić diodę co określony czas, używaliśmy pętli for, która wykonywała się kilkaset tysięcy razy zajmując całą moc naszego procesora. Jest to rozwiązanie bardzo nie ekonomiczne z punktu widzenia użycia procesora. Każdy procesu z rodzimy ARM ma wbudowane timery, które możemy wykorzystać do zapalania i gaszenia diody LED. Timery do działania wykorzystują przerwania. Działa to w ten sposób, że timer odlicza do zadanej wartości, w momencie jej osiągnięcia wywołuje przerwanie, czyli przerywa działania realizowane w głównej pętli while i realizuje zdefiniowaną procedurą. Poniżej funkcja określająca nam liczbę do której będziemy odliczać:

SysTick_Config( SystemCoreClock/10UL ) ;

Ponieważ chcemy aby nasza dioda zapalała się z taką samą częstotliwością niezależnie od prędkości procesora to pobieramy aktualną częstotliwość pracy za pomocą funkcji SystemCoreClock, przy uruchomieniu procesora na początku pracuje on domyślnie z prędkością 4 Mhz, a zmienna podaje nam wartość 4000000 cykli pracy na sekundę, następnie dzielimy ją przez 10, ponieważ chce aby stan diody zmieniał się 10 razy na sekundę. Następnym krokiem jest uruchomienie przerwania odpowiedzialnego za realizację funkcji, poniżej odpowiednia funkcja:

NVIC_EnableIRQ( SysTick_IRQn ) ;

Funkcje deklarujące użycie timera używamy na początku programu poza główną pętlą while. Teraz stworzymy krótką funkcję, która będzie zmieniała stan zapalenia diody w stosunku do poprzedniego. W tym celu stworzymy zmienną ul_toggle_led która przechowuje informacje o tym czy dioda jest zapalona, czy nie. Zapis static przed deklaracja zmiennej oznacza, że może być ona użyta w dowolnym miejscu kody, a nie tylko w wybranej funkcji. Poniżej składania funkcji odpowiedzialnej za zmianę stanu diody:


void toggle_led( void)
{
if (ul_toggle_led == 0)
{
LEDA_on;
ul_toggle_led = 1;
}
else
{
LEDA_off;
ul_toggle_led = 0;
}
}

Działa ona w następujący sposób, jeśli zmienna ul_toggle_led = 0 to włącz diodę i zmień zmienną ul_toggle_led na 1, jeśli zmienna ul_toggle_led = 1 to wyłącz diodę  i zmień wartość zmiennej na 0.

Teraz zdefiniujemy funkcję, którą ma być wykonana w przypadku wywołania przerwania przez timer:


void SysTick_Handler( void )
{
toggle_led();
}

Nazwa funkcji jest stała i unikalna dla danego typu przerwania nie możemy jej zmieniać, edytować możemy jedynie fragment pomiędzy nawiasami, gdzie wykorzystujemy stworzoną przez nas wcześniej funkcję toggle_led poniżej pełen kod programu, główna pętla programu jest pusta. To podstawowa zaleta użycia timera. Dzięki niemu możemy w głównej części programu realizować złożone zadania i obliczenia, a w odpowiednim momencie timera sam przerwie nasze obliczenia i wywoła określoną funkcje, po czym wróci do głównej części programu.



#include "sam.h"
#define LEDA (1<<10) 
#define LEDA_on PIOC->PIO_CODR = LEDA
#define LEDA_off PIOC->PIO_SODR = LEDA

static uint8_t ul_toggle_led = 0;

void toggle_led( void)
{
if (ul_toggle_led == 0)
{
LEDA_on;
ul_toggle_led = 1;
}
else
{
LEDA_off;
ul_toggle_led = 0;
}
}

void SysTick_Handler( void )
{
toggle_led();
}

int main(void)
{
/* Initialize the SAM system */
SystemInit();
WDT->WDT_MR=WDT_MR_WDDIS; // Wyłączenie funkcji watchdog
PIOC->PIO_OER = LEDA; // Ustawia rejestr C jako wyjście
PIOC->PIO_PUDR =LEDA; // Wyłącza rezystory podciągające
SysTick_Config( SystemCoreClock/10UL ) ;
NVIC_EnableIRQ( SysTick_IRQn ) ;

while (1)
{

}
}