Instrukcja do laboratorium 1

Transkrypt

Instrukcja do laboratorium 1
Komunikacja w mikrokontrolerach
Laboratorium
Ćwiczenie 1
Podstawy programowania,
stany uśpienia
Program ćwiczenia:
zapoznanie z regulaminem laboratorium i zasadami zaliczenia,
zapoznanie ze sprzętem laboratoryjnym i oprogramowaniem,
obsługa portów wejścia/wyjścia mikrokontrolera,
obsługa przerwań sprzętowych mikrokontrolera,
obsługa stanów uśpienia mikrokontrolera.
Zagadnienia do przygotowania:
podstawy programowania mikrokontrolerów w języku C,
obsługa portów wejścia/wyjścia mikrokontrolera,
zjawisko drgań styków przycisków – wpływ na pracę
mikrokontrolera, sposoby eliminacji,
przerwania sprzętowe mikrokontrolera (zewnętrzne oraz
licznika T/C1) – uruchamianie, obsługa,
licznik T/C1 w trybie pracy normalnym – obsługa,
stany uśpienia mikrokontrolera – konfiguracja, aktywacja,
wybudzanie.
Literatura:
[1] Wykład
[2] Dokumentacja mikrokontrolera ATmega8535, www.atmel.com.
[3] Mikrokontrolery AVR ATmega w praktyce, R. Baranowski, BTC 2005.
[4] AVR i ARM7. Programowanie mikrokontrolerów dla każdego, P.
Borkowski, Helion 2010.
[5] Mikrokontrolery AVR Język C. Podstawy programowania, M. Kardaś,
Atnel 2011.
dr inż. Piotr Markowski © 2015
Wydział Elektroniki Mikrosystemów i Fotoniki
Komunikacja w mikrokontrolerach
Zawartość instrukcji
1. Podstawy programowania AVR w języku C ......................................................................................... 1
2. Licznik T/C1.......................................................................................................................................... 3
3. Przerwania sprzętowe ......................................................................................................................... 4
4. Stany uśpienia...................................................................................................................................... 5
5. Przykładowe zadania ........................................................................................................................... 6
UWAGA! Do poprawnej obsługi mikrokontrolera niezbędne jest korzystanie z jego dokumentacji.
Instrukcja zawiera pewne uproszczenia w stosunku do rzeczywistych możliwości mikrokontrolera
oraz języka C.
1. Podstawy programowania AVR w języku C
W trakcie zajęć laboratoryjnych programowanie mikrokontrolerów będzie się odbywało przy
wykorzystaniu darmowego środowiska programistycznego AVR Studio 4 oraz darmowego
kompilatora WinAVR. Ten zestaw umożliwia kompilowanie i asemblację programów napisanych
w języku C oraz symulację ich działania w mikrokontrolerze. Trzecią aplikacją wykorzystywaną na
zajęciach będzie PonyProg2000, który umożliwia zapisywanie programu w pamięci mikrokontrolera.
Wprowadzenie do obsługi wymienionych aplikacji odbędzie się na pierwszych zajęciach.
Po utworzeniu nowego projektu AVR GCC w programie AVR Studio 4, należy napisać
odpowiedni kod, który będzie sterował pracą mikrokontrolera.
Uwaga: proszę sprawdzić, czy w menu Project -> Configuration Options -> Custom Options
ustawiono odpowiednie ścieżki dostępu:
...\WinAVR\bin\avr-gcc.exe oraz ... \WinAVR\utils\bin\make.exe
Do projektu każdorazowo należy dołączyć bibliotekę io.h dostarczoną z programem WinAVR.
Zawarto w niej m.in. definicję rejestrów specjalnych oraz instrukcji mikrokontrolera. Funkcja główna
programu zawsze musi nazywać się main(). Na listingu 1 przedstawiono uniwersalny szkielet
programu.
#include <avr/io.h>
int main(void)
{
}
List. 1. Uniwersalny szkielet programu.
1
dr inż. Piotr Markowski © 2015
Komunikacja w mikrokontrolerach
Zmienne można deklarować jako typy standardowe dla języka C lub typy specjalnie przeznaczone
dla mikrokontrolerów (z punktu widzenia mikrokontrolerów wygodniejsze jest stosowanie zmiennych
8 czy 16-bitowych), np:
int8_t
uint8_t
int16_t
uint16_t
liczba całkowita 8-bitowa ze znakiem (zakres -128...127),
liczba całkowita 8-bitowa bez znaku (zakres 0...255),
liczba całkowita 16-bitowa ze znakiem (zakres -32768...32767),
liczba całkowita 16-bitowa bez znaku (zakres 0...65535).
Ustalanie wartości rejestrów sterujących pracą mikrokontrolera wykonuje się np. w następujący
sposób:
DDRA = 0b10101010;
//przypisanie wartości w kodzie NKB
DDRB = 0xaa;
//przypisanie wartości w kodzie szesnastkowo-dziesiętnym
DDRC = 170;
//przypisanie wartości w kodzie dziesiętnym
PORTD |= (1<<PD2);
//ustawienie bitu PD2 na ‘1’ z zachowaniem wartości pozostałych bitów
PORTD |= ((1<<2)|(1<<PD1));
//ustawienie bitu nr ‘2’ (PD2) oraz PD1 (nr ‘1’) na ‘1’ z zachowaniem...
PORTD &= ~(1<<PD3)&~(1<<PD2);
//wyzerowanie bitu nr ‘2’ (PD2) oraz nr ‘3’ (PD3) z zachowaniem...
PORTA ^= 0xff;
//zanegowanie wszystkich bitów rejestru PORTA (XOR bitów z wartością ‘1’)
TCNT1 += 1;
//zwiększenie stany rejestru o 1;
PORTB = TCNT0;
//przepisanie zawartości rejestru specjalnego do innego;
uint8_t zmienna = OCR0; //przepisanie zawartości rejestru do zmiennej ‘zmienna’
Należy pamiętać, że nazwy rejestrów specjalnych muszą być wpisywane wielkimi literami.
Sprawdzenie aktualnego stanu bitu w rejestrze można wykonać np. w taki sposób:
if (PIND & (1<<PD0))
{}
if (!(PIND & (1<<PD0)))
{}
//warunek prawdziwy gdy na bicie PD0 w rejestrze PIND jest ‘1’
//warunek prawdziwy gdy na bicie PD0 w rejestrze PIND jest ‘0’
Część programu mającą się wykonywać wielokrotnie można umieścić np. w pętli nieskończonej
for() lub while(), jednak w przypadku pętli for() występują problemy przy symulacji w AVR Studio 4.
while (1)
{}
for (;;)
{}
Opóźnienie wykonywania programu można spowodować wywołując funkcję _delay_us() lub
_delay_ms(). Pierwsza powoduje opóźnienie w mikrosekundach (czas trwanie podajemy w nawiasie),
druga w milisekundach. Aby funkcje działały należy w nagłówku projektu dodać wpisy:
#define F_CPU 1000000L
#include <util/delay.h>
//1000000L to przykładowa częstotliwość zegara taktującego nasz mikrokontroler
Biblioteka delay.h zawiera definicje przedstawionych funkcji, ale do prawidłowego działania
wymaga wcześniejszego zdefiniowania stałej F_CPU.
Na listingu 2 przedstawiono kod przy kładowego programu. Załóżmy, że do nóżek wejściowych
PD0 oraz PD1 mikrokontrolera podpięto przyciski, łączące nóżki z masą. Do portu A podłączono
8 LED.
2
dr inż. Piotr Markowski © 2015
Komunikacja w mikrokontrolerach
1
2
3
#include <avr/io.h>
#define F_CPU 1000000UL
#include <util/delay.h>
4
int ustaw(void)
{
DDRD &= ~((1<<PD0)|(1<<PD1));
PORTD |= (1<<PD0)|(1<<PD1);
DDRA = 0xff;
}
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//dołaczenie biblioteki
//ustalenie częstotliowści dla delay.h
//dołaczenie biblioteki
int main(void)
{
uint8_t a = 0xaa;
ustaw();
while (1)
{
_delay_ms(500);
if (!(PIND & (1<<PD0)))
PORTA ^= a;
else if (!(PIND & (1<<PD1)))
PORTA ^= ~a;
else
PORTA = a;
}
}
//PD0 oraz PD1 jako wejścia ('0')
//pull-up na PD0 oraz PD1
//cały port A jako wyjścia ('1')
//zmienna 'a' - stan początkowy 0b10101010
//wywołanie funkcji 'ustaw()'
//pętla nieskończona
//czy bit PD0 = 0? (czy przycisk wciśnięty?)
//wyświetl na porcie A: (PORTA xor a) => migają diody nr. 1,3,5,7
//czy bit PD1 = 0? (czy przycisk wciśnięty?)
//wyświetl na porcie A: (PORTA xor ~a) => migają diody nr. 0,2,4,6
//wyświetl na porcie A: a
List. 2. Kod przykładowego programu.
W pierwszej kolejności definiowana jest zmienna ‘a’ i przypisywana jest jej wartość początkowa
„0b10101010”. Następnie wywoływana jest funkcja ustaw(), w której ustawiany jest pull-up dla wejść
PD0 oraz PD1, port A definiowany jest jako wyjściowy. W pętli nieskończonej while realizowane jest
opóźnienie 500 ms oraz sprawdzany jest aktualny stan bitów PD0 oraz PD1 w rejestrze PIND. Jeżeli
nie jest wciśnięty żaden przycisk wykonuje się warunek z linii 18 – wyświetlenie na LED wartości
zmiennej‘a’. Jeżeli zostanie naciśnięty i przytrzymany przycisk PD0 na LED wyświetli się wartość
„0b10101010”. Pętla while wykona się kolejny raz, zatem po 500 ms stan na LED zmieni się na
„0b00000000”, itd. Jeżeli przycisk PD0 zostanie zwolniony, a wciśnięty zostanie przycisk PD1 na
LED wyświetli się wartość zanegowane ‘a’ – „0b01010101”, która po 500 ms zmieni się na
„0b00000000”, itd. Zatem wciśnięcie jednego z przycisków powoduje miganie LED. W ramach
przygotowania do zajęć proszę przetestować działanie programu w symulatorze AVR Studio 4.
2. Licznik T/C1
Dokładne informacje na temat pracy liczników zawiera dokumentacja mikrokontrolera [2] oraz
materiały z wykładu [1]. Na zajęciach laboratoryjnych wykorzystywany będzie tryb pracy normalny
lub CTC licznika T/C1. Licznik będzie pracował w trybie zliczania impulsów/odliczania czasu (nie w
trybie generowania przebiegu prostokątnego). W związku z tym w ustawieniach rejestrów sterujących
pracą licznika (TCCR1A, TCCR1B – s. 110 [2]) na bitach COM1xx, FOC1x, ICNC1 oraz ICES1
należy wpisać ‘0’. Bity WGM1x należy ustawić adekwatnie do wybranego trybu pracy.
W trakcie laboratorium będzie konieczna znajomość funkcji i sposobu użycia rejestru zliczającego
TCNT1 oraz rejestrów porównawczych OCR1A i OCR1B, a także rejestru odblokowującego
przerwania liczników (TIMSK). Wyczerpujące informację o wymienionych rejestrach można znaleźć
w literaturze do laboratorium [1-5].
Zagadnienie przerwań sprzętowych licznika T/C1 zostanie poruszone w punkcie 3.
3
dr inż. Piotr Markowski © 2015
Komunikacja w mikrokontrolerach
3. Przerwania sprzętowe
Mikrokontroler Atmega8535 stosowany na zajęciach laboratoryjnych posiada 21 przerwań
sprzętowych omówionych na stronie 46 dokumentacji [2]. W trakcie zajęć będą wykorzystywane
przerwania zewnętrzne INT0, INT1, INT2 oraz przerwania liczników.
W celu odblokowania wybranego przerwania należy:
1. ustalić jakie zdarzenie ma wywołać przerwanie (rejestry MCUCR, MCUCSR);
2. wpisać wartość ‘1’ na odpowiednim bicie w odpowiednim rejestrze kontrolnym (GICR,
TIMSK)
3. odblokować przerwania globalnie (SREG, flaga I)
Krok (1) wykonuje się jedynie dla przerwań zewnętrznych. Zgodnie z tab. 35 oraz 36 na stronie 68
dokumentacji [2] wybieramy sposób aktywacji przerwania zewnętrznego:
• poziomem niskim na nóżce;
• dowolnym zboczem (narastającym lub opadającym);
• zboczem opadającym;
• zboczem narastającym.
Krok (2) to ustawienie bitów w rejestrze GICR dla przerwań zewnętrznych (s. 69 dokumentacji
[2]) lub w rejestrze TIMSK dla liczników (s. 85, 115, 133 dokumentacji [2]).
Krok (3) wykonujemy wywołując funkcję sei().
Aby obsługiwać przerwania w AVR GCC należy do projektu dołączyć bibliotekę interrupt.h.
Zdefiniowano w niej wektory przerwań sprzętowych mikrokontrolerów AVR i przypisano im
ustandaryzowane nazwy. W tab. 1 zebrano nazwy wektorów dla przerwań zewnętrznych oraz
liczników mikrokontrolera ATmega8535. Pełną listę wektorów można znaleźć np. w internecie
(np. http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__interrupts.html).
Obsługa przerwań sprowadza się do ich skonfigurowania i uruchomienia (jak opisano powyżej)
oraz do napisania funkcji obsługującej dla każdego. Funkcja ta musi nosić nazwę:
ISR(xxx)
{}
przy czym zamiast ‘xxx’ wpisujemy nazwę wektora z tabeli 1.
Tab. 1. Nazwy wybranych wektorów przerwań
Wektor
Opis
przerwanie zewnętrzne INT0
INT0_vect
przerwanie zewnętrzne INT1
INT1_vect
przerwanie zewnętrzne INT2
INT2_vect
przepełnienie licznika T/C0
TIMER0_OVF_vect
zrównanie licznika T/C0 (TCNT0 = OCR0)
TIMER0_COMP_vect
przepełnienie licznika T/C1
TIMER1_OVF_vect
TIMER1_COMPA_vect zrównanie licznika T/C1 (TCNT1 = OCR1A)
TIMER1_COMPB_vect zrównanie licznika T/C1 (TCNT1 = OCR1B)
przepełnienie licznika T/C2
TIMER2_OVF_vect
zrównanie licznika T/C2 (TCNT2 = OCR2)
TIMER2_COMP_vect
Na listingu 3 przedstawiono szkielet programu zawierającego obsługę przerwania od przepełnienia
licznika T/C0 oraz przerwania zewnętrznego INT0.
4
dr inż. Piotr Markowski © 2015
Komunikacja w mikrokontrolerach
1
2
#include <avr/io.h>
#include <avr/interrupt.h>
3
int main(void)
{
DDRD &= ~(1<<PD2);
PORTD |= (1<<PD2);
MCUCR |= (1<<ISC00);
GICR |= (1<<INT0);
TIMSK |= (1<<TOIE0);
sei();
TCCR0 |= (1<<CS00);
4
5
6
7
8
9
10
11
//PD2 jako wejście
//pull-up na PD2
//reakcja INT0 na dowolne zbocze
//odblokowanie przerwania INT0
//odblokowanie przerwania od przepełnienia T/C0
//globalne odblokowanie przerwań
//uruchomienie licznika T/C0
while(1)
{}
}
12
13
14
15
ISR(INT0_vect)
{
PORTA=0x81;
}
ISR(TIMER0_OVF_vect)
{
PORTA=0x18;
}
//podprogram obsługi INT0
//zrób coś
//zrób coś innego
List. 3. Kod przykładowego programu.
4. Stany uśpienia
Uśpienie mikrokontrolera przede wszystkim umożliwia zmniejszenie ilości energii pobieranej
przez system. Jest to możliwie dzięki odcięciu taktowania od części układów peryferyjnych.
W rezultacie przestają one pracować, a co za tym idzie, pobierać energię. W pewnych sytuacjach
zasadne jest też uśpienie mikrokontrolera, aby zminimalizować szum elektromagnetyczny przez niego
wytwarzany (np. konwersja analog.-cyfr. lub cyfr.-analog.). Na procedurę obsługi stanów uśpienia
składa się:
• wybór odpowiedniego stanu uśpienia (np. w Atmega8535 mamy 6 dostępnych opcji);
• odblokowanie opcji uśpienia mikrokontrolera;
• uśpienie mikrokontrolera;
• wybudzenie mikrokontrolera.
W tabeli 2 zamieszczono wybrane stany uśpienia mikrokontrolera ATmega8535. Podano także
wykaz sygnałów, które mogę układ wybudzić. Należy podkreślić, że tylko ściśle określone zdarzenia
są do tego zdolne – wybrane przerwania sprzętowe. Przed uśpieniem mikrokontrolera należy
sprawdzić, czy aktywowano te przerwanie/przerwania. Inaczej konieczny będzie reset
mikrokontrolera.
Do wyboru określonego trybu uśpienia, a także do jego aktywowania służą odpowiednio funkcje
set_sleep_mode(xxx) (zamiast xxx należy wpisać wektor stanu uśpienia z tabeli 2) oraz sleep_mode()
dostępne w bibliotece sleep.h. Na listingu 4 przedstawiono przykładowy kod wprowadzający
mikrokontroler w stan uśpienia i wybudzający go przy pomocy przerwania INT0.
5
dr inż. Piotr Markowski © 2015
Komunikacja w mikrokontrolerach
Tab. 1. Nazwy wybranych wektorów przerwań
Tryb uśpienia
Nazwa wektora
SLEEP_MODE_IDLE
Idle
SLEEP_MODE_ADC
ADC –
redukcja
szumów
Power down
Power save
1
2
3
4
5
6
7
8
9
10
11
12
13
Sygnał wybudzający
dowolne przerwanie
- przerw. zewn. aktywowane poziomem niskim,
- rozpoznanie adresu TWI (I2C),
- przerwania licznika T/C2,
- EEPROM gotowy,
- zakończenie konwersji ADC.
SLEEP_MODE_PWR_ DOWN - przerw. zewn. aktywowane poziomem niskim,
- rozpoznanie adresu TWI (I2C).
SLEEP_MODE_PWR_ SAVE - przerw. zewn. aktywowane poziomem niskim,
- rozpoznanie adresu TWI (I2C),
- przerwania asynchroniczne licznika T/C2.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
//biblioteka trybów uśpienia
int main (void)
{
DDRD=0;
PORTD=0xFF;
MCUCR=0;
//przerwanie INT0 aktywowane poziomem niskim
GICR|=(1<<INT0);
set_sleep_mode(SLEEP_MODE_IDLE); //wybór trybu uśpienia
sei();
sleep_mode();
//uśpienie mikrokontrolera
while (1)
{}
}
ISR(INT0_vect)
{}
List. 4. Kod przykładowego programu.
5. Przykładowe zadania
a) napisz program wykonujący różne operacje arytmetyczne (+, -, *, /) oraz logiczne (AND, OR,
XOR, <<, ~) na liczbach 8-bitowych oraz wysyłaj wyniki na nóżki portu A;
b) napisz program wysyłający różne kombinacje bitowe na port B, w zależności od stanu 4 nóżek
wejściowych na porcie D (zakładamy 8 LED podpięte do portu A i 4 przyciski podpięte do
portu D);
c) przy pomocy licznika T/C0 odlicz 50 impulsów zewnętrznych, doprowadzanych przez wejście T0;
d) przy pomocy licznika T/C1 odlicz czas 1 min.
e) przy pomocy licznika T/C1 odliczaj czas 1 min; co 60s zwiększaj liczbę wysyłaną na port
wyjściowy A (domyślnie – 8 LED); wykorzystaj przerwanie od doliczenia do zadanej wartości
(OCIE1A lub OCIE1B);
f) napisz program obsługujący przerwania zewnętrzne INT0 oraz INT1, wysyłający różne
kombinacje bitowe na port B, w zależności od tego, które przerwanie zostało wywołane;
g) wprowadź mikrokontroler w stan uśpienia IDLE, następnie wybudź go przy użyciu przerwania
zewnętrznego.
6
dr inż. Piotr Markowski © 2015

Podobne dokumenty