Instrukcja ćwiczeń laboratoryjnych do przedmiotu Systemy

Transkrypt

Instrukcja ćwiczeń laboratoryjnych do przedmiotu Systemy
Społeczna Wyższa Szkoła Przedsiębiorczości i Zarządzania
Instrukcja ćwiczeń laboratoryjnych
do przedmiotu Systemy Wbudowane
Łódź, 2010
Zestaw laboratoryjny i podłączenie do komputera PC
W skład zestawu laboratoryjnego wchodzą następujące elementy:
1. układ uruchomieniowy ZL3AVR,
2. zasilacz,
3. programator KamPROG,
4. przewód USB do połączenia programatora z komputerem PC,
5. taśma 10-przewodowa do połączenia programatora z układem uruchomieniowym,
6. dwie taśmy 16-przewodowe do wykonywania połączeń na płycie układu,
7. taśma 4-przewodowa do wykonywania połączeń na płycie układu,
8. trzy pojedyncze przewody do wykonywania połączeń na płycie układu,
9. Opis układu uruchomieniowego ZL3AVR (dostępny również w formie elektronicznej
jako dokument „zl3avr.pdf”).
Zasady korzystania z zestawów i wykonywania połączeń
− Po pobraniu zestawu należy sprawdzić jego skład. Ewentualny brak wyposażenia należy
zgłosić prowadzącemu zajęcia.
− Łączenie układu uruchomieniowego z programatorem i komputerem PC oraz połączenia
złączy na samej płycie układu należy wykonywać przy wyłączonym zasilaniu.
− Podczas wykonywania połączeń na płycie układu uruchomieniowego zadbać o
odprowadzenie ładunku elektrostatycznego gromadzącego się na ludzkim ciele do masy,
można to osiągnąć dotykając ręką obudowy złączy audio na brzegu układu
uruchomieniowego.
− Po zakończonych zajęciach spakować wszystkie elementy do pojemnika. Proszę pamiętać
o zasilaczu!
Rys.1 Połączenie elementów zestawu laboratoryjnego
Ćwiczenie 1.
Celem ćwiczenia jest zapoznanie studentów ze sprzętowym i programistycznym
środowiskiem uruchomieniowym mikrokontrolera ATmega32.
Ćwiczenie wykonywane jest interaktywnie, polega na wykonywaniu instrukcji słownych
prowadzącego zajęcia. W ramach ćwiczenia wprowadzane są następujące zagadnienia:
- łączenie elementów zestawu uruchomieniowego z komputerem PC,
- zachowanie środków ostrożności podczas łączenia elementów i załączania zasilania,
- tworzenie nowego projektu w środowisku AVRstudio4,
- edycja prostego programu, wymuszającego określone stany danego portu mikrokontrolera,
- opis binarnego formatu stałych i znaczenia przypisywania wartości nazwom rejestrów,
- proces kompilacji kodu źródłowego i budowania aplikacji,
- symulacja pracy mikrokontrolera,
- wywołanie obsługi programatora i ładowanie programu na pokład mikrokontrolera,
- porównanie symulacji z rzeczywistym wykonaniem programu,
- realizacja opóźnień przez wielokrotne wykonanie pustych pętli,
- wpływ ustawień optymalizacji na proces kompilacji programu.
Kod przykładowego programu utworzonego na zajęciach:
#include <avr\io.h>
int main()
{
long i;
DDRB=0xFF;
//Konfiguracja końcówek portu B jako wyjścia
while(1)
//Nieskończona pętla
{
PORTB=0b00001111;
//Wysłanie wzoru sygnałów na wyjścia portu B
for(i=0;i<100000;i++); //Pętla realizująca opóźnienie
PORTB=0b11110000;
for(i=0;i<100000;i++);
}
}
Ćwiczenie 2.
Celem ćwiczenia jest nabycie umiejętności programowej obsługi prostych urządzeń
wyjściowych na przykładzie wysterowania 4-cyfowego wyświetlacza siedmiosegmentowego.
Czynności do wykonania:
Przed podłączeniem zestawu do komputera PC wykonać następujące połączenia:
- za pomocą taśmy 16-przewodowej połączyć port B mikrokontrolera (JP16) ze złączem
„Cyfra” (JP24) wyświetlacza siedmiosegmentowego, końcówka PB0 powinna być połączona
z końcówką „a”,
- za pomocą pojedynczego przewodu połączyć końcówkę C1 złącza „Kolumna” z końcówką
masy „GND” złącza „I2C” (JP26). Połączenie pozwoli na wysterowanie segmentów
pojedynczego wyświetlacza siedmiosegmentowego (W4) z portu B.
Napisać program wyświetlający pojedynczą cyfrę z zakresu 0-9. Do przypisania danej cyfrze
odpowiedniego zestawu „zapalonych” segmentów wyświetlacza wykorzystać następującą
tablicę, zdefiniowaną jako zmienna globalna:
const char LED7SEG[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
Wyświetlane cyfry są indeksami elementów tablicy wysyłanych na port B, np. PORTB=
LED7SEG[0]. Należy pamiętać o prawidłowym skonfigurowaniu kierunku linii portu B.
Napisać funkcję realizującą programowo opóźnienie o zadaną liczbę milisekund, powtarzając
zadaną liczbę razy operację trwającą jedną milisekundę, np. pętlę opóźniającą, zgodnie z
wzorem:
void delay(unsigned miliseconds)
{
unsigned i;
for(i=0; i<miliseconds;i++)
/*do uzupełnienia: operacja trwająca 1 milisekundę*/
}
Funkcję wykorzystać do wyświetlania kolejnych cyfr dziesiętnych, ze zmianą co jedną
sekundę.
Za pomocą taśmy 4-przewodowej połączyć złącze „Kolumna” z wyprowadzeniami portu A
mikrokontrolera tak, by końcówka C4 była połączona z końcówką PA0, C3 z PA1 itd.
Pozwoli to na programowe wybieranie jednego z czterech wyświetlaczy. Wymuszenie zera
logicznego na stosownym wejściu złącza „Kolumna” załącza odpowiedni wyświetlacz. W
danej chwili powinien być wybrany tylko jeden wyświetlacz. Poniższa funkcja przy
wykonanych połączeniach wyświetla na pozycji „position” cyfrę „digit”:
void disp_digit(unsigned char position, unsigned char digit)
{
unsigned char i,mask=1;
DDRA=DDRA | 0x0F;
DDRB=0xFF;
//skonfigurowanie czterech mniej znaczących bitów
//portu A jako wyjściowe
i=PORTA;
mask=mask<<(position-1);
mask=~mask;
PORTA=(i & 0xF0) | (mask & 0x0F);
PORTB=LED7SEG[digit];
}
Przetestować możliwość wyświetlenia dowolnej cyfry na dowolnej pozycji wyświetlacza,
wykorzystując powyższą funkcję.
Poniższa funkcja wyświetla ciąg cyfr zapisanych w tablicy „dig_string” na wyświetlaczu z
zastosowaniem szybkiego przełączania wyświetlaczy i wyprowadzania kodów odpowiednich
cyfr. Cykliczne powtarzanie wywołania tej funkcji daje wrażenie ciągłego świecenia się
wszystkich wyświetlaczy.
void disp_dig_string(char *dig_string)
{
unsigned char i;
for(i=1; i<=4; i++)
{
disp_digit(i, dig_string[i-1]);
delay(2);
}
}
Zastosować utworzone funkcje do wyświetlania czterocyfrowej liczby dziesiętnej po jej
uprzednim zdekomponowaniu na ciąg czterech cyfr, zapisanych w kolejnych elementach
tablicy, będącej argumentem funkcji „disp_dig_string”. Operację zawrzeć w funkcji:
void disp_number(unsigned number)
{
char numtab[4];
/*do uzupełnienia: konwersja liczby na ciąg cyfr
zapisanych do tablicy numtab*/
disp_dig_string(numtab);
}
Sprawozdanie powinno zawierać kody utworzonych programów oraz wyjaśnienie sposobu
realizacji opóźnienia o jedną milisekundę i zdekomponowania liczby na ciąg cyfr. Czy jest
możliwe wygaszenie zer nieznaczących i jak należy to wykonać?
Ćwiczenie 3.
Celem ćwiczenia jest nabycie umiejętności programowej obsługi prostych urządzeń
wejściowych na przykładzie klawiatury matrycowej.
Czynności do wykonania:
Przed podłączeniem zestawu wykonać następujące połączenia:
- połączenie wyświetlacza siedmiosegmentowego z portem A i B mikrokontrolera, jak w
ostatniej części ćwiczenia 2,
- za pomocą taśmy 16-przewodowej połączyć port C mikrokontrolera (JP18) ze złączem
„Klawiatura 4x4” (JP23) klawiatury matrycowej, końcówka „PC0” powinna być połączona z
końcówką „W1”,
- za pomocą zworki zewrzeć końcówki złącza „Mała klawiatura” (JP3). To połączenie zwiera
końcówki przycisków pierwszej kolumny (z lewej strony) do masy, umożliwiając ich
bezpośredni odczyt z portu mikrokontrolera.
Przeanalizować schemat elektryczny klawiatury, dostarczony w dokumentacji układu
uruchomieniowego. Należy zauważyć, że przy wykonanych połączeniach oraz
skonfigurowaniu portu C jako port wejściowy z załączonymi rezystorami podciągającymi
(DDRC=0; PORTC=0xFF;) wciśnięcie klawisza pierwszej kolumny będzie generowało zero
logiczne na podłączonej do niego końcówce portu. Pozostałe końcówki będą pozostawać w
stanie jedynki logicznej.
Napisać program odczytujący w sposób ciągły stan pierwszej kolumny klawiszy i
wyświetlający go w postaci liczby dziesiętnej. Zastosować wyrażenie: PINC & 0x0F, które
wywołuje odczyt stanu końcówek portu C podłączonych do klawiszy, a jego wartość zależy
jedynie od stanu klawiszy. Jeśli żaden z klawiszy nie został wciśnięty wyrażenie przyjmuje
wartość 15. Należy zauważyć, że stała 0x0F w powyższym wyrażeniu stanowi rodzaj maski
bitowej: operacja iloczynu bitowego powoduje, że jedynie stan bitów 0:3 zależy od stanu
logicznego końcówek mikrokontrolera. Bity 4:7 przyjmują wartości zerowe.
Zaproponować modyfikację programu, w której zamiast wartości wzoru bitów
wygenerowanych przez wciśnięte klawisze, zostanie wyświetlony numer klawisza pod
warunkiem, że użytkownik wciska tylko jeden z nich. Można w tym celu wykorzystać
instrukcję „switch”. Operację odczytu numeru klawisza zawrzeć w funkcji:
unsigned char read_key4();
Wówczas w programie głównym należy wykonywać nieskończoną pętlę:
while(1)
disp_number(read_key4());
Sprawozdanie powinno zawierać kody utworzonych programów z komentarzami oraz
wyjaśnienie sposobu odczytu stanu klawiszy i przetwarzania go na numer wciśniętego
klawisza.
Ćwiczenie 4.
Celem ćwiczenia jest nabycie umiejętności oprogramowania obsługi przerwań na przykładzie
obsługi klawiatury matrycowej.
Obsługa przerwań jest realizowana za pomocą następująco makra (wymaga dołączenia
zbiorów „avr\interrupt.h” i „avr\signal.h”):
ISR(numer_wektora_przerwania)
{
/* Operacje wykonywane w procedurze obsługi przerwania o danym wektorze*/
}
Poniżej są zestawione zgodnie z malejącym
implementowanych w mikrokontrolerze ATmega32:
priorytetem
wektory
przerwań
INT0_vect, INT1_vect, INT2_vect – przerwania zewnętrzne,
TIMER2_COMP_vect,
TIMER2_OVF_vect,
TIMER1_CAPT_vect,
TIMER1_CMPA_vect,
TIMER1_CMPB_vect, TIMER1_OVF_vect, TIMER0_COMP_vect, TIMER0_OVF_vect –
przerwania od liczników,
SPI_STC_vect – przerwanie portu SPI lub STC,
USART_RXC_vect, USART_UDRE_vect, USART_TXC_vect
ADC_vect – przerwanie na końcu konwersji ADC,
EE_RDY_vect – przerwanie gotowości pamięci EEPROM,
ANA_COMP_vect – przerwanie komparatora analogowego,
TWI_vect – przerwanie portu TWI,
SPM_RDY_vect – przerwanie gotowości pamięci FLASH.
– przerwania portu USART,
Aby umożliwić wprowadzanie danych z klawiatury należy rejestrować zdarzenie wciśnięcia
klawisza. Pozwoli to np. zapamiętać, który klawisz został ostatnio wciśnięty. W układzie
uruchomieniowym ZL3AVR znajduje się jednokońcówkowe złącze JP13, ułatwiające
zaobserwowanie wciśnięcia klawisza. Po wymuszeniu zera logicznego jednocześnie na
końcówkach K1, K2, K3 i K4 wciśnięcie któregokolwiek klawisza wymusi pojawienie się
zera logicznego na złączu JP13. Podłączenie tego złącza do wyprowadzenia zewnętrznego
przerwania mikrokontrolera pozwoli na realizację odczytu klawiatury w przerwaniu, bez
konieczności cyklicznego obserwowania jej stanu.
Aby możliwe było jednoznaczne znalezienie wciśniętego klawisza w procedurze obsługi
przerwania należy kolejno, programowo ustalać stan zera logicznego na końcówkach K1 do
K4, podłączonych do portu mikrokontrolera i obserwować stany logiczne na końcówkach W1
do W4. UWAGA!: Złącze „Mała klawiatura” (JP3) musi mieć wówczas rozwarte końcówki
(usunięta zworka) by nie powstała niebezpieczna dla mikrokontrolera sytuacja, w której
mikrokontroler próbuje wymusić stan jedynki logicznej na końcówce zwartej do masy.
Czynności do wykonania:
Przed podłączeniem zestawu wykonać następujące połączenia:
- połączenie wyświetlacza siedmiosegmentowego z portem A i B mikrokontrolera, jak w
poprzednim ćwiczeniu,
- połączenie klawiatury matrycowej z portem C mikrokontrolera, jak w poprzednim
ćwiczeniu (końcówka „PC0” z końcówką „W1”),
- za pomocą pojedynczego przewodu połączyć końcówkę złącza JP13 z wyprowadzeniem
INT0 mikrokontrolera (końcówka PD2/INT0 złącza JP19),
- jeśli złącze „Mała klawiatura” (JP3) miało zwarte końcówki należy usunąć zworkę.
Napisać program zliczający wciśnięcia dowolnego klawisza z zastosowaniem przerwań.
Inkrementacja licznika wciśnięć powinna być zawarta w procedurze obsługi przerwania
zewnętrznego. Poniższy wzór zawiera szablon programu wraz z dołączonymi, potrzebnymi
zbiorami bibliotecznymi, definicją funkcji obsługi przerwania, konfiguracją przerwania
zewnętrznego i załączeniem systemu przerwań:
#include <avr\io.h>
#include <avr\signal.h>
#include <avr\interrupt.h>
ISR(INT0_vect)
{
/* tutaj należy wpisać
zewnętrznego INT0 */
}
instrukcje
wykonywane
w
obsłudze
przerwania
int main()
{
DDRC=0xF0;
/*inicjalizacja portu C połączonego z klawiaturą*/
PORTC=0x00;
/*Inicjalizacja przerwania zewnętrznego INT0*/
MCUCR=0x02;
/*przerwanie generowane zboczem opadającym*/
GICR=0x40;
/*aktywacja przerwania zewnętrznego INT0 */
sei();
/*aktywacja systemu przerwań*/
/* tutaj należy wpisać kod programu głównego*/
}
Należy go uzupełnić o deklarację, inicjalizację, inkrementację licznika wciśnięć i jego
wyświetlanie z zastosowaniem funkcji utworzonych w poprzednich ćwiczeniach.
Przyciski klawiatury charakteryzują się występowaniem drgań zestyków, co może prowadzić
do ich kilkakrotnego łączenia i rozłączania w czasie kilkudziesięciu mikrosekund podczas
wciskania klawisza. Czy to zjawisko wpływa negatywnie na działanie napisanego programu?
Jak można temu przeciwdziałać programowo?
Poniższa funkcja realizuje odczyt kodu wciskanego klawisza w przerwaniu:
ISR(INT0_vect)
{
unsigned char kolumna=0, wiersz, kolmask=0x10;
unsigned i, wmask, pincpy;
for(i=0;i<100;i++);
/*opóźnienie na ustabilizowanie styku*/
do
{
PORTC=0x0F;
PORTC=0x0F;
PORTC=0x0F;
/*wymuszenie zer na kolumnach*/
/*w celu powtarzania testu wciśnięcia */
/*jakiegokolwiek klawisza*/
if((PINC & 0x0F) != 0x0F)
{
PORTC=0x0F | ~kolmask;
PORTC=0x0F | ~kolmask;
wiersz=0;
wmask=0x01;
do
{
pincpy=PINC;
pincpy=PINC;
if(!(pincpy & wmask))
{
keybuffer=4*kolumna+wiersz;
keypressed=1;
}
wiersz++;
wmask=wmask*2;
} while(wiersz<4);
}
kolumna++;
kolmask=kolmask*2;
} while(kolumna<4);
PORTC=0x0F;/*wpisanie zer do kolumn zapewnia wygenerowanie przerwania*/
PORTC=0x0F;/*po wciśnięciu dowolnego klawisza, ale wywołuje przerwanie */
PORTC=0x0F;/*wskutek dalszego naciskania tego samego klawisza */
GIFR=0x40;
/*wykasowanie zgloszonego przerwania*/
}
Jeśli wciśnięto klawisz funkcja przypisuje zmiennej globalnej keypressed wartość 1, a kod
wciśniętego klawisza – zmiennej globalnej keybuffer. Dzięki temu możliwe jest
sprawdzenie w programie głównym czy został wciśnięty klawisz i odczytanie jego kodu.
Zmiennej keypressed należy po odczytaniu kodu klawisza przypisać wartość 0, by zapobiec
wielokrotnej interpretacji jednego wciśnięcia klawisza. Zmienne muszą być zadeklarowane w
programie jako globalne. Przykładem prawidłowego użycia tych zmiennych jest fragment
programu sumujący kody wciskanych klawiszy i wyświetlający je.
while(1)
{
if(keypressed)
{
liczba=liczba + keybuffer;
keypressed=0;
}
disp_number(liczba);
}
Napisać program wyświetlający kod ostatnio wciśniętego klawisza, zaobserwować jakie kody
przypisano poszczególnym klawiszom. Napisać program wprowadzający do pamięci liczbę
dziesiętną, wielocyfrową z klawiatury. Zaproponować sposób zakodowania typowej
klawiatury kalkulatora z wykorzystaniem tablicy.
Sprawozdanie powinno zawierać utworzone fragmenty kodu programów z komentarzami,
odpowiedzi na pytania i własne wnioski.
Ćwiczenie 5.
Celem ćwiczenia jest nabycie umiejętności oprogramowania układów czasowo-licznikowych
oraz ich wykorzystania do realizacji różnych zadań.
W ćwiczeniu układ T1 zostanie oprogramowany tak by służył do mierzenia czasu trwania
wybranych zdarzeń z dokładnością do jednej mikrosekundy. Pozwoli to np. na ocenę czasu
wykonania fragmentów programu. Następnie układ T0 zostanie tak oprogramowany, by
generował przerwanie co 1ms. W procedurze obsługi przerwania można zrealizować
odliczanie czasu późnienia o zadaną liczbę milisekund albo odliczanie do tysiąca (pełna
sekunda), a następnie np. odliczanie sekund, minut i godzin. W ćwiczeniu nie jest
wykorzystywana klawiatura i wystarczy zrealizować jedynie podłączenie wyświetlacza
7-segmentowego.
Czynności do wykonania:
Przed podłączeniem zestawu wykonać następujące połączenia:
- połączenie wyświetlacza siedmiosegmentowego z portem A i B mikrokontrolera, jak w
poprzednich ćwiczeniach.
Napisać program sprawdzający dokładność generowania opóźnień przez utworzoną w
ćwiczeniu 2. funkcję delay. W tym celu należy wykorzystać poniższe definicje funkcji
konfiguracji i inicjalizacji pracy układu czasowego T1 oraz zatrzymania pracy T1 i pomiaru
czasu:
void start_us_timer()
{
TCCR1B=0; /*zatrzymanie timera*/
TCNT1H=0; /*wyzerowanie licznika*/
TCNT1L=0;
TCCR1A=0;
TCCR1B=0x02;
/*uruchomienie timera - zliczanie impulsów
zegara CLK_io/8: dla fQ=16MHz są to 2MHz (T=0,5us) */
}
unsigned get_time()
{
unsigned time;
TCCR1B=0;
time=TCNT1L;
time+=256*TCNT1H;
time/=2;
return time;
}
/*zatrzymanie timera*/
/*odczyt mniej znaczącego bajtu stanu licznika*/
/*odczyt bardziej znaczącego bajtu stanu licznika*/
/*skalowanie odczytu do mikrosekund*/
/*zwrocenie czasu (liczby mikrosekund)*/
Należy zauważyć, że 16-bitowy licznik T1 może zliczyć co najwyżej 65535 impulsów, co
przy wyborze jako źródła sygnału zegarowego 2MHz (CLKIO/8 dla fQ=16MHz) daje 32767
mikrosekund. Po upływie tego czasu od startu timera funkcja get_time() zwróci wartość
niepoprawną (rzeczywistą wartość modulo 32767). Fragment programu z pomiarem czasu
może wyglądać następująco:
unsigned time;
...
start_us_timer();
delay(1);
time=get_time();
Zmodyfikować funkcję delay tak, by jej wykonanie z parametrem 1 trwało prawie dokładnie
1000us. Sprawdzić jak się zmienia czas wykonania z innymi wartościami opóźnienia oraz na
skutek załączenia optymalizacji.
Następnie utworzyć funkcję int_delay o działaniu takim samym jak delay, ale z
wykorzystaniem odpowiednio oprogramowanego licznika T0 do realizacji opóźnienia. W tym
celu wykorzystać funkcję konfiguracji timera T0 i przerwania od T0 oraz funkcję obsługi
przerwania:
void start_ms_timer()
{
TCCR0=0;
/*zatrzymanie timera*/
TCNT0=0;
/*wyzerowanie licznika*/
TIMSK=0x02; /*załączenie przerwania od zrównania T0 z OCR0 */
OCR0=249;
/*ustalenie wartosci maksymalnej T0 */
TCCR0=0x0B; /*ustalenie trybu CTC i uruchomienie timera
ze zliczaniem impulsów zegara CLK_io/64:
dla fQ=16MHz jest to 1/4MHz (T=4us) */
}
ISR(TIMER0_COMP_vect)
{
ms_counter++; /*inkrementacja w przerwaniu licznika milisekund*/
}
Do prawidłowego działania programu konieczne jest, dołączenie następujących zbiorów
nagłówkowych:
#include <avr\io.h>
#include <avr\signal.h>
#include <avr\interrupt.h>
oraz zadeklarowanie zmiennej globalnej:
unsigned ms_counter;
Pomierzyć czas opóźnień generowanych przez nową realizację opóźnienia wg wzoru:
int main()
{
unsigned time;
sei();
start_ms_timer();
start_us_timer();
int_delay(5);
time=get_time();
/*aktywacja systemu przerwań*/
/*konfiguracja timera T0 do zliczania milisekund*/
/*konfiguracja timera T1 do odliczania mikrosekund*/
/*opóźnienie o 5ms realizowane z wykorzystaniem T0*/
/*pomiar czasu*/
while(1)
disp_number(time);
}
Sprawozdanie powinno zawierać utworzone fragmenty kodu programów z komentarzami,
odpowiedzi na pytania i własne wnioski.

Podobne dokumenty