ST7FLITE19 - Pomiar napięcia na wejściu AIN4
Wiele z mikrokontrolerów ST7 ma wbudowany w strukturę przetwornik analogowo – cyfrowy. Celem niniejszego artykułu jest pokazanie (abstrahując od strony realizacji interfejsu sprzętowego) w jaki sposób zaprząc przetwornik do pracy i zmierzyć napięcie doprowadzone do jednego z wejść analogowych oraz przedstawić wynik jego pomiaru w [V]. W przykładzie posłużono się mikrokontrolerem ST7FLITE19. Program napisany jest w języku asembler ST7.
10-bitowy przetwornik analogowo – cyfrowy
Wewnątrz struktury mikrokontrolera ST7FLITE19 linie portu PB poprzez multiplekser analogowy, dołączone są do wejścia przetwornika analogowo – cyfrowego. Przetwornik nie ma wejścia umożliwiającego doprowadzenie zewnętrznego napięcia odniesienia, więc wszystkie pomiary wykonywane są w odniesieniu do napięcia zasilającego. W związku z tym wahania napięcia zasilającego będą wpływać na dokładność otrzymanego wyniku pomiaru.
Rozdzielczość przetwornika jest równa 10-bitów, co przy zasilaniu napięciem 5V daje ziarno wynoszące około 4,9 mV. Na wejściu przetwornika znajduje się wzmacniacz, który może być załączony przez ustawienie bitu AMPSEL w rejestrze ADCDRL. Ma on stałe wzmocnienie równe 8. Po załączeniu wzmacniacza staje się możliwy pomiar nawet niewielkich zmian napięcia, ponieważ w tym trybie napięcie w zakresie od 0 do 430 mV, przy zasilaniu mikrokontrolera z 5 V, może być mierzone z rozdzielczością około 0,61 mV. Tu jedna ważna uwaga.
W niektórych materiałach na temat ST7 pojawia się wzmianka, że rozdzielczość przetwornika wynosi przy załączonym wzmacniaczu 13 bitów. Jest to prawdą tylko w odniesieniu do napięcia zasilania, a nie do napięcia mierzonego! Jeśli jest włączony wzmacniacz, to nie można poprawnie zmierzyć przyłożonego do wejścia analogowego napięcia o wartości równej napięciu zasilania. W tym trybie maksymalne napięcie wejściowe jest 8-krotnie mniejsze od napięcia zasilającego. Oczywiście – jak wspomniałem wcześniej – rozdzielczość przetwornika to 0,61V przy zasilaniu mikrokontrolera napięciem 5 V i odnosząc to do tej wartości napięcia zasilającego otrzymamy wspomniane 13 bitów rozdzielczości (5V / 0,61mV ≈ 8192 tj. 12 bitów), jednak jest to tylko rozdzielczość pozorna, ponieważ właściwości przetwornika nie zmieniają się: nadal udostępnia on słowo o długości 10 bitów.
Pomiary napięcia z użyciem przetwornika
Port wejściowy, który będzie dołączony do źródła napięcia mierzonego, musi być ustawiony w trybie pracy jako wejście, bez dołączonego wewnętrznego rezystora zasilającego (pull-up). Sam pomiar napięcia jest bardzo prosty:
- Poprzez nastawę rejestru ADCCSR wybrać wejście pomiarowe, tj. w przypadku ST7FLITE19/29 numer linii portu B mikrokontrolera, do której doprowadzone jest mierzone napięcie.
- W rejestrze kontrolnym przetwornika ADCCSR należy ustawić bit ADON.
- Poczekać na ustawienie bitu EOC w tym samym rejestrze: wartość logiczna „1“ oznacza, że przetwornika zakończył pomiar i wynik jest gotowy do odczytu. Uwaga: w ST7FLITE19 przetwornik nie generuje przerwań!
- Odczytać rejestry danych ADCDRL (młodszy bajt – współdzielony z bitami nastaw, znaczenie mają tylko 2 najmłodsze bity.) i ADCDRH (starszy bajt); odczyt ADCDRH powoduje przypisanie fladze EOC wartości logicznej „0“.
Przykład programu mierzącego napięcie przyłożone do AIN4 przestawiono na listingu 1.
Listing 1.Fragment programu: pomiar napięcia na wejściu AIN4
;-------------------------------------------------------
; pomiar napięcia: wartość zmierzona zapamiętywana jest
; w zmiennej VVOLTS (2 bajty)
;-------------------------------------------------------
volts
clr ADCCSR ;inicjacja rejestru kontrolnego,wyłączenie
;przetwornika
clr ADCDRL ;nastawa czasu pomiaru i wzmacniacza wejściowego
;(wzmacniacz=wyłączony)
ld A,#$40 ;ustawienie szybkości przetwarzania (SPEED=1)
ld ADCCSR,A
ld A,ADCDRH ;kasowanie flagi EOC
ld A,#$04
ld ADCCSR,A ;załączenie kanału pomiarowego AIN4
or A,#$20 ;uruchomienie pomiaru
ld ADCCSR,A
btjf ADCCSR,#7,* ;oczekiwanie na zakończenie pomiaru
clr vvolts ;kompozycja starszego bajtu wyniku
ld X,vvolts ;ustawienie dwóch najmłodszych bitów przez
;przesunięcie z flaga C
ld A,ADCDRH
sll A
rlc X
sll A
rlc X
ld vvolts,X ;zapamiętanie starszego bajtu
ld {vvolts+1},A ;sumowanie młodszego bajtu suma z dwoma
;najmłodszymi bitami z ADCDRL
ld A,ADCDRL
and A,#$03 ;maskowanie nieznaczących dla pomiaru bitów D7-D2
or A,{vvolts+1} ;sumowanie D0 i D1 wyniku
ld {vvolts+1},A ;zapamiętanie wyniku pomiaru
Prezentacja wyniku pomiaru napięcia wejściu AIN4.
Program mierzy napięcie na wejściu AIN4 a wynik pomiaru wyświetlany jest w Voltach na wyświetlaczu LCD. Schemat wykonanych połączeń przedstawiono na rysunku 1. Proste sformułowanie zagadnienia wcale nie jest tak proste, jeśli podejść do niego od strony implementacji w asemblerze. Jak na pewno sobie to uzmysławiamy, wynik odczytany z przetwornika analogowo – cyfrowego ma postać binarną, zupełnie nieczytelną dla człowieka. Naturalną dla nas notacją jest notacja dziesiętna i do takiej to postaci musi być przekształcony wynik pomiaru.
Napięcie z suwaka potencjometru PR1, poprzez rezystor R2 zabezpieczający wejście mikrokontrolera, doprowadzane jest do wejścia AIN4 (nóżka 3) mikrokontrolera ST7FLITE29. Oczywiście można w tym miejscu podłączyć dowolne źródło napięcia dostarczające napięcie z zakresu od 0 do 5 V (słuszne dla napięcia zasilającego o wartości 5 V). Wyświetlacz pracuje z interfejsem 4-bitowym i jest dołączony do portu A mikrokontrolera. ST7FLITE19 pracuje z wykorzystaniem wewnętrznego generatora RC o częstotliwości 1 MHz. Kod źródłowy programu umieszczono na listingu 2.
Rysunek 1. Schemat połączenia wyświetlacza i potencjometru z mikrokontrolerem.
Opis programu głównego
Na początku jest inicjowany wskaźnik stosu, a do wyświetlacza LCD jest przesyłany szereg poleceń ustawiających go w trybie pracy z interfejsem 4-bitowym. Następnie ekran jest czyszczony a kursor jest ustawiany w pozycji HOME (lewy górny róg ekranu). Teraz z użyciem trybu adresowania indeksowego z przesunięciem 16-bitowym z pamięci programu jest pobierany napis, który jest wyświetlany od tej pozycji kursora, aż do napotkania bajtu 0 na końcu definicji.
Wynik pomiaru napięcia wyświetlany jest w drugiej linii wyświetlacza. Kursor przemieszcza się do tej linii po wywołaniu procedury gotoaxy, oczywiście jeśli wcześniej wstawi się właściwe parametry do rejestru A przekazującego argumenty wywołania (4 starsze bity, to numer kolumny a 4 młodsze, to numer wiersza). Teraz kolejno wywoływane są procedury:
- pomiaru napięcia volts
- konwersji wyniku pomiaru na postać liczby dziesiętnej bin_2_dec
- konwersji liczby dziesiętnej na postać kodów ASCII dec_2_ascii
- formatowania wyniku pomiaru add_dp
Rolę bufora wyniku pełni zmienna w pamięci RAM o nazwie decims. Z dedykowanych jej komórek pamięci w trybie adresowania z przesunięciem 8-bitowym jest pobierany wynik pomiaru w postaci sformatowanego tekstu i przesyłany na wyświetlacz LCD. Na końcu napisu dodawana jest litera „V“, następnie program odczekuje około 0,2 sekundy i cykl powtarza się tworząc w ten sposób nieskończoną pętlę realizowaną przez CPU mikrokontrolera.
Program główny jest nieskomplikowany – procedury wywoływane są jedna po drugiej przekazując sobie nawzajem wynik pomiaru. Omówmy teraz kolejno ich działanie, zajmując się szczegółami ich implementacji. Niektóre z omawianych funkcji przydadzą się nam jeszcze w innych zastosowaniach.
Pomiar napięcia (volts)
Na rysunku 2 umieszczono schemat funkcjonowania funkcji pomiaru napięcia. Ma ona do spełnienia dwie ważne role. Pierwszą z nich jest przeprowadzenie pomiaru napięcia przyłożonego do wejścia AIN4, drugą zamiana wyniku na jego reprezentację w mili-Voltach.
Jak pamiętamy napięcie referencyjne jest równe napięciu zasilania. W związku z tym jeśli napięcie zasilające wynosi 5V a liczba bitów przetwornika jest równa 10, to napięcie może być mierzone z dokładnością do około 5 mV (a dokładnie 5V/1024 ≈ 4,888mV). Ułóżmy pewną proporcję. Skoro 1024 = 5000 mV, to wynik pomiaru = X. Zgodnie z tym zapisem:
Niestety, procedura mierząca będzie popełniać błąd: około 2,3%. W tym momencie, dla skrócenia programu i uproszczenia obliczeń godzimy się na niego. Jak łatwo wywnioskować z powyższych rozważań, wynik pomiaru musi być przemnożony przez 5. Można to zrobić na co najmniej trzy sposoby:
- dodając do siebie 5-krotnie liczbę,
- wykorzystując funkcję mnożenia,
- składając wynik z operacji cząstkowych takich, jak dodawanie, przesunięcia itp.
Dla potrzeb prezentowanej aplikacji wybrano tę trzecią metodę. Jak łatwo zauważyć mnożenie x5 można rozłożyć na trzy operacje: dwa mnożenia przez 2 i jedną sumę. Mnożenie przez 2 to nic innego, jak przesunięcie liczby w lewo o 1 bit. Aby pomnożyć liczbę razy 4 wystarczy przesunąć ją dwukrotnie w lewo. Podobnie jest z dzieleniem przez 2: zmienia się kierunek przesunięcia na prawy, ale zasada pozostaje bez zmian.
Z opisanego wyżej algorytmu korzysta procedura pomiaru. Zapamiętuje ona wynik pomiaru w komórkach sub_a (używanych przez procedurę odejmowania) a następnie przesuwa go dwukrotnie w lewo. Po przesunięciu, do wyniku pomiaru nadal pamiętanego w komórkach volts dodawana jest uzyskana w ten sposób wartość. W ten to prosty sposób wynik pomiaru mnożony jest przez 5 umożliwiając wyświetlenie zawartości rejestru przetwornika w mili-Voltach.
Rysunek 2.Schemat funkcjonowania procedury pomiaru napięcia volts.
Konwersja liczby binarnej na dziesiętną (bin_2_dec).
Konwersja wykonywana jest dla liczb bez znaku o długości 2-bajtów, z zakresu od 0 do 65535. Procedura konwersji napisana jest nieco „na wyrost“, ponieważ wynik pomiaru z przetwornika ma długość co najwyżej 10 bitów i jest liczbą bez znaku, czyli w postaci dziesiętnej jest liczbą o wartości co najwyżej 1023. Nawet po przemnożeniu przez 5, liczba mieści się na 13 bitach. Zdecydowałem się jednak na pewną nadmiarowość procedury, ponieważ może się ona przydać również do innych zastosowań.
Schemat funkcjonowania podprogramu konwersji umieszczono na rysunku 3. Podprogram wykorzystuje opisywaną na tej stronie (Podstawowe operacje arytmetyczne) procedurę odejmowania liczb 2-bajtowych. Liczba operacji przeprowadzanych operacji różnic jest zliczana do momentu wystąpienia przeniesienia a licznik stanowi wartość wagi dziesiętnej na danej jej pozycji. Tak więc od liczby – zważywszy na fakt, że jej wartość nie przekracza 65535 – kolejno odejmowane są 10-tysięcy, tysiące, setki i dziesiątki. Pozostała po odejmowaniu dziesiątek wartość, to liczba jednostek. Ta może być przypisana wprost, nie wymaga żadnych obliczeń.
Dla uproszczenia procedury, za każdym razem używana jest operacja odejmowania liczb 2-bajtowych mimo, że do odejmowania setek czy dziesiątek wystarczyłby rozmiar pojedynczego bajtu.
Rysunek 3.Algorytm funkcjonowania podprogramu konwersji liczb binarnych na dziesiętne.
Konwersja na postać ASCII i formatowanie wyniku.
Po zmianie formatu liczby z binarnej na dziesiętną należy jeszcze zamienić ją na postać znaków do wyświetlenia.
Jak łatwo zauważyć, kolejne liczby dziesiętne są doskonałymi wręcz indeksami (wskaźnikami) do ewentualnie utworzonej tablicy zawierającej ich postać taką, jak wymaga wyświetlacz. W przypadku LCD jest to sprawa bardzo prosta: wystarczy wykorzystać fakt, że kody znaków ASCII, którymi posługuje się wyświetlacz, począwszy od „0“ a skończywszy na „9“ ułożone są kolejno, jeden za drugim. Tak więc sam generator znaków wbudowany w LCD ma potrzebną nam tablicę konwersji. Poszczególne elementy tej tablicy mogą być wskazywane w następujący sposób: <liczba dziesiętna> + <kod „0“>. Właściwość tę wykorzystuje procedura dec_2_asc. Dodaje ona po prostu do wag dziesiętnych każdej z cyfr kod znaku „0“. W ten sposób tworzony jest w pamięci mikrokontrolera gotowy do wyświetlenia ciąg znaków i jeśli wynik pomiaru ma być podany w mili-Voltach wystarczy po prostu wysłać go na wyświetlacz. Jeśli wynik pomiaru ma być w Voltach, trzeba go jeszcze odpowiednio sformatować.
Formatowanie wyniku w wypadku opisywanego programu polega tylko na wstawieniu w odpowiednie miejsce przecinka oddzielającego jednostki Voltów od ich ułamków. W prezentowanym przykładzie jest to bardzo proste. Jak wspomniano wcześniej, funkcja konwersji liczb binarnych na dziesiętne działa w zakresie pięciu cyfr dziesiętnych (do 65535). W związku z tym, że wynik pomiaru zawsze jest mniejszy i mieści się na 4 cyfrach, to na miejscu najwyższej wagi (104) pojawia się cyfra „0“. Wystarczy w miejsce „0“ przenieść liczbę z młodszej wagi (103) a w jej miejsce wstawić przecinek. Ilustruje to rysunek 4.
Rysunek 4. Formatowanie wyniku pomiaru.
Jacek Bogusz
j.bogusz@easy-soft.net.pl
Listing 2.Program do pomiaru napięcia doprowadzonego do wejścia AIN4.
st7/
;Program demonstrujący użycie przetwornika A/D
; i wyświetlacza LCD: 2 linie x 20 znaków
#include "st7flite29.inc"
;segmenty pamięci
BYTES
segment byte at 80-FF 'ram0'
segment byte at 100-1FF 'stack'
segment byte at 200-27F 'ram1'
segment byte at 1000-10FF 'eeprom'
segment byte at E000-FFDF 'program'
segment byte at FFE0-FFFF 'intvect'
BYTES
;deklaracje zmiennych
segment 'ram0'
del DS.B
copya DS.B
vvolts DS.W
sub_a DS.W
sub_b DS.W
roznicaw DS.W
decims DS.B 6
;deklaracje stałych
WORDS
segment 'program'
txt1 DC.B "NAPIECIE(AIN4):",0
;-------------------------------------------------------
; początek programu głównego, inicjalizacja
; stosu, portów I/O oraz zmiennych
;-------------------------------------------------------
.init
ld A,#$FF ;port A jako wyjściowy
ld PADDR,A ;i stan niski na wszystkich wyjściach
ld PAOR,A
clr A
ld PADR,A
ld PBDDR,A ;port B jako wejściowy
ld PBOR,A
ld A,#$01 ;f_CPU = f_OSC/32 @ 1MHz
ld MCCSR,A
ld A,#$12 ;f_TIMER = f_CPU
ld ATCSR,A
ld A,#$DF ;konfiguracja 12-bitowego timera
ld ATRL,A ;autoreload, przerwanie co ok.1 ms
ld A,#$0F
ld ATRH,A
ret
;-------------------------------------------------------
; funkcja obsługi przerwania timera B używana przez
; delay do odmierzania czasu
;-------------------------------------------------------
.timerb
ld A,ATCSR ;odczyt ATCSR w celu zerowania flagi przerwania
ld A,del ;do A wartość opóźnienia
and A,#$FF ;sprawdzenie, czy A=0
jreq timerb_exit
dec A
ld del,A
timerb_exit
iret
;-------------------------------------------------------
; funkcja realizująca opóźnienie "del" x 1ms (maks.255ms)
; wielkość opóźnienia w komórce "del"
;-------------------------------------------------------
.delay
push A ;zapamiętanie akumulatora na stosie
rim ;załączenie przerwań
d_loop
ld A,del ;zmienna "del" zmniejszana przez funkcje
and A,#$FF ;obsługi przerwania timer'a
jreq d_end ;w tej pętli funkcja oczekuje na wartość del=0
jp d_loop
d_end
sim ;wyłączenie przerwań
pop A ;odtworzenie zawartości akumulatora
ret
;-------------------------------------------------------
; wysłanie danej do wyświetlacza, rejestr docelowy
; określany przez inne funkcje-tu tylko przesłanie
; zmienna wejściowa w akumulatorze!
; RS=PA4,EN=PA7,PA0=DB4,PA1=DB5,PA2=DB6,PA3=DB7
;-------------------------------------------------------
.lcd_write
push Y
ld Y,A ;zapamiętanie akumulatora
ld A,PADR ;odczyt stanu portu A (bo mogą być podłączone do
;wyprowadzeń portów inne obwody,niż wyświetlacz)
or A,#$80 ;ENABLE=1
ld PADR,A
or A,#$0F ;ustawienie bitów PA.0-4 na "1" dla operacji AND
ld PADR,A
push A ;zapamiętanie akumulatora (tzn stanu PADR)
ld A,Y ;odtworzenie danej do przesłania
swap A ;zamiana połówek bajtu - starsza jako 1-sza
or A,#$F0 ;ustawienie "nieznaczącej" polowy bajtu na "1"
;dla operacji AND
ld copya,A ;zapamiętanie zmiennej do przesłania
pop A ;odtworzenie stanu PADR
and A,copya ;iloczyn logiczny dla bezkolizyjnego zapisu danych
ld PADR,A ;wyprowadzenie połówki bajtu
and A,#$7F ;zmiana stanu ENABLE - zapis bajtu do LCD
ld PADR,A
or A,#$80 ;ENABLE=1
ld PADR,A
or A,#$0F ;ustawienie bitów PA.0-4 na "1" dla operacji AND
ld PADR,A
push A ;zapamiętanie stanu PADR
ld A,Y ;odtworzenie danej do przesłania
or A,#$F0 ;ustawienie "nieznaczącej" polowy bajtu na "1"
;dla operacji AND
ld copya,A ;zapamiętanie zmiennej do przesłania
pop A ;odtworzenie stanu PADR
and A,copya ;iloczyn logiczny dla bezkolizyjnego zapisu danych
ld PADR,A ;wyprowadzenie połówki bajtu
and A,#$7F ;zmiana stanu ENABLE - zapis bajtu do LCD
ld PADR,A
ld A,#10 ;pauza - LCD akceptuje dane
call delay
pop Y
ret
;-------------------------------------------------------
; wysłanie zawartości akumulatora do rejestru
; konfiguracji wyświetlacza LCD
;-------------------------------------------------------
.lcd_reg_write
push A ;zapamiętanie zawartości akumulatora
ld A,PADR
and A,#$6F ;RS(D4)=0,ENABLE(D7)=0
ld PADR,A
pop A ;wysłanie zawartości akumulatora do LCD
call lcd_write
ret
;-------------------------------------------------------
; wysłanie zawartości akumulatora do rejestru
; danych wyświetlacza LCD
;-------------------------------------------------------
.lcd_data_write
push A
ld A,PADR
and A,#$6F ;RS(D4)=0,ENABLE(D7)=0
or A,#$10 ;RS(D4)=1
ld PADR,A
pop A
call lcd_write
ret
;-------------------------------------------------------
; inicjalizacja wyświetlacza LCD w trybie interfejsu
; o długości słowa 4 bity
;-------------------------------------------------------
.lcd_init
ld A,#100 ;pauza około 100 ms
ld del,A
call delay
ld A,PADR ;RS(D4)=0,ENABLE(D7)=0,dane=0
and A,#$60
ld PADR,A
ld Y,#3 ;3-krotne przesłanie 0x03 do lcd
or A,#3
ld PADR,A
lcd_init_loop
ld A,PADR ;odczyt stanu rejestru danych portu A
or A,#$80 ;ENABLE=1
ld PADR,A
nop
nop
and A,#$7F ;ENABLE=0
ld PADR,A
ld A,#5 ;opóźnienie 5 milisekund
ld del,A
call delay
dec Y ;czy juz powtórzono 3x?
jrne lcd_init_loop
ld A,#2 ;zapisanie "2" do rejestru kontrolnego
call lcd_reg_write
ld A,#$28 ;kolejne wartości inicjujące ($28,8,1,6,$C)
call lcd_reg_write
ld A,#8
call lcd_reg_write
ld A,#1
call lcd_reg_write
ld A,#6
call lcd_reg_write
ld A,#$C
call lcd_reg_write
ret
;-------------------------------------------------------
; kasowanie ekranu wyświetlacza LCD
;-------------------------------------------------------
.lcd_cls
push A
ld A,#1
call lcd_reg_write
pop A
ret
;-------------------------------------------------------
; ustawienie kursora na współrzędnych X,Y
; X i Y podawane w A w formacie A:Y<<4+X
;-------------------------------------------------------
.gotoaxy
ld Y,A ;zapamiętanie akumulatora w rejestrze "Y"
and A,#$0F ;wyodrębnienie parametru X
push A ;zapamiętanie parametru X na stosie
ld A,Y ;odtworzenie akumulatora, wyodrębnienie par.Y
sra A ;arytmetyczne przesuniecie w prawo
sra A ;najstarszy bit jest zastępowany przez "0"
sra A
sra A
cp A,#0 ;skok wykonywany,jeśli A=0 (dla Y=0)
jreq gzero
cp A,#1 ;jeśli A=1
jreq gone
cp A,#2 ;jeśli A=2
jreq gtwo
cp A,#3 ;jeśli A=3
jreq gthree
ret
;jeśli Y=0
gzero
ld A,#$80
ld copya,A ;obliczenie offsetu adresu dla Y=0
jp gotoaxy_exit
gone
ld A,#$C0
ld copya,A ;dla Y=1
jp gotoaxy_exit
gtwo
ld A,#$94
ld copya,A ;dla Y=2
jp gotoaxy_exit
gthree
ld A,#$D4
ld copya,A ;dla Y=3
gotoaxy_exit
pop A
add A,copya
call lcd_reg_write
ret
;-------------------------------------------------------
;pomiar napięcia (wartość zmierzona w komórce VVOLTS
;-------------------------------------------------------
volts
clr ADCCSR ;wyłączenie przetwornika
clr ADCDRL ;wyłączenie wzmacniacza wejściowego
ld A,#$40 ;ustawienie szybkości przetwarzania (SPEED=1)
ld ADCCSR,A
ld A,ADCDRH ;kasowanie flagi EOC
ld A,#$04
ld ADCCSR,A ;załączenie kanału pomiarowego AIN4
or A,#$20 ;uruchomienie pomiaru
ld ADCCSR,A
btjf ADCCSR,#7,* ;oczekiwanie na zakończenie pomiaru
clr vvolts ;kompozycja starszego bajtu wyniku
ld X,vvolts ;ustawienie dwóch najmłodszych bitów przez
;przesunięcie z flagą C
ld A,ADCDRH
sll A
rlc X
sll A
rlc X
ld vvolts,X ;zapamiętanie starszego bajtu
ld {vvolts+1},A ;kompozycja młodszego bajtu suma z dwoma
;młodszymi bitami z ADCDRL
ld A,ADCDRL
and A,#$03 ;maskowanie nieznaczących bitów
or A,{vvolts+1} ;sumowanie D0 i D1 wyniku
ld {vvolts+1},A ;zapamiętanie wyniku pomiaru
;----------------------------------------
;obliczenia dla napięcia Uref=5V
;Ux = Uref x vvolts = 5 x vvolts
;----------------------------------------
ld A,{vvolts+1} ;młodszy bajt wyniku pomiaru
ld X,vvolts ;starszy bajt wyniku pomiaru
sll A ;mnożenie x4 (2 przesunięcia w lewo)
rlc X
sll A
rlc X
ld {sub_a+1},A ;zapamiętanie liczby po mnożeniu x 4
ld sub_a,X ;w zmiennej SUB_A
ld A,{vvolts+1} ;dodawanie: 4vvolts + vvolts
add A,{sub_a+1}
ld {vvolts+1},A
ld A,vvolts
adc A,sub_a
ld vvolts,A
ret
;-------------------------------------------------------
; odejmowanie liczb 2-bajtowych
;-------------------------------------------------------
subw
push A ;zapamiętanie rejestrów roboczych
push X
ld A,{sub_a+1} ;pobranie młodszego liczby a
sub A,{sub_b+1} ;dodanie młodszego liczby b
ld {roznicaw+1},A ;zapamiętanie młodszego bajtu różnicy
ld A,sub_a ;pobranie starszego bajtu liczby a
sbc A,sub_b ;odjecie przeniesienia i starszego bajtu liczby b
ld roznicaw,A ;zapamiętanie starszego bajtu różnicy
pop X ;odtworzenie rejestrów
pop A
ret
;-------------------------------------------------------
;konwersja na liczbę dziesiętna; poszczególne wagi
;umieszczone w komórkach tablicy DECIMS
;uwaga: funkcja nieco "na wyrost"! wynik pomiaru nigdy
; nie przekroczy wartości 1024 dec. (3FFh)
;-------------------------------------------------------
bin_2_dec
ld Y,#6 ;zerowanie tablicy DECIMS
clr A
clr X
bin_2_dec_1
ld (decims,X),A
inc X
dec Y
jrne bin_2_dec_1
;załadowanie wyniku pomiaru do
ld A,vvolts ;komórek SUB_A
ld sub_a,A
ld A,{vvolts+1}
ld {sub_a+1},A
;liczenie ile razy 10000 mieści sie w liczbie
ld A,#$27
ld sub_b,A
ld A,#$10
ld {sub_b+1},A
bin_2_10000
callr subw ;odejmowanie SUB_A-SUB_B
jrc bin_2_10000_end ;jeśli C, to liczba<10000
inc decims ;zwiekszenie licznika 10000
ld A,roznicaw ;przeniesienie wyniku do SUB_A
ld sub_a,A
ld A,{roznicaw+1}
ld {sub_a+1},A
jra bin_2_10000 ;powtórne odejmowanie 10000
bin_2_10000_end ;liczenie ile razy 1000 mieści sie w liczbie
ld A,#3
ld sub_b,A
ld A,#$E8
ld {sub_b+1},A
bin_2_1000
callr subw
jrc bin_2_1000_end ;jeśli C, to liczba<1000
inc {decims+1} ;zwiększenie licznika 1000
ld A,roznicaw ;przeniesienie wyniku do SUB_A
ld sub_a,A
ld A,{roznicaw+1}
ld {sub_a+1},A
jra bin_2_1000 ;powtórne odejmowanie 1000
bin_2_1000_end ;liczenie ile razy 100 mieści sie w różnicy
clr sub_b
ld A,#100
ld {sub_b+1},A
bin_2_100
callr subw
jrc bin_2_100_end ;jeśli C, to liczba<100
inc {decims+2} ;zwiększenie licznika 100
ld A,roznicaw ;przeniesienie wyniku do SUB_A
ld sub_a,A
ld A,{roznicaw+1}
ld {sub_a+1},A
jra bin_2_100 ;powtórne odejmowanie 100
bin_2_100_end ;liczenie ile razy 10 mieści się w różnicy
clr sub_b
ld A,#10
ld {sub_b+1},A
bin_2_10
callr subw
jrc bin_2_10_end ;jeśli C, to liczba<10
inc {decims+3} ;zwiększenie licznika 10
ld A,roznicaw ;przeniesienie wyniku do SUB_A
ld sub_a,A
ld A,{roznicaw+1}
ld {sub_a+1},A
jra bin_2_10 ;powtórne odejmowanie 10
bin_2_10_end
ld A,{sub_a+1} ;pozostałość po odejmowaniu to jednostki
ld {decims+4},A
ret
;-------------------------------------------------------
;konwersja na napis w ASCII; poszczególne kody znaków
;umieszczone w komórkach tablicy DECIMS
;-------------------------------------------------------
dec_2_ascii
clr X ;X posłuży jako indeks
ld Y,#5 ;operacja dla kolejnych 5 bajtów
dec_2_ascii_loop
ld A,(decims,X) ;do akumulatora bajt z tablicy
add A,#'0' ;dodanie kodu zera
ld (decims,X),A ;zapamiętanie bajtu w tablicy
inc X
dec Y ;czy to juz wszystkie bajty?
jrne dec_2_ascii_loop
ret
;-------------------------------------------------------
;dodanie przecinka (po 1-szym znaku)
;-------------------------------------------------------
add_dp
clr X
ld Y,#1
ld A,(decims,Y) ;pierwsza cyfra to na pewno 0: nadpisujemy
ld (decims,X),A
inc X ;wstawiamy przecinek w miejsce 2-giej cyfry
ld A,#','
ld (decims,X),A
ret
;-------------------------------------------------------
; program główny, wyświetlenie tekstu zawartego w ROM
; i wyniku pomiaru napięcia na wejściu AIN4
;-------------------------------------------------------
.main
rsp
call init
call lcd_init
call lcd_cls ;czyszczenie LCD, ustawienie kursora
;w pozycji HOME
;wyświetlenie napisu
clr X ;zerowanie rejestru indeksowego
main0
ld A,(txt1,X) ;pobranie kodu znaku do A
and A,#$FF ;czy to koniec napisu (znak=0)?
jreq txt1end ;tak,następny napis
call lcd_data_write ;nie,zapis kodu znaku do LCD
inc X ;następny znak z napisu
jp main0
txt1end
ld A,#$10 ;współrzędne dla 2-giej linii
call gotoaxy
main1
call volts ;pomiar napięcia
call bin_2_dec ;konwersja na liczbę dziesiętna
call dec_2_ascii ;konwersja na ASCII
call add_dp ;formatowanie wyniku
ld Y,#5
clr X
main2
ld A,(decims,X) ;wyświetlenie wyniku pomiaru
call lcd_data_write
inc X
dec Y ;czy wszystkie 6 znaków
jrne main2
ld A,#'V' ;wyświetlenie jednostki
call lcd_data_write
ld A,#200 ;opóźnienie 0,2 sekundy
ld del,A
call delay
jra txt1end ;powtórz (pętla nieskończona)
;-------------------------------------------------------
; "pusta" funkcja, zawiera tylko instrukcję powrotu
; z obslugi przerwania
;-------------------------------------------------------
.it_ret iret
segment 'intvect'
;-------------------------------------------------------
; wektory przerwań
;-------------------------------------------------------
DC.W it_ret
DC.W it_ret
DC.W it_ret
.sci DC.W it_ret
.timb DC.W timerb
.tima DC.W it_ret
.spi DC.W it_ret
DC.W it_ret
DC.W it_ret
.ext3 DC.W it_ret
.ext2 DC.W it_ret
.ext1 DC.W it_ret
.ext0 DC.W it_ret
DC.W it_ret
.soft DC.W it_ret
.rst DC.W main
END
Dodaj nowy komentarz