Celem pierwszego programu będzie cykliczne zapalenie i gaszenie dwóch diód znajdujących się na zestawie uruchomieniowym SAM4XPlained. Do programowania układu posłuży wbudowany w zestaw programator J-Link. Do napisaniu pierwszego programu posłuży nam środowiska Atmel Studio 7.0 do pobrania z poniższego adresu:
http://www.atmel.com/tools/atmelstudio.aspx
Płytka jest wyposażona w dwie dostępne bezpośrednio diody led oznaczone na poniższym zdjęciu jako LED1 oraz LED2.
Diody podłączone są do w/w PC10 oraz PC17 według poniższego schematu, ponieważ podłączone są do zasilania, to podając logiczne jedne na wyjście np. PC10 gasimy diodę, a podając logiczne 0 ją zapalamy:
Możemy teraz przejść do tworzenia nowego programu. Z zakładki File->New->Project… wybieramy projekt typu GCC C Exexcutable Project,
a następnie z listy procesorów wybieramy ATSAM4S16C.
Program automatycznie utworzy nam kod programu jak poniżej zawierający następujące ważne dla nas elementy. #include „sam.h” – za pomocą tej komendy do programu zostanie podłączony zestaw definicji rejestrów dla procesorów typu sam. SystemInit(); – załącza na działanie naszego procesora
#include „sam.h”
int main(void)
{
/* Initialize the SAM system */
SystemInit();
/* Replace with your application code */
while (1)
{
}
}
Uruchomienie poniższego programu nic nie spowoduje, musimy teraz uruchomić odpowiednie rejestry w procesorze, które spowodują podanie napięcia na interesującą nas nóżkę PC10.
Procesor podzielony jest na rejestry A,B,C każdy z nich obsuguje własny zestaw wejść i wyjść.
Na początku musimy uruchomić interesujący na rejestr C poprzez wpisanie wartości 1 w odpowiedni bit pamięci. Poniżej adresy uruchamiające odpowiednie rejestry:
PIO Enable Register (SAM4S Series Datascheet – page 588)
PIOA – 0x400E0E00
PIOB – 0x400E1000
PIOC – 0x400E1200
każdy z tych adresów przechowuje zmienną 32 bitową, aby uruchomić w/w PC10 muismy ustawić 10bit jako aktywny, robimy to poniższą formułą.
(*(volatile unsigned int*)0x400E1200U) = 1<<10;
następną rzeczą którą musimy wykonać to ustawić rejest jako wyjście, służ do tego poniże adresy:
PIO Output Enable Register (SAM4S Series Datascheet – page 592)
PIOA – 0x400E0E10
PIOB – 0x400E1010
PIOC – 0x400E1210
rejestr PC10 ustawiamy poniższą formułą:
(*(volatile unsigned int*)0x400E1210U) = 1<<10;
,musimy też ustawić rezystor podciągający w pozycji 0
PIO Pull-Up Disabled Register (SAM4S Series Datascheet – page 608)
PIOA – 0x400E0E60
PIOB – 0x400E1060
PIOC – 0x400E1260
rejestr PC10 ustawiamy poniższą formułą:
(*(volatile unsigned int*)0x400E1260U) = 1<<10;
, następnie przechodzimy do adresów który umożliwią nam wystawienie logicznej jedynki na dowolne wyjście:
PIO Set Output Data Register (SAM4S Series Datascheet – page 597)
PIOA – 0x400E0E30
PIOB – 0x400E1030
PIOC – 0x400E1230
rejestr PC10 ustawiamy poniższą formułą:
(*(volatile unsigned int*)0x400E1230U) = 1<<10;
teraz udało się nam zapalić diodę, aby ją zgasić musimy wyczyścić rejestr za pomocą poniższych adresów:
PIO Clear Output Data Register (SAM4S Series Datascheet – page 598)
PIOA – 0x400E0E34
PIOB – 0x400E1034
PIOC – 0x400E1234
rejestr PC10 ustawiamy poniższą formułą:
(*(volatile unsigned int*)0x400E1234U) = 1<<10;
do zakończenia pracy potrzebujemy formułę która będzie nam odliczała czas pomiędzy zapalaniem i gaśnięciem diody. Zrobimy to przy użyciu pętli for jak poniżej:
for(uint32_t i=0; i<2000000;i++) {}
zmiennna i jest zmienną 32 bitową typu uint32_t i obliczenia na niej wykonują się w jednym takcie zegara, w przypadku procesorów AVR wymagane było kilka taktów na taką zmienną.
cały program powinien mieć poniższą składnie:
#include „sam.h”
int main(void)
{
SystemInit();
(*(volatile unsigned int*)0x400E1200U) = 1<<10;
(*(volatile unsigned int*)0x400E1210U) = 1<<10;
(*(volatile unsigned int*)0x400E1260U) = 1<<10;
while (1)
{
(*(volatile unsigned int*)0x400E1230U) = 1<<10;
for(uint32_t i=0; i<2000000;i++) {}
(*(volatile unsigned int*)0x400E1230U) = 1<<10;
for(uint32_t i=0; i<2000000;i++) {}
}
}
Teraz możemy przejść do uproszczania naszego programu, na codzień cieżko jest programować podając adresy rejestrów, czy numery pinów, dlatego możemy te elementy zdefiniować czytelnymi dla nas skrótami. Rozbudujemy też nasz program o zapalanie diód na przemiennie. Do zdefioniowania uproszczonych definicji służy instrukcja #define, a poniższa składania przypisze nam pin 10 do diody A oraz pin 17 do diody B.
#define LEDA (1<<10) #define LEDB (1<<17)
Od teraz zamiast zapisu 1<<10 stosujemy zapis LED_A.
Możemy też uprościć adresowania rejestrów w pamięcie. Ich definicje znajdują się w pliku sam4sh16.h który jest wywołąny przez plik sam.h. I tak, uruchomienie portu które dotychczas wyglądało tak:
(*(volatile unsigned int*)0x400E1200U) = 1<<10;
zastępujemy uproszczonym zapisem:
PIOC->PIO_PER = LEDA;
oczywiście, są też rejestry PIOA oraz PIOB, ale my ich narazie nie wykorzystujemy, jeśli chcemy uruchomić rejestr dla dwóch diód, możemy zrobić to tak:
PIOC->PIO_PER = LEDA;
PIOC->PIO_PER = LEDB;
lub prościej tak:
PIOC->PIO_PER = (LEDA | LEDB);
poniżej definicje dla wykorzystywanych przez nas pozostałych adresów:
(*(volatile unsigned int*)0x400E1210U) = 1<<10 teraz możemy
PIOC->PIO_OER = LEDA;
(*(volatile unsigned int*)0x400E1260U) = 1<<10 teraz możemy
PIOC->PIO_PUDR = LEDA;
(*(volatile unsigned int*)0x400E1230U) = 1<<10 teraz możemy
PIOC->PIO_SODR = LEDA;
(*(volatile unsigned int*)0x400E1234U) = 1<<10 teraz możemy
PIOC->PIO_CODR = LEDA;
Poniżej przykład zooptymalizowanego programu:
#include "sam.h" #define LEDA (1<<10) #define LEDB (1<<17) int main(void) { /* Initialize the SAM system */ SystemInit(); PIOC->PIO_PER = (LEDA | LEDB); PIOC->PIO_OER = (LEDA | LEDB); PIOC->PIO_PUDR = (LEDA | LEDB); /* Replace with your application code */ while (1) { PIOC->PIO_SODR = LEDA; PIOC->PIO_CODR = LEDB; for(uint32_t i=0; i<250000;i++) { } PIOC->PIO_SODR = LEDB; PIOC->PIO_CODR = LEDA; for(uint32_t i=0; i<250000;i++) {} } }
example.c
#include „sam.h”
#define LEDA (1<<10)
#define LEDB (1<<17) int main(void) { /* Initialize the SAM system */ SystemInit(); PIOC->PIO_PER = (LEDA | LEDB);
PIOC->PIO_OER = (LEDA | LEDB);
PIOC->PIO_PUDR = (LEDA | LEDB);
/* Replace with your application code */
while (1)
{
PIOC->PIO_SODR = LEDA;
PIOC->PIO_CODR = LEDB;
for(uint32_t i=0; i<250000;i++) { } PIOC->PIO_SODR = LEDB;
PIOC->PIO_CODR = LEDA;
for(uint32_t i=0; i<250000;i++) {}
}
}
Autor: Kwiatkowski Dawid
kontakt: dawid@3dpart.pl