Wstęp
Pomimo, iż mikro-kontrolery AVR powoli odchodzą w zapomnienie na rzeczy o wiele szybszych układów ARM oraz coraz większej popularności FPGA postanowiłem pozostawić rozdział odnośnie tych mikro-klocków na swojej stronie, gdyż do prostych celów nadal możemy je śmiało wykorzystywać.
Przygotowanie do programowania
Poniżej przedstawię listę programów (dla systemu Windows) które należy zdobyć aby zacząć naukę programowania mikro-kontrolerów AVR
Narzędzia użyte do programowania
- Kompilator  -  AVR STUDIO wersja 4.13 z serwispakiem I
aby zainstalować na Windows2000 wymagana jest aktualizacja Windows Instaler
- Programator  -  zgodny z STK200
- Program ładujący pliki HEX  -  PonyProg2000
- Program do testowania RS232Â Â -Â Â (terminal) Herkules Setup Utility
- Środowisko uruchomieniowe  -  Symulator Atmega16 w AVR Studio (W rzeczywistym procku po resecie rejestry przyjmują przypadkowe wartości, tak samo pamięć. W symulatorze zapomniano o tym szczególe i po kliknięciu reset zerują się wszystkie rejestry -  ZAWSZE INICJUJ UŻYWANE ZMIENNE)
Ważną czynnością przed rozpoczęciem pisania programu jest skonfigurowanie mikro-kontrolera do pracy z zewnętrznym rezonatorem kwarcowym. Poniższy screen przedstawia jak ustawić bity fuses za pomocą programu PonyProg2000
Programowanie właściwe - urządzenie pomiarowe
Na początku programu napisanego w asemblerze należy zawsze umieścić wektory przerwań
Program Główny
| Wektory przerwań |
;-----------------------
;poczÄ…tek segmentu kodu
;-----------------------
.cseg
;---------------------------------------------
;wektory obsługi przerwań
;---------------------------------------------
jmp RESET; skok po Reset
.org $00E
jmp TIMER1_COMPB; skok do procedury obsługi przerwania timera
.org $016
jmp USART_RXC; skok do procedury obsługi przerwania "znak odebrany"
.org $1c
jmp adc_cc; skok do procedury obsługi przerwania zakończona konwersja A/C
|
W instrukcji do kontrolera AVR znajduje się szczegółowy opis przerwań mikro-kontrolera. Ja tutaj umieściłem tylko te przerwania, z których będę korzystał w programie. Wektor przerwań to po prostu określony adres pamięci programu, pod który procek skacze w momencie wystąpienia przerwania. Właśnie w tym miejscu umieszczamy skok do naszej procedury obsługi przerwania. Jeżeli tego nie zrobimy a przerwanie nastąpi to zwyczajnie się procek wysypie. Do określenia, w którym miejscu umieścić skok służy nam dyrektywa .org
Konfiguracja układu transmisji szeregowej
Procedurę która odpowiada za inicjację i wykorzystanie rs232 umieściłem w oddzielnym pliku.
Oprogramowanie kliknięcia przycisku:
| usart.asm |
USARTINIT:
clr r16 ;Set baud rate
out UBRRH, r16
ldi r16,3 ;Set baud rate
out UBRRL, r16
ldi r16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);Enable receiver and transmitter, oraz włączenie przerwania dane odebrane
out UCSRB,r16
ldi r16, (1<<URSEL)|(3<<UCSZ0) ;Set frame format: 8data, 1stop bit
out UCSRC,r16;
ret
usart_send_B: ;wysyłanie przez odpytywanie
sbis UCSRA,UDRE ;jeśli usart gotowy pomin instrukcje skoku i wstaw dane do wyslania
rjmp usart_send_B
out UDR,r16 ;Put data (r16) into buffer, sends the data
ret
send_msg:
lpm r16,Z+ ;Pobierz bajt z pamięci programu i zwieksza o 1 indeks
tst r16 ;Sprawdź czy zero
breq msg_end ;Jesli tak to koniec wysyłania
rcall usart_send_B ;Jesli bajt nie zerowy to wysli
rjmp send_msg ;I skocz do poczÄ…tku programu w celu pobrania kolejnego bajtu
msg_end:
ret
|
W powyższym pliku procedura USARTINIT: odpowiada za inicjację portu szeregowego.
Rejestry mikro-kontrolera
- UBRR - Usart Boud Rate Register - rejestr do którego zapisujemy prędkość transmisji szeregowej, aby uzyskać prędkość transmisji 115200 b/s przy kwarcu 7,3728 [MHz] do rejestru wpisujemy 3(patrz manual do Atmega16)
- UCSRB - Usart Control Status Register B - W tym rejestrze włączamy bity odpowiedzialne za włączenie nadawania (bit TXEN) i odbioru (bit RXEN), oraz bit odpowiedzialny za generowanie przerwania gdy usart odebrał dane (bit RXCIE).
- UCSRC - Usart Control Status Register C - aby dokonać zapisu do tego rejestru musi być ustawiony bit URSEL, bierze się to z stąd, że dwa rejestry konfiguracyjne mają ten sam adres w przestrzeni I/O mikro-kontrolera, i ten bit decyduje gdzie trafi dana (nie wiem dlaczego ale tak jest)
Procedura usart_send_B: odpowiada za wysłanie jednego bajtu. Procedura wysłania odbywa się następująco: program najpierw testuje bit UDRE (czy USART gotowy) jeśli nie to wykonuje się instrukcja skoku do początku procedury i ponowny test. Jeśli warunek testowany jest spełniony wówczas instrukcja skoku będzie pominięta i dana z R16 zostanie umieszczona w buforze UDR.
send_msg: wysyła przez port szeregowy wiadomość tekstową zakończoną zerem. I tak aby skorzystać z tej procedury należy najpierw załadować rejestr Z adresem wiadomości, wiadomość musi być zakończona zerowym bajtem.
Deklaracja wiadomości
| ela:.db "JESTEM Karta ELAP",13,10,"UrzÄ…dzenie...",13,10,0 |
Następnie w programie:
| ldi_16 2*ela,zl,zh ;załadowanie z rejestru Z adresem wiadomości |
| rcall send_msg ;wywołanie procedury wysyłającej |
Bufor FIFO
Bufor fifo (mojego pomysłu) jest zadeklarowany w pamięci SRAM mikro-kontrolera zarówno zapis jak i odczyt odbywają się w sposób pokazany na rysunku (wskaźniki odczytu jak i zapisu działają niezależnie). Jeżeli wskaźnik zapisu lub odczytu osiągnie wartość max to operacja zaczyna się od początku bufora Dodatkowa zmienna, którą zwiększamy o 1 przy zapisie do FIFO a zmniejszamy przy odczycie służy do kontroli zajętości Bufora
Główny blok programu
Główny blok programu napisany jest w oparciu o gniazda, w którym zawsze wykonuje się jedno. Wybór gniazda dokonujemy przy pomocy zmiennej sterującej Ls. Rejestr "Z" jest załadowany adresem etykiety "skoki:", do której dodana jest wartość LS i wykonywany jest skok do instrukcji oddalonej o LS od etykiety
idea działania programu - gniazda+zmienna sterująca:
loop:
ldi_16 skoki,zl,zh ;załaduj rejestr Z adresem procedury skoki (makro)
add zl,ls ;dodaj wartość LS do rejestru Z
clr r16
adc zh,r16
ijmp ;Wykonaj skok do adresu określonego w Z
skoki:
rjmp loop ;ls=0
rjmp aply_ ;ls=1
rjmp odczyt ;ls=2
rjmp wyjscie2 ;ls=3
rjmp wyjscie1 ;ls=4
rjmp who_is ;ls=5
rjmp pressent ;ls=6
rjmp _tyb_inf ;ls=7
rjmp _number_sample ;ls=8
rjmp _sampl_tim_info ;ls=9 |
Konfiguracja Timera T1 oraz przetwornika AC
Karta miała działać w oparciu o przerwania Timera (Porównanie z rejestrem TCR1B). Rejestr ten jest uaktualniamy podczas konfiguracji i ma wpływ na częstotliwość próbkowania karty. Przerwanie timera to jest ustawione jako źródło wyzwalania Przetwornika AC. Po zakończeniu konwersji uaktywnione jest przerwanie przetwornika, w którego to procedurze obsługi dane z rejestrów przetwornika są przeniesione do bufora umieszczonego w pamięci SRAM mikro-kontrolera.
Konfiguracja Timera:
| Timer1.asm |
Timer1_init:
;inicjacja timera pierwszego 16bit
;timer ma generować przerwania dla porównania z rejestrem OCR1B
in r16,sfior ;jeśli sie pojawi to reset preskalera
sbr_ r16, psr10
out sfior,r16;
ldi r16, (1<<WGM12)|(1<<cs10)|(1<<cs12)
; ustawienie maxymalnej wartości jako OCR1A bit WGM12 - timer wyłączony
out TCCR1B, r16;
;ldi r16, (1<<OCF1B)
;out tifr,r16
ldi r16, (1<<OCIE1B) ; włączenie przerwania od porównania z rejestrem OCR1B
out TIMSK,r16
ret
|
Aby skonfigurować Timer należy:
- Ustawić bit psr10 w rejestrze SFIOR - reset preskalera(nie bardzo konieczne)
- W rejestrze TCCR1B ustawić bity WGM12 (ustawienie maksymalnej wartości jako OCR1A), cs10 i cs12 te dwa bity odpowiadają za częstotliwość taktowania Timera
- W rejestrze TIMSK ustawić bit OCIE1B (włączenie przerwania od porównania z rejestrem OCR1B).
Konfiguracja przetwornika ACDC :
| adc.asm |
; do przetwornika ADC
ADC_init:
PUSH R16
CLR R16
out Admux, r16
ldi r16, (1<<ADEN)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)
;ADEN - włączony przetwornik A/C
;ADIE - włączenie generowania przerwania kiedy konwersja zakoñczona
;ADSC - start przetwarzania A/C - bit wymagany równierz w trybie autowyzwalania
;ADATE - włączenie autowyzwalania - źródło wyzwalania zdefiniowane w rejestrze SFIOR
;ADPS2,ADPS1 - wybór częstotliwości taktowania przetwornika u nas = częstotliwość zegara/64
out ADCSRA,R16
;teraz ustawimy źródło wyzwalania przetwornika jako przerwanie timera1-porównanie;
;ADST2-1, ADST1-0' ADST0-1 Timer/Counter1 Compare Match B
in r16,SFIOR
ldi tmp2, (1<<ADTS0)|(1<<ADTS2)
or r16,tmp2
out SFIOR,r16
POP r16
RET
|
Aby skonfigurować przetwornik A/C:
- Wyzerować rejestr ADMUX (wybieramy pierwszy pin portu PA jako wejście do przetwornika AC)
- W rejestrze ADCSRA ustawić bity: ADEN (włączony przetwornik A/C), ADIE (włączenie generowania przerwania kiedy konwersja zakończona), ADSC(start przetwarzania A/C - bit wymagany również w trybie auto wyzwalania), ADATE (włączenie auto wyzwalania - źródło wyzwalania zdefiniowane w rejestrze SFIOR), ADPS2,ADPS1(wybór częstotliwości taktowania przetwornika u nas = częstotliwość zegara/64)
- W rejestrze SFIOR ustawić bity ADTS0 i ADTS2 (jako źródło wyzwalania określamy przerwanie timera TIMER1_COMPB).
Uwagi końcowe - urządzenie pomiarowe
Więcej informacji o programie można dostać czytając komentarze w plikach źródłowych
Kartę można skonfigurować przy pomocy terminala i tak
- "a" Start zbierania danych
- "c" Wejście do trybu konfiguracji
- "t" Ustawienie trybu pracy karty
- "T" Ustawienie okresu próbkowania jako wielokrotność 140 us
- "n" Ustawienie ilości zebranych próbek
- "b" Wyjście z trybu konfiguracji
- "?" Przedstawienie siÄ™ karty
- "e" koniec zbierania danych
Programowanie właściwe - regulator mocy PWM
To będzie bardzo prosty programik mający na celu zmierzyć napięcie na potencjometrze i ta tej podstawie wygenerować przebieg PWM o odpowiednim współczynniku wypełnienia. Program składa się z procedury inicjującej przetwornik AC - przetwornik pracuje w trybie wolnego zbierania danych
Konfiguracja przetwornika ACDC :
| adc.asm |
ADC_init:
PUSH R16
;przesunięcie danych w rejestrach przetwornika - zmniejszam rozdzielczość pomiaru do 8 bitów
ldi R16, (1<<ADLAR)
out Admux, r16
ldi r16, (1<<ADEN)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADSC)
;ADEN - włączony przetwornik A/C
;ADIE - włączenie generowania przerwania kiedy konwersja zakończona
;ADSC - start przetwarzania A/C - bit wymagany równierz w trybie autowyzwalania
;ADATE - włączenie autowyzwalania - źródło wyzwalania zdefiniowane w rejestrze SFIOR
;ADPS2,ADPS1 - wybór częstotliwości taktowania przetwornika u nas = częstotliwość zegara/64
;źródło wyzwalania fre-rouning mode ADTS 0 0 0
out ADCSRA,R16
POP r16
RET
|
Procedury inicjujÄ…cej Timer - timer pracuje w trybie PWM
Konfiguracja Timera1 -PWM:
| Timer1.asm |
Timer0_init:
;ustawienie mocy na zero
ldi r16, $f0;
out OCR0, r16;
;reset preskalera
in r16,sfior
sbr_ r16, psr10
out sfior,r16;
;konnfiguracja timera jako PWM
ldi r16, (1<<WGM00)|(1<<CS02)|(1<<COM01)
; podział czestotliwości na 1024 bity CS oraz ustawienie timera w tryb PWM bit WGM00
out TCCR0, r16;
ret
|
ustawienie rejestru pwm odbywa się podczas obsługi przerwania przetwornika AC (konwersja zakończona) są to tylko 2 instrukcje asemblera.
Ustawienie współczynnika wypełnienia rejestr OCR0:
in r16,ADCH
out OCR0, r16 |
Podsumowanie
Tutaj przedstawię zdjęcie obrazujące jak nasze urządzenie pomiarowe współpracuje z programem w Labwiev