Powyższy schemat przedstawia prawidłowy sposób podłączenia zewnętrznych elementów, podczas
korzystania z przetwornika analogowo-cyfrowego ADC (Analog to Digital Converter) wbudowanego
w mikrokontroler ATmega48/88/168/328. Elementy te znacznie zmniejszają zakłócenia oraz ich
negatywny wpływ na pracę przetwornika ADC.
Cewka L1 i kondensator C2 tworzy filtr, który eliminuje zakłócenia w napięciu zasilającym część ana-
logową mikrokontrolera (pin AVCC). Kondensator C3 filtruje napięcie odniesienia Vref dla przetwornika
ADC (pin AREF). Rezystor R2 jest opcjonalny - wymusza poziom masy (GND) na wejściu pomiarowym
PC0/ADC0 (Vin). Bez tego rezystora i przy niepodłączonym pinie PC0/ADC0, pojawiałyby się na nim
przypadkowe wartości napięcia.
Na schemacie znajduje się również alfanumeryczny wyświetlacz LCD (sterownik HD44780 lub kom-
patybilny), który służy do prezentacji wyników pomiarów. Dokładny opis jego podłączenia, fragmentów
kodu programu do jego obsługi, pozostałych elementów elektronicznych oraz prawidłowej konfiguracji
mikrokontrolera (fuse/lock bity), znajduje się na tej stronie.
Wstęp
Obecnie każdy mikrokontroler posiada wbudowany przetwornik ADC, który zamienia analogową wartość napięcia sta-
łego Vin na odpowiadającą mu wartość cyfrową (binarną/dwójkową). Mikrokontroler ATmega48/88/168/328 posiada
wielokanałowy przetwornik 10-bitowy, co oznacza że może on mierzyć napięcie Vin z kilku wybieranych (multiplekso-
wanych) wejść/źródeł, które jest zamieniane na 10-bitową wartość Aval=$0000-$03FF (0-1023 dziesiętnie).
Od wartości napięcia odniesienia Vref dla przetwornika ADC zależy maksymalne napięcie Vin, które może być zmie-
rzone oraz rozdzielczość pomiaru wynosząca: Mres=Vref/1024 [V]. Jeśli mierzone napięcie Vin będzie wyższe od
napięcia Vref, to wynik konwersji będzie zbliżony do wartości maksymalnej, czyli $03FF. Napięcie Vref nie może być
niższe od 1V, ani wyższe od napięcia zasilania Vcc mikrokontrolera. Wartość zmierzonego napięcia Vin można
obliczyć ze wzoru: Vin=Mres*Aval [V].
Przykład: jeśli Vref=3.072V to przetwornik ADC będzie mierzył napięcie Vin=0-3.072V, które zamieni na wartość cyfro-
wą Aval=$0000-$03FF (10-bitów). Rozdzielczość pomiaru Mres = 3.072V/1024 = 0.003V, czyli każda zmiana napię-
cia Vin o 3mV spowoduje zmianę wartości cyfrowej o 1. Jeśli wynik pomiaru z przetwornika wynosi Aval=$0064=100,
to wartość zmierzonego napięcia Vin = 0.003V*100 = 0.3V.
Oczywiście są to obliczenia teoretyczne dotyczące idealnego przetwornika ADC i źródła napięcia odniesienia Vref.
W praktyce całkowita dokładność przetwornika ADC wbudowanego w mikrokontroler ATmega48/88/168/328, wynosi
w najgorszym wypadku plus/minus 2 LSB (Least Significant Bit), co oznacza że otrzymana wartość Aval może odbiegać
o plus/minus 2 od wartości prawidłowej. Nie istnieje też źródło napięcia odniesienia Vref o idealnie dokładnej i stałej
wartości, a ma ono kluczowy wpływ na wynik konwersji.
Dopuszczalny zakres napięć wejściowych Vin na dowolnym kanale przetwornika ADC, wynosi od GND do Vcc.
Podanie niższego/wyższego napięcia Vin może zaburzyć pracę przetwornika ADC, ale nie uszkodzi mikrokontrolera
pod warunkiem, że płynący prąd nie przekroczy wartości 1mA.
Dzieje się tak dlatego, że każdy pin posiada dwie wewnętrzne diody zabezpieczające (clamping diodes) o spadku
napięcia 0.5V, które chronią mikrokontroler przed za niskim/wysokim napięciem wejściowym Vin w stosunku do jego
napięcia zasilania Vcc. Utrzymują one wszystkie sygnały trafiające do mikrokontrolera w zakresie jego maksymalnych
napięć wejściowych (od GND-0.5V do Vcc+0.5V). Podanie napięcia wejściowego Vin o wartości niższej/wyższej niż
GND-0.5V/Vcc+0.5V, spowoduje przepływ prądu przez dolną/górną diodę zabezpieczającą do masy/plusa zasilania,
a w rezultacie zmniejszenie napięcia Vin na pinie do wartości GND-0.5V/Vcc+0.5V. Jedynym ograniczeniem wewnę-
trznych diod zabezpieczających jest zalecany maksymalny prąd, który może przez nie płynąć wynoszący 1mA. Większy
prąd może spowodować uszkodzenie diody zabezpieczającej, a następnie pinu/portu mikrokontrolera.
Wynika z tego, że stosując odpowiednio dużą wartość rezystancji wejściowej Rin, na pin mikrokontrolera można podać
napięcie wejściowe Vin, o wartości znacznie przekraczającej jego maksymalny zakres (od GND-0.5V do Vcc+0.5V).
Przykład: z rezystorem wejściowym Rin=1M na pin mikrokontrolera można podać napięcie ujemne/dodatnie Vin=1000V,
bez obawy o jego uszkodzenie.
Trzeba jednak pamiętać, że przetwornik ADC działa dobrze jedynie ze źródłami napięcia o impedancji wyjściowej Zout
do 10k (wtedy czas próbkowania jest pomijalnie mały). Zbyt wysoka rezystancja Rin na wejściu przetwornika uniemożliwi
prawidłowy pomiar, ponieważ prąd ze źródła napięcia Vin będzie zbyt niski, aby naładować kondensator próbkujący Csh
(S/H - Sample and Hold) w odpowiednio krótkim czasie (konwersja trwa 13-260 us). Oczywiście ograniczenie to dotyczy
tylko wykonywania konwersji (Free Running mode) z dużą szybkością (15-76.9 kSps). Prawidłowy pomiar źródeł napię-
cia o wyższej impedancji Zout jest możliwy, jeśli pojedyncze konwersje (Single Conversion mode) będą wykonywane
z odpowiednio małą szybkością. Wtedy kondensator próbkujący Csh ma dłuższy czas, aby naładować się niskim prą-
dem ze źródła Vin do wartości równej napięciu tego źródła. Trzeba przy tym brać pod uwagę prądy upływu Iil/Iih wejścia
przetwornika (pin ADCn) w stanie niskim/wysokim, które rosną wraz z temperaturą i maksymalnie mogą wynosić 1uA
(w temperaturze pokojowej zwykle poniżej 50nA). Cyfrowy bufor wejściowy na pinie ADCn musi zostać wyłączony (bit
ADCnD w rejestrze DIDR0), aby wyeliminować wprowadzaną przez niego pojemność oraz zmniejszyć pobór prądu.
Poniższy rysunek przedstawia wewnętrzną budowę przetwornika ADC (podane wartości elementów były zmierzone
doświadczalnie w jednym egzemplarzu mikrokontrolera ATmega328P).
| |
Po wybraniu/zmianie wejścia dla przetwornika
ADC w multiplekserze (bity MUXn w rejestrze
ADMUX), do źródła Vin zostaje podłączony kon-
densator Cmux przez rezystor Rmux. Po włącze-
niu przetwornika ADC (bit ADEN w rejestrze
ADCSRA), przełącznik Ssh podłącza do źródła
Vin kondensator Csh przez rezystor Rsh. Po uru-
chomieniu pojedynczej konwersji (bit ADSC w re-
jestrze ADCSRA), kondensator Csh zostaje odłą-
czony od źródła Vin. Po zakończeniu wykonywa-
nia konwersji kondensator Csh zostaje ponownie
podłączony do źródła Vin.
Aby uzyskany wynik każdego pomiaru był prawi-
dłowy, czas Tsh pomiędzy kolejnymi konwersjami
powinien wynosić: Tsh = 6*Rin*Cin, gdzie Rin - |
rezystancja wejściowa przetwornika ADC (suma impedancji wyjściowej Zout źródła mierzonego napięcia Vin i rezystan-
cji Rmux+Rsh), Cin - pojemność wejściowa przetwornika ADC (suma pojemności ścieżek Cpcb na płytce drukowanej
i kondensatorów Cmux+Csh). Według not katalogowych suma rezystancji Rmux+Rsh może wynosić 1-100k, a pojemno-
ści Cmux+Csh równe 14pF. Przykład: jeśli Rin = 1M i Cin = 30pF, to kolejna konwersja powinna być wykonana po od-
czekaniu 180us (Tsh = 6*1000000*0.00000000003 = 6*0.00003 = 0.00018). Po tym czasie kondensator próbkujący
Csh podłączony do źródła Vin naładuje się do wartości 99.75% napięcia tego źródła.
Innym sposobem na prawidłowy pomiar źródeł napięcia o wysokiej impedancji Zout jest podłączenie kondensatora 1nF,
pomiędzy masę i wejście przetwornika (pin ADCn). Dzięki temu kondensator próbkujący Csh będzie dużo szybciej ła-
dowany bezpośrednio z kondensatora 1nF (minimalny spadek napięcia), niż przez rezystancję wejściową Rin (np. dzie-
lnika napięcia). Spowoduje to wolniejszą odpowiedź częstotliwościową na szybkie zmiany napięcia źródła Vin, ale nie
wymaga czekania pomiędzy kolejnymi konwersjami. Użyty kondensator 1nF musi mieć niską stratność dielektryczną.
Poniżej znajduje się kodu programu przeznaczonego dla mikrokontrolerów serii ATmega48/88/168/328,
który konfiguruje przetwornik ADC, wykonuje za jego pomocą ciągłe pomiary i prezentuje ich wyniki na
wyświetlaczu LCD.
.include "m48def.inc" ;definicje dla ATmega48
;.include "m88def.inc" ;definicje dla ATmega88
;.include "m168def.inc" ;definicje dla ATmega168
;.include "m328def.inc" ;definicje dla ATmega328
.cseg ;pamięć programu (FLASH)
.org $0000 ;wektory przerwań (dla ATmega48/88: 26 wektorów 2-bajtowych, o adresach $0000-$0019,
;dla ATmega168/328: 26 wektorów 4-bajtowych, o adresach $0000-$0032).
;Jeśli żadne przerwania nie będą używane, to w tym miejscu można bezpośrednio umieścić kod programu
;Wektor nr 1 (Reset)
;rjmp RESET ;dla ATmega48/88 musi to być instrukcja RJMP o rozmiarze 2 bajtów,
;jmp RESET ;dla ATmega168/328 musi to być instrukcja JMP o rozmiarze 4 bajtów,
;ponieważ wszystkie wektory w tych mikrokontrolerach mają taki rozmiar.
;RESET:
;Inicjalizacja wskaźnika stosu (zbędna dla ATmega48/88/168/328)
;ldi R16,LOW(RAMEND)
;out SPL,R16
;ldi R16,HIGH(RAMEND)
;out SPH,R16
;Konfiguracja mikrokontrolera
ldi R16,$01 ;częstotliwość pracy mikrokontrolera (CLK): 1-20 [MHz]
mov R15,R16
ldi R16,$EE ;zatrzymanie zbędnych modułów (TWI, SPI, USART, TC0, TC1, TC2)
sts $64,R16 ;zapis rejestru PRR
ldi R16,$80 ;wyłączenie zasilania komparatora analogowego
sts $50,R16 ;zapis rejestru ACSR
ldi R16,$FF
out PORTB,R16 ;ustawienie pinów PB0-PB7 jako wejść z wysokim stanem (pull-up)
out DDRD,R16 ;ustawienie pinów PD0-PD7 jako wyjść z niskim stanem
ldi R16,$30
out PORTD,R16 ;ustawienie wysokiego stanu na wyjściach PD4-PD5
;(PD0-PD3/PD6-PD7 stan niski).
ldi R16,$3E
out PORTC,R16 ;ustawienie pinów PC1/ADC1-PC5/ADC5 jako wejść z wysokim stanem (pull-up)
ldi R16,$01 ;wyłączenie cyfrowego bufora wejściowego na pinie PC0/ADC0
sts $7E,R16 ;zapis rejestru DIDR0
Aby zmniejszyć zakłócenia i pobór prądu, zostaje wyłączony komparator analogowy oraz zatrzymane
taktowanie nieużywanych modułów mikrokontrolera (TWI, SPI, USART, TC0, TC1, TC2). Na wszy-
stkich pinach mikrokontrolera panuje ustalony stan logiczny (cyfrowe wyjścia PC0/ADC0-PC3/ADC3
nie powinny się przełączać podczas trwania konwersji), a na wejściu pomiarowym PC0/ADC0 (Vin)
jest wyłączony cyfrowy bufor wejściowy.
Mikrokontrolery ATmega48/88/168/328 nie mają oddzielonej masy cyfrowej (pin 8) od analogowej
(pin 22). Muszą się one ze sobą łączyć na płytce drukowanej, ale tylko w jednym punkcie - znacznie
zmniejsza to wpływ zakłóceń, generowanych przez część cyfrową mikrokontrolera na jego część
analogową.
Ponadto istnieje też możliwość wykonywania pomiarów przez przetwornik ADC tylko wtedy, gdy mikro-
kontroler jest "uśpiony" (tryb "ADC Noise Reduction"), co dodatkowo niweluje wpływ wewnętrznych
zakłóceń na wynik konwersji. Wymaga to jednak użycia przerwań mikrokontrolera, które nie zostały
wykorzystane w tym przykładzie.
;Konfiguracja wyświetlacza LCD 2x16 znaków (HD44780)
rcall LCDi ;inicjalizacja LCD (włączenie trybu 4-bitowego)
ldi R18,$28 ;ustawienie opcji: 2 linie, znaki 5x8 pikseli, interfejs 4-bit
rcall LCDwi ;zapis instrukcji do LCD
ldi R18,$0C ;włączenie LCD bez kursora
rcall LCDwi ;zapis instrukcji do LCD
rcall LCDclr ;czyszczenie LCD
ldi ZL,LOW(Text0<<1)
ldi ZH,HIGH(Text0<<1)
rcall LCDwf ;wyświetlenie na LCD ciągu ASCIIZ z pamięci FLASH
ldi R18,$C0 ;ustawienie kursora na pozycji 1 w 2 linii
rcall LCDwi ;zapis instrukcji do LCD
rcall LCDwf ;wyświetlenie na LCD ciągu ASCIIZ z pamięci FLASH
Inicjalizacja i włączenie wyświetlacza LCD oraz wyświetlenie na nim początkowych napisów.
;Główna pętla programu
Loop:
;Obsługa zworki J1 - wybieranie wejścia dla przetwornika ADC
sbic PINC,$05 ;odczyt stanu pinu PC5/ADC5
rjmp Loop0 ;skok, jeśli na pinie PC5/ADC5 jest stan wysoki (zworka J1=OFF)
;Zworka J1=ON (zwarta): wybranie wewnętrznego źródła napięcia odniesienia (1.1V) jako wejścia dla ADC
ldi R16,$4E ;wybranie napięcia odniesienia z pinu AVCC i wejścia dla ADC
sts $7C,R16 ;zapis rejestru ADMUX
rjmp Loop1
;Zworka J1=OFF (rozwarta): wybranie pinu PC0/ADC0 jako wejścia dla ADC
Loop0: ldi R16,$40 ;wybranie napięcia odniesienia z pinu AVCC i wejścia dla ADC
sts $7C,R16 ;zapis rejestru ADMUX
;Uruchomienie konwersji ADC
Loop1: ldi R16,$C3 ;włączenie ADC i wykonanie pojedynczej konwersji z f=CLK/8 (125 kHz)
sts $7A,R16 ;zapis rejestru ADCSRA
;Czekanie na zakończenie konwersji przez ADC
Loop2: lds R16,$7A ;odczyt rejestru ADCSRA
andi R16,$40
brne Loop2 ;skok, jeśli bit ADSC=1 (konwersja trwa)
;Odczyt wyniku konwersji ADC
lds R23,$78 ;odczyt rejestru ADCL (młodszy bajt wyniku)
lds R16,$79 ;odczyt rejestru ADCH (starszy bajt wyniku)
;Konwersja 16-bitów wyniku do systemu HEX-ASCIIZ: [R16:R23] => "NNNN"
ldi ZL,LOW(sbuf)
ldi ZH,HIGH(sbuf) ;adres bufora wyjściowego w pamięci SRAM
rcall ConBH ;konwersja bajtu do systemu HEX-ASCIIZ
mov R16,R23 ;młodszy bajt wyniku
rcall ConBH ;konwersja bajtu do systemu HEX-ASCIIZ
;Wyświetlenie szesnastkowej wartości wyniku na LCD
sbiw ZL,$04
ldi R18,$C7 ;ustawienie kursora na pozycji 8 w 2 linii
rcall LCDwi ;zapis instrukcji do LCD
rcall LCDws ;wyświetlenie na LCD ciągu ASCIIZ z pamięci SRAM
ldi R16,$FF ;opóźnienie 25.5 ms
rcall Wait ;pętla opóźniająca
rcall Wait ;pętla opóźniająca
rcall Wait ;pętla opóźniająca
rcall Wait ;pętla opóźniająca
rjmp Loop
Główna pętla programu, która jest wykonywana bez przerwy. Na początku program sprawdza stan zwo-
rki J1 (pin PC5/ADC5). Jeśli zworka J1 jest rozwarta (OFF), to za pomocą rejestru ADMUX dla przetwo-
rnika ADC jest wybierane napięcie odniesienia Vref z pinu AVCC (bity REFS1-0, Vref=Vcc) oraz wejście
na pinie PC0/ADC0 (bity MUX3-0). Jeśli zworka J1 jest zwarta (ON), to dla przetwornika ADC wybierane
jest napięcie odniesienia Vref również z pinu AVCC, ale wejście z wewnętrznego źródła napięcia odnie-
sienia (1.1V). Następnie przy użyciu rejestru ADCSRA włączany jest przetwornik ADC (bit ADEN) i uru-
chamiana pojedyncza konwersja (bit ADSC) z wybraną częstotliwością (bity ADPS2-0). Aby uzyskać
wynik konwersji o maksymalnej rozdzielczości 10-bitów, przetwornik musi pracować z częstotliwością
Fadc=50-200 kHz (wyższa obniży rozdzielczość pomiaru). Częstotliwość Fadc jest uzyskiwana z głównej
częstotliwości CLK taktującej mikrokontroler, podzielonej w 7-bitowym prescalerze przetwornika ADC
(podział przez 2/4/8/16/32/64/128). Poniższa tabela zawiera wszystkie możliwe do uzyskania często-
tliwości Fadc w zależności od częstotliwości taktowania CLK mikrokontrolera.
CLK [MHz] | Fadc [Hz] |
2 | 4 | 8 | 16 | 32 | 64 | 128 |
1 | 500k | 250k | 125k | 62.5k | 31.25k | 15.62k | 7.81k |
2 | 1M | 500k | 250k | 125k | 62.5k | 31.25k | 15.62k |
3 | 1.5M | 750k | 375k | 187.5k | 93.75k | 46.88k | 23.44k |
4 | 2M | 1M | 500k | 250k | 125k | 62.5k | 31.25k |
5 | 2.5M | 1.25M | 625k | 312.5k | 156.25k | 78.12k | 39.06k |
6 | 3M | 1.5M | 750k | 375k | 187.5k | 93.75k | 46.88k |
7 | 3.5M | 1.75M | 875k | 437.5k | 218.75k | 109.38k | 54.69k |
8 | 4M | 2M | 1M | 500k | 250k | 125k | 62.5k |
9 | 4.5M | 2.25M | 1.12M | 562.5k | 281.25k | 140.62k | 70.31k |
10 | 5M | 2.5M | 1.25M | 625k | 312.5k | 156.25k | 78.12k |
11 | 5.5M | 2.75M | 1.37M | 687.5k | 343.75k | 171.88k | 85.94k |
12 | 6M | 3M | 1.5M | 750k | 375k | 187.5k | 93.75k |
13 | 6.5M | 3.25M | 1.62M | 812.5k | 406.25k | 203.12k | 101.56k |
14 | 7M | 3.5M | 1.75M | 875k | 437.5k | 218.75k | 109.38k |
15 | 7.5M | 3.75M | 1.87M | 937.5k | 468.75k | 234.38k | 117.19k |
16 | 8M | 4M | 2M | 1M | 500k | 250k | 125k |
17 | 8.5M | 4.25M | 2.12M | 1.06M | 531.25k | 265.62k | 132.81k |
18 | 9M | 4.5M | 2.25M | 1.12M | 562.5k | 281.25k | 140.62k |
19 | 9.5M | 4.75M | 2.37M | 1.18M | 593.75k | 296.88k | 148.44k |
20 | 10M | 5M | 2.5M | 1.25M | 625k | 312.5k | 156.25k |
| |
Po wykryciu zakończenia konwersji (bit ADSC=
0) przez pętlę warunkową, 16-bitowy wynik jest
odczytywany z rejestrów [ADCH:ADCL] (najsta-
rsze 6-bitów ma zawsze wartość 0). Każdy bajt
tego wyniku jest zamieniany na szesnastkowy
ciąg ASCII i zapisywany do bufora w pamięci
SRAM mikrokontrolera. Uzyskany w ten sposób
4-znakowy ciąg ASCIIZ jest wyświetlany na
LCD, począwszy od pozycji nr 8 w 2 linii.
Przed końcem pętli znajduje się kod generujący
opóźnienie ok. 102ms, aby spowolnić wyświe-
tlanie nowych wartości na LCD. Instrukcja skoku
RJMP zamyka pętlę.
Dzięki temu podczas pracy programu na wyś-
wietlaczu LCD można obserwować aktualną
4-cyfrową wartość szesnastkową, która odpo-
wiada napięciu na pinie PC0/ADC0 (J1=OFF)
lub napięciu wewnętrznego źródła odniesienia
(J1=ON) mikrokontrolera.
Otrzymana wartość zależy bezpośrednio od na-
pięcia zasilania Vcc mikrokontrolera (pin AVCC),
ponieważ jest ono jednocześnie napięciem od-
niesienia Vref dla przetwornika ADC. Aby wyniki
pomiarów nie były zależne od napięcia zasilania
Vcc, należy na pin AREF podać napięcie odnie-
|
sienia Vref z innego źródła (bity REFS1-0 w rejestrze ADMUX). Może to być wewnętrzne źródło napięcia
odniesienia (1.1V) mikrokontrolera, ale lepszym rozwiązaniem jest użycie zewnętrznego układu scalo-
nego, który dostarczy stabilniejszego napięcia Vref (mniejsza zależność od zmian napięcia zasilania
i temperatury).
Przykładowa zawartość ekranu wyświetlacza LCD, podczas wykonywania głównej pętli programu jest
przedstawiona poniżej: rysunek 1 - wejście pomiarowe PC0/ADC0 (Vin) niepodłączone (J1:OFF);
rysunek 2 - pomiar napięcia zasilania Vcc (J1:OFF); rysunek 3 - pomiar napięcia wewnętrznego źródła
odniesienia 1.1V (J1:ON).
Rysunek 1
Rysunek 3 | | Rysunek 2 |
;Zapis szesnastkowej wartości bajtu do pamięci SRAM w postaci ciągu ASCIIZ: "NN"
ConBH: ldi R17,$02
ConBH0: swap R16 ;bajt do konwersji
mov R18,R16
andi R18,$0F
subi R18,$D0
cpi R18,$3A
brlt ConBH1
subi R18,$F9 ;subi R18,$D9 - małe litery (a-f)
ConBH1: st Z+,R18
dec R17
brne ConBH0
st Z,R17 ;wyjście danych
ret
;Inicjalizacja wyświetlacza LCD
LCDi:
;Czekanie na ustabilizowanie się napięcia zasilania
ldi R16,$C8 ;opóźnienie 20 ms
rcall Wait ;pętla opóźniająca
rcall Wait ;pętla opóźniająca
;Wysłanie trzech instrukcji $30 do LCD
ldi R18,$03
LCDi0: ldi R16,$B3
out PORTD,R16 ;E=1, RS=0 (PD4-PD5=1)
;(wait 4 CL)
cbi PORTD,$07 ;E=0, RS=0 (PD4-PD5=1)
ldi R16,$32 ;opóźnienie 5 ms
rcall Wait ;pętla opóźniająca
dec R18
brne LCDi0
;Wysłanie instrukcji $20 do LCD (włączenie trybu 4-bitowego)
ldi R16,$B2
out PORTD,R16 ;E=1, RS=0 (PD4-PD5=1)
;(wait 4 CL)
cbi PORTD,$07 ;E=0, RS=0 (PD4-PD5=1)
ldi R16,$0A ;opóźnienie 1 ms
rcall Wait ;pętla opóźniająca
ret
;Zapis instrukcji do wyświetlacza LCD (tryb 4-bitowy)
LCDwi: ldi R20,$B0 ;E=1, RS=0 (PD4-PD5=1)
rjmp LCDwc0
;Zapis znaku do wyświetlacza LCD (tryb 4-bitowy)
LCDwc: ldi R20,$F0 ;E=1, RS=1 (PD4-PD5=1)
LCDwc0: ldi R21,$02
LCDwc1: swap R18 ;bajt do wysłania
mov R19,R18
andi R19,$0F
andi R20,$F0 ;E=1, RS=początkowa wartość (PD4-PD5=1)
add R20,R19
out PORTD,R20 ;wysłanie 4-bitów do LCD (najpierw starsze, później młodsze)
;(wait 4 CL)
cbi PORTD,$07 ;E=0, RS=początkowa wartość (PD4-PD5=1)
ldi R16,$01 ;opóźnienie 100 us
rcall Wait ;pętla opóźniająca
dec R21
brne LCDwc1
ret
;Czyszczenie wyświetlacza LCD i powrót kursora na pozycję 1 w 1 linii
LCDclr: ldi R18,$01 ;czyszczenie LCD i powrót kursora
rcall LCDwi ;zapis instrukcji do LCD
ldi R16,$10 ;opóźnienie 1.6 ms
rcall Wait ;pętla opóźniająca
ret
;Zapis ciągu ASCIIZ z pamięci SRAM do wyświetlacza LCD (tryb 4-bitowy)
LCDws0: rcall LCDwc ;zapis znaku do LCD
LCDws: ld R18,Z+ ;odczyt znaku z pamięci SRAM
tst R18
brne LCDws0
ret
;Zapis ciągu ASCIIZ z pamięci FLASH do wyświetlacza LCD (tryb 4-bitowy)
LCDwf0: rcall LCDwc ;zapis znaku do LCD
LCDwf: lpm R18,Z+ ;odczyt znaku z pamięci FLASH
tst R18
brne LCDwf0
ret
;Pętla opóźniająca o zadany czas: 100u - 25.5m [s]
Wait: mov R2,R15 ;R15 (CLK) = częstotliwość pracy mikrokontrolera: $01-$14 (1-20 MHz)
ldi R17,$19
mul R16,R17 ;R16 (DLY) = wartość opóźnienia: $01-$FF (100us-25.5ms)
Wait0: movw YL,R0
Wait1: sbiw YL,$01
brne Wait1
dec R2
brne Wait0
ret
;Napisy dla wyświetlacza LCD
Text0: .db "*** ADC test ***",0,"Value:",0
.dseg ;pamięć danych (SRAM)
.org $0100
sbuf: .byte 5 ;rezerwuje N-bajtów w pamięci SRAM