Warsztaty AVR - ATmega8.pdf

(327 KB) Pobierz
353478641 UNPDF
Podstawowe urządzenia peryferyjne
mikrokontrolera ATmega8
Spis treści
1. Konfiguracja pinów....................................................................................................................................2
2. ISP..............................................................................................................................................................2
3. I/O Ports.....................................................................................................................................................3
4. External Interrupts......................................................................................................................................4
5. Analog Comparator....................................................................................................................................6
6. Analog-to-Digital Converter......................................................................................................................6
7. USART.......................................................................................................................................................6
8. EEPROM...................................................................................................................................................6
9. SPI..............................................................................................................................................................7
10. Timer/Counter..........................................................................................................................................7
10.1.Timer/Counter0.................................................................................................................................7
10.2.Timer/Counter1.................................................................................................................................8
11. AVR Libc..................................................................................................................................................9
11.1.Funkcje opóźniające..........................................................................................................................9
11.2.Pamięć Flash....................................................................................................................................10
11.3.Stan uśpienia....................................................................................................................................10
1
1. Konfiguracja pinów
ATmega8 posiada 28 pinów, z czego 23 są programowalnymi pinami wejścia-wyjścia.
VCC Zasilanie 4.5-5.5V lub 2.7-5.5V (wersja ATmega8L)
GND Masa
AVCC Zasilanie modułu A/D
AREF Wejście/wyjście napięcia referencyjnego dla modułu A/D
PB5..0, PC5..0, PD7..0 Programowalne porty, które mogą posiadać kilka funkcji.
PC6 (RESET) Standardowo działa jako sygnał RESET, może jednak być zaprogramowany
przez odpowiedni fuse bit, jako zwykły port, ale zablokuje to późniejsze
programowanie przez SPI. Sygnał jest aktywny stanem niskim, więc w czasie
normalnej pracy ma być podawany sygnał wysoki. Zapewnia to wewnętrzny
pull-up, dlatego reset nie musi zostać podłączony lub może zostać podłączony
bezpośrednio do switch'a zwierającego do masy.
PB7..6 (XTAL2..1) Standardowo działa jako zwykły port, ale po zaprogramowaniu odpowiednich
fuse bitów można tutaj podpiąć zewnętrzny kwarc.
2. ISP
ISP (In-System Programming) umożliwia programowanie układu nawet już wewnątrz zbudowanego
urządzenia. Programowanie najczęściej odbywa się przez interface SPI. Wymaga on podpięcia układu do
programatora przez kilka przewodów:
RESET PC6 Reset układu, wymuszenie programowania
SCK
PB5 Zegar do transmisji
MISO
PB4 Wyjście danych
MOSI
PB3 Wejście danych
GND
Masa
VCC
Zasilanie układu (jeżeli układ nie posiada innego źródła zasilania)
2
353478641.016.png 353478641.017.png 353478641.018.png 353478641.019.png 353478641.001.png
Ten interface umożliwia programowanie pamięci programu (FLASH), pamięci EEPROM, lock bitów
oraz fuse bitów. Lock bity programuje się raczej rzadko, głównie w urządzeniach przeznaczonych do
sprzedaży, aby zablokować dostęp do pamięci programu. Czasami istnieje potrzeba zaprogramowania
fuse bitów, najczęściej aby zmienić źródło lub częstotliwość zegara. Dokładny opis fuse bitów w
rozdziale „Memory Programming” dokumentacji.
3. I/O Ports
Najczęściej używanymi peryferiami są porty wejścia-wyjścia. Można dzięki nim bezpośrednio sterować
stanami logicznymi na portach lub je odczytywać. Do ich obsługi służy kilka rejestrów (gdzie x = B, C
lub D; n = 7..0):
DDR x
7 6 5 4 3 2 1 0
DD x 7 DD x 6 DD x 5 DD x 4 DD x 3 DD x 2 DD x 1 DD x 0
Określa kierunek portu. 0 na bicie DD xn oznacza, że port P xn jest wejściem, 1 oznacza wyjście.
PORT x
7 6 5 4 3 2 1 0
PORT x 7 PORT x 6 PORT x 5 PORT x 4 PORT x 3 PORT x 2 PORT x 1 PORT x 0
PORT xn ma dwa znaczenia:
- gdy DD xn == 1 (wyjście), to bit PORT xn steruje wyjściem portu P xn .
- gdy DD xn == 0 (wejście) i PORT xn == 1, to zostaje włączony wewnętrzny rezystor pull-up.
PIN x
7 6 5 4 3 2 1 0
PIN x 7 PIN x 6 PIN x 5 PIN x 4 PIN x 3 PIN x 2 PIN x 1 PIN x 0
PIN xn służy do odczytu stanu na porcie xn .
Przykłady
A. Chcielibyśmy zapalać i gasić diodę LED na porcie PB0.
Na początek ustawiamy bit DDB0 na 1, czyli port PB0 na wyjście
(wystarczy wykonać to tylko raz).
DDRB |= (1<<DDB0);
Następnie, gdy chcemy zapalić diodę ustawiamy na PB0 stan 0, aby płyną prąd, wiec PORTB0 na 0.
PORTB &= ~(1<<PORTB0);
Aby zgasić należy ustawić PORTB0 na 1.
PORTB |= (1<<PORTB0);
B. Odczytujemy wciśnięcie przycisku na porcie PD2.
Na początek ustawiamy bit DDD2 na 0, czyli port PD2 na wejście
(wystarczy wykonać to tylko raz).
DDRD &= ~(1<<DDD2);
3
353478641.002.png 353478641.003.png 353478641.004.png 353478641.005.png 353478641.006.png 353478641.007.png 353478641.008.png 353478641.009.png 353478641.010.png 353478641.011.png 353478641.012.png
Musimy włączyć wewnętrzny pull-up, ponieważ, gdy przycisk jest
rozłączony mielibyśmy nieokreślony stan (wystarczy wykonać to tylko
raz).
PORTD |= (1<<PORTD2);
Teraz możemy sprawdzać czy przycisk został wciśnięty za pomocą
bitu PIND2.
if (PIND & (1<<PIND2)) {
// stan 1 na porcie => przycisk puszczony
} else {
// stan 0 na porcie => przycisk wciśnięty
}
C. Odczytujemy wyjście z innego układu cyfrowego podłączonego do PD2.
Procedura wygląda tak samo jak dla przycisku, z tym wyjątkiem, że nie jest nam potrzebny pull-up, wiec
ustawiamy 0 na PORTD2.
D. Zapisujemy bit na wejście innego układu cyfrowego.
Procedura wygląda tak samo jak dla zapalania LED'a.
4. External Interrupts
Niekiedy na pewne zdarzenia musimy zareagować natychmiast. Do tego służą przerwania. Gdy wystąpi
określone zdarzenie procesor przerywa wykonywanie aktualnego kodu i przeskoczy natychmiast do
określonej funkcji, gdzie programista może obsłużyć to zdarzenie. Po jej zakończeniu procesor powraca
do poprzednio wykonywanego kodu. Przerwania mogą być wewnętrzne (generowane przez peryferia) lub
zewnętrzne (generowane przez porty).
ATmega8 posiada dwa przerwania zewnętrzne: INT0 (PD2) oraz INT1 (PD3). Do ich obsługi służą trzy
rejestry opisane w dokumentacji w rozdziale „External Interrupts”.
Przykład
Zliczmy ilość zmian stanu na porcie INT0 (PD2).
#include <avr/io.h>
#include <avr/interrupt.h>
volatile long i;
ISR(INT0_vect) {
i++;
}
int main() {
i = 0;
DDRD &= ~(1<<DDD2);
PORTD &= ~(1<<PORTD2);
MCUCR = (MCUCR & (~(3<<ISC00))) | (1<<ISC00);
GICR |= (1<<INT0);
sei();
while (1);
return 0;
}
4
353478641.013.png 353478641.014.png
volatile long i;
Zmienna, która zawiera wartość licznika. Została zadeklarowana z atrybutem „volatile”, aby pominąć
optymalizację. Zabezpiecza to przed np. taką sytuacją: W programie czekamy, aż licznik przekroczy
1000: while (i<=1000); . Kompilator podczas optymalizacji może przenieść zmienną i do rejestru, w
tym czasie przerwanie aktualizuje zmienną i w RAM'ie, rejestr się nie zmienia, wiec pętla się będzie
wykonywać bez końca.
ISR(INT0_vect) {
i++;
}
Tak wygląda najprostsza deklaracja przerwania: zamiast typu, nazwy i parametrów funkcji wpisujemy
ISR( xxx _vect), gdzie xxx to nazwa przerwania. W ciele funkcji przerwania zwiększamy zmienną i, nic nie
zwracamy.
DDRD &= ~(1<<DDD2);
PORTD &= ~(1<<PORTD2);
Ustawiamy port na wejście bez pull-up'a.
MCUCR = (MCUCR & (~(3<<ISC00))) | (1<<ISC00);
Ustawiamy, aby przerwanie było wywoływanie na obu zboczach.
GICR |= (1<<INT0);
Włączamy przerwanie INT0.
sei();
Pozwalamy na wykonywanie przerwań. Po starcie wykonywanie przerwań jest zabronione. Możemy
wywołać makro sei(), aby to zmienić. Makro cli() ponownie zabrania wykonywania. Po wywołaniu cli()
nie zostanie wykonane żadne przerwanie, zaległe przerwania wykonają się dopiero po sei().
while (1);
Nieskończona pętla. Wszystko robi za nas przerwanie.
UWAGA!!! W nowszych wersjach avr-gcc można spotkać bug'a w obsłudze przerwań. Ujawnia się w
bardziej skomplikowanych funkcjach obsługi przerwania. Można go ominąć pisząc, np.:
ISR(INT0_vect) {
asm volatile (
"push r18\n push r19\n push r20\n"
"push r21\n push r22\n push r23\n"
"push r24\n push r25\n push r26\n"
"push r27\n push r30\n push r31\n"
);
// Tutaj kod przerwania
// ...
asm volatile (
"pop r31\n pop r30\n pop r27\n"
5
353478641.015.png
Zgłoś jeśli naruszono regulamin