ST7FLITE19 - Pomiar napięcia na wejściu AIN4

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

http://www.tomaszbogusz.blox.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

Zawartość pola nie będzie udostępniana publicznie.