ptm_spr_4.pdf

(93 KB) Pobierz
665004865 UNPDF
Marek Karpiński (181172), Michał Kaczara (181132)
Podstawy Techniki Mikroprocesorowej - laboratorium – piątek 11:15 TN / mgr A. Sterna
SPRAWOZDANIE NR 4
TEMAT: Mikrokontroler 8051 i wyświetlacz LCD HD44780 - zegarek
Opis ćwiczenia i programów
Naszym zadaniem podczas piątych zajęć laboratoryjnych było napisanie programu wykonywanego
przez mikrokontroler 8051 i realizującego zegarek, wyświetlany na wyświetlaczu LCD ze sterownikiem
HD44780. W tym celu musieliśmy użyć napisanych wcześniej procedur obsługi wyświetlacza oraz napisać
nowe, odpowiedzialne za generowanie i zmianę stanu zegarka. Musieliśmy także podzielić program na
segmenty oraz zdefiniować własne bloki danych i flagę bitową.
Poniżej zamieszczamy kod biblioteki procedur pozwalających na obsługę wyświetlacza.
Listing 1. Plik LCD.a51 – biblioteka procedur do obsługi wyświetlacza
;----------------------------------------------------------------------------------------------
; Definicje procedur udostępnianych publicznie
;----------------------------------------------------------------------------------------------
PUBLIC LCD_INIT, LCD_GOTO_XY, LCD_PUT_CHAR, LCD_PUT_STRING, LCD_PUT_DEC, LCD_PUT_HEX
PROG SEGMENT CODE
; Definicja segmentu kodu programu
RSEG PROG
; Segment kodu
WR_CMD EQU 0FF2CH
; Adres w pamięci XRAM dla linii sterowania
WR_DATA EQU 0FF2DH
; Adres w pamięci XRAM dla linii zapisu danych
RD_STATUS EQU 0FF2EH
; Adres w pamięci XRAM dla linii odczytu statusu
RD_DATA EQU 0FF2FH
; Adres w pamięci XRAM dla linii odczytu danych
INITDISP EQU 038H
; Kod inicjalizacji wyświetlacza
CLEAR EQU 001H
; Kod czyszczenia wyświetlacza
LCDON EQU 00EH
; Kod włączania wyświetlacza
;----------------------------------------------------------------------------------------------
; Przesyłanie zawartości na linię sterowania wyświetlacza
; Kod polecenia, które ma być wysłane - A
;----------------------------------------------------------------------------------------------
LCD_WR_CMD:
PUSH ACC ; Odłożenie akumulatora na stos
MOV DPTR, #RD_STATUS ; Przesłanie adresu linii odczytu statusu do rejestru DPTR
PETLA:
; Pętla sprawdzająca zajętość wyświetlacza
MOVX A, @DPTR
; Przesłanie wartości spod adresu w DPTR do akumulatora
JB ACC.7, PETLA
; Sprawdzenie bitu zajętości, jeśli ustawiony – skok do PETLA
MOV DPTR, #WR_CMD
; Przesłanie adresu linii sterowania do rejestru DPTR
POP ACC
; Zdjęcie akumulatora ze stosu
MOVX @DPTR, A
; Przesłanie zawartości akumulatora na linię sterowania
RET
; Wyjście z procedury
;----------------------------------------------------------------------------------------------
; Przesyłanie danych na linię zapisu danych wyświetlacza
; Dane, które mają zostać przesłane - A
;----------------------------------------------------------------------------------------------
LCD_WR_DATA:
PUSH ACC ; Odłożenie akumulatora na stos
MOV DPTR, #RD_STATUS ; Przesłanie adresu linii odczytu statusu do rejestru DPTR
PETLA1:
; Pętla sprawdzająca zajętość wyświetlacza
MOVX A, @DPTR
; Przesłanie wartości spod adresu w DPTR do akumulatora
JB ACC.7, PETLA1
; Sprawdzenie bitu zajętości, jeśli ustawiony – skok do PETLA1
MOV DPTR, #WR_DATA
; Przesłanie adresu linii zapisu danych do rejestru DPTR
POP ACC
; Zdjęcie akumulatora ze stosu
MOVX @DPTR, A
; Przesłanie zawartości akumulatora na linię zapisu danych
RET
; Wyjście z procedury
1
665004865.001.png
;----------------------------------------------------------------------------------------------
; Incijalizacja wyświetlacza
; Brak argumentów
;----------------------------------------------------------------------------------------------
LCD_INIT:
MOV A, #INITDISP
; Przesłanie kodu inicjalizacji do akumulatora
CALL LCD_WR_CMD
; Wywołanie procedury piszącej na linię sterowania
MOV A, #CLEAR
; Przesłanie kodu czyszczenia wyświetlacza do akumulatora
CALL LCD_WR_CMD
; Wywołanie procedury piszącej na linię sterowania
MOV A, #LCDON
; Przesłanie kodu włączania wyświetlacza do akumulatora
CALL LCD_WR_CMD
; Wywołanie procedury piszącej na linię sterowania
RET
; Wyjście z procedury
;----------------------------------------------------------------------------------------------
; Ustawianie kursora na pozycji o podanych współrzędnych x i y
; Współrzędne kursora y: 1 bit(0,1), x: 4 bity(0,15) - A(---yxxxx)
;----------------------------------------------------------------------------------------------
LCD_GOTO_XY:
MOV R2, #0
; Wyzerowanie rejestru R2(y)
JNB ACC.5, DALEJ
; Jeśli 5. bit akumulatora(y) = 0 – skok do DALEJ
MOV R2, #1
; Jeśli nie, to y = 1
DALEJ:
MOV R3, A
; Przesłanie akumulatora do R3
MOV A, #00001111b
; Przesłanie maski dla x do akumulatora
ANL A, R3
; Zastosowanie maski
MOV R1, A
; Przesłanie wartości z A(x) do R1
MOV A, R2
; Przesłanie y do akumulatora
MOV B, #64
; Przesłanie 64 – liczby bajtów w linii LCD do rejestru B
MUL AB
; Pomnożenie A i B(wyznaczenie adresu pamięci dla y)
ADD A, R1
; Dodanie do akumulatora współrzędnej x
ORL A, #128
; Włączenie adresowania pamięci DD-RAM wyświetlacza
CALL LCD_WR_CMD
; Wywołanie procedury piszącej na linię sterowania
RET
; Wyjście z procedury
RET
;----------------------------------------------------------------------------------------------
; Wypisywanie znaku na wyświetlaczu
; Kod ASCII znaku, który ma zostać wypisany - A
;----------------------------------------------------------------------------------------------
LCD_PUT_CHAR:
CALL LCD_WR_DATA
; Wywołanie procedury przesyłającej dane do wyświetlacza
RET
; Wyjście z procedury
;----------------------------------------------------------------------------------------------
; Wypisywanie łańcucha znaków na wyświetlaczu
; Wskaźnik na łańcuch, który ma zostać wypisany - DPTR
;----------------------------------------------------------------------------------------------
LCD_PUT_STRING:
CLR A
; Wyzerowanie akumulatora
MOVC A, @A+DPTR
; Odczytanie kolejnego znaku z pamięci programu
JZ KONIEC
; Jeżeli 0(koniec łańcucha), skok do końca procedury
PUSH DPH
; Odłożenie na stos starszych bitów adresu łańcucha
PUSH DPL
; Odłożenie na stos młodszych bitów adresu łańcucha
CALL LCD_PUT_CHAR
; Wywołanie procedury wypisującej znak na wyświetlaczu
POP DPL
; Zdjęcie ze stosu młodszych bitów adresu łańcucha
POP DPH
; Zdjęcie ze stosu starszych bitów adresu łańcucha
INC DPTR
; Zwiększenie adresu łańcucha(wskazuje na kolejny znak)
SJMP LCD_PUT_STRING
; Pętla instrukcji, skok do początku procedury
KONIEC:
RET
; Wyjście z procedury
;----------------------------------------------------------------------------------------------
; Wypisywanie liczby(DEC) z zaresu 0 - 99 na wyświetlaczu
; Liczba, która ma zostać wypisana - A
;----------------------------------------------------------------------------------------------
LCD_PUT_DEC:
MOV B, #10
; Przesłanie do B bazy systemu - 10
DIV AB
; Dzielenie liczby w akumulatorze przez bazę systemu
ADD A, #48
; Dodanie do akumulatora 48(otrzymamy kod ASCII cyfry)
CALL LCD_PUT_CHAR
; Wyświetlenie cyfry na LCD
MOV A, B
; Przesłanie do akumulatora reszty z dzielenia
2
ADD A, #48
; Dodanie do akumulatora 48(otrzymamy kod ASCII cyfry)
CALL LCD_PUT_CHAR
; Wyświetlenie cyfry na LCD
RET
; Wyjście z procedury
;----------------------------------------------------------------------------------------------
; Wypisywanie liczby(HEX) z zakresu 0 - 255 na wyświetlaczu
; Liczba, która ma zostać wypisana - A
;----------------------------------------------------------------------------------------------
LCD_PUT_HEX:
MOV B, #16
; Przesłanie do B bazy systemu - 16
DIV AB
; Dzielenie liczby w akumulatorze przez bazę systemu
MOV R1, A
; Backup akumulatora w R1
ADD A, #246
; Dodanie 246 do akumulatora - sprawdzenie czy A > 9
JNC DALEJ1
; Jeśli nie ma przeniesienia to skok do DALEJ1
MOV A, R1
; Jeśli jest - restore akumulatora z R1
ADD A, #55
; Dodanie 55 do akumulatora(otrzymamy kod ASCII litery)
CALL LCD_PUT_CHAR
; Wyświetlenie litery na LCD
JMP RESZTA
; Skok wymijający - przejście do konwersji reszty
DALEJ1:
MOV A, R1
; Restore akumulatora z R1
ADD A, #48
; Dodanie do akumulatora 48(otrzymamy kod ASCII cyfry)
CALL LCD_PUT_CHAR
; Wyświetlenie cyfry na LCD
RESZTA:
MOV A, B
; Przesłanie do akumulatora reszty z dzielenia
ADD A, #246
; Dodanie 246 do akumulatora - sprawdzenie czy A > 9
JNC DALEJ2
; Jeśli nie ma przeniesienia to skok do DALEJ2
MOV A, B
; Przesłanie do akumulatora reszty z dzielenia
ADD A, #55
; Dodanie 55 do akumulatora(otrzymamy kod ASCII litery)
CALL LCD_PUT_CHAR
; Wyświetlenie litery na LCD
JMP KONIEC_PUT_HEX
; Skok wymijający - przejście do końca procedury
DALEJ2:
MOV A, B
; Przesłanie reszty do akumulatora
ADD A, #48
; Dodanie do akumulatora 48(otrzymamy kod ASCII cyfry)
CALL LCD_PUT_CHAR
; Wyświetlenie cyfry na LCD
KONIEC_PUT_HEX:
RET
; Wyjście z procedury
END
; Koniec kodu w pliku LCD.a51
Zamieszczony powyżej Listing 1. zawiera procedury napisane podczas 4. zajęć laboratoryjnych.
Zawiera także poprawioną procedurę LCD_GOTO_XY oraz dwie nowe: LCD_PUT_DEC(wyświetlającą na
wyświetlaczu liczbę z zakresu 0 – 99 w formacie dziesiętnym) oraz LCD_PUT_HEX(wyświetlającą liczbę
z zakresu 0 - 255 w formacie szesnastkowym).
Z kolei Listing 2. zamieszczony poniżej zawiera kod głównego programu, wraz z procedurami
inicjalizacji timera(TIMER_INIT) oraz realizacji jego zadań w ramach obsługi przerwania(TIMER_INT).
Ważną funkcję pełni też procedura LCD_DISP_TIME, wyświetlająca aktualny stan zegarka na wyświetlaczu.
Widoczny jest tu także podział programu na segmenty: kodu, danych oraz bitów.
Listing 2. Plik 4.a51 – główny program realizujący zegarek
;----------------------------------------------------------------------------------------------
; Załączenie publicznych procedur zdefiniowanych w pliku LCD.a51
;----------------------------------------------------------------------------------------------
EXTRN CODE (LCD_INIT, LCD_GOTO_XY, LCD_PUT_CHAR, LCD_PUT_HEX)
PROG SEGMENT CODE
; Definicja segmentu kodu programu
DANE SEGMENT DATA
; Definicja semgnetu danych
BITY SEGMENT BIT
; Definicja semgentu bitów
STOS SEGMENT IDATA
; Definicja segmentu danych w pamięci IRAM
TIME EQU 50 ; Zmienna - czas do odmierzenia w ms
LOAD EQU (65536 - TIME*1000) ; Zmienna - liczba cykli potrzebna do odmierzenia czasu
CSEG AT 0
; Segment kodu - nast. instrukcja pod adresem 0x000
MOV SP, #(STOS-1)
; Obliczenie odpowiedniej wartości wsk. na początek stosu
LJMP START
; Skok do początku programu
3
665004865.002.png
CSEG AT 0BH
; Segment kodu - nast. instrukcja pod adresem 0x0BH
LJMP TIMER_INT
; Skok do procedury obsługi przerwania
RSEG PROG
; Segment kodu
START:
CALL LCD_INIT
; Wywołanie procedury inicjalizującej wyświetlacz
CALL TIMER_INIT
; Wywołanie procedury inicjalizującej timer
PETLA_WYSW:
; Głowna pętla programu
CALL LCD_DISP_TIME
; Wywołanie procedury wyświetlającej zegarek na LCD
JNB FLAGA, $
; Jeśli flaga zmian nieustawiona to pętla instrukcji
CLR FLAGA
; Wyczyszczenie flagi
SJMP PETLA_WYSW
; Skok na początek pętli
JMP $
; Pętla instrukcji - zapobiega wejściu do procedur
;----------------------------------------------------------------------------------------------
; Wyświetlanie stanu zegarka na LCD
; Brak argumentów
;----------------------------------------------------------------------------------------------
LCD_DISP_TIME:
MOV A, #00000100B
; Przesłanie współrzędnych do akumulatora(x=4,y=0)
CALL LCD_GOTO_XY
; Wywołanie procedury ustawiającej kursor na pozycji(x,y)
MOV A, HOUR
; Przesłanie aktualnej liczby godzin do akumulatora
CALL LCD_PUT_DEC
; Wyświetlenie godzin na LCD
MOV A, #':'
; Przesłanie do akumulatora separatora - ":"
CALL LCD_PUT_CHAR
; Wyświetlenie separatora na LCD
MOV A, MIN
; Przesłanie aktualnej liczby minut do akumulatora
CALL LCD_PUT_DEC
; Wyświetlenie minut na LCD
MOV A, #':'
; Przesłanie do akumulatora separatora - ":"
CALL LCD_PUT_CHAR
; Wyświetlenie separatora na LCD
MOV A, SEC
; Przesłanie aktualnej liczby sekund do akumulatora
CALL LCD_PUT_DEC
; Wyświetlenie sekund na LCD
RET
; Wyjście z procedury
;----------------------------------------------------------------------------------------------
; Inicjalizacja timera wykorzystującego mechanizm przerwań, odmierzanie 20*50 ms = 1 s
; Brak argumentów
;----------------------------------------------------------------------------------------------
TIMER_INIT:
MOV HOUR, #0
; Wyzerowanie godzin
MOV MIN, #0
; Wyzerowanie minut
MOV SEC, #0
; Wyzerowanie sekund
CLR TR0
; Wyłączenie timera
ANL TMOD, #0F0H
; Wyzerowanie rejestru sterującego timera nr 0
ORL TMOD, #1
; Ustawienie trybu timera na 16 bitów
MOV TH0, #HIGH(LOAD) ; Załadowanie starszych bitów zmiennej z liczbą cykli(stop)
MOV TL0, #LOW(LOAD) ; Załadowanie starszych bitów zmiennej z liczbą cykli(stop)
MOV COUNT, #20
; Ustawienie licznika timera na 20
CLR TF0
; Wyzerowanie bitu przepełnienia
SETB ET0
; Ustawienie obsługi przerwania dla timera nr 0
SETB EA
; Ustawienie obsługi przerwania
SETB TR0
; Włączenie timera
RET
; Wyjście z procedury
;----------------------------------------------------------------------------------------------
; Realizacja zadań timera w procedurze obsługi przerwania
; Brak argumentów
;----------------------------------------------------------------------------------------------
TIMER_INT:
PUSH PSW ; Backup rejestru flag na stosie
PUSH ACC ; Backup akumulatora na stosie
MOV TH0, #HIGH(LOAD) ; Załadowanie starszych bitów zmiennej z liczbą cykli(stop)
MOV TL0, #LOW(LOAD) ; Załadowanie starszych bitów zmiennej z liczbą cykli(stop)
DJNZ COUNT, KONIEC
; Jeśli licznik > 0 - nie odmierzono sekundy - skok do KONIEC
MOV COUNT, #20
; Jeśli nie, ponowne ustawienie licznika timera
SETB FLAGA
; ustawienie flagi zmian
INC SEC
; Zwiększenie liczby sekund
MOV A, SEC
; Przesłanie liczby sekund do akumulatora
4
CJNE A, #60, KONIEC
; Jeśli liczba sekund != 60, skok na koniec procedury
MOV SEC, #0
; Wyzerowanie sekund
INC MIN
; Zwiększenie liczby minut
MOV A, MIN
; Przesłanie liczby minut do akumulatora
CJNE A, #60, KONIEC
; Jeśli liczba minut != 60, skok na koniec procedury
MOV MIN, #0
; Wyzerowanie minut
INC HOUR
; Zwiększenie liczby godzin
MOV A, HOUR
; Przesłanie liczby godzin do akumulatora
CJNE A, #24, KONIEC
; Jeśli liczba godzin != 24, skok na koniec procedury
MOV HOUR, #0
; Wyzerowanie godzin
KONIEC:
POP ACC
; Restore akumulatora ze stosu
POP PSW
; Restore rejestru flag ze stosu
RETI
; Wyjście z procedury obsługi przerwania
;----------------------------------------------------------------------------------------------
; Segmenty danych, bitów i stosu
;----------------------------------------------------------------------------------------------
RSEG DANE
; Segment danych
SEC: DS 1
; Etykieta wskazująca na blok pamięci dla sekund
MIN: DS 1
; Etykieta wskazująca na blok pamięci dla minut
HOUR: DS 1
; Etykieta wskazująca na blok pamięci dla godzin
COUNT: DS 1
; Etykieta wskazująca na blok pamięci dla licznika timera
RSEG BITY
; Segment bitów
FLAGA: DBIT 1
; Etykieta wskazująca na blok pamięci dla flagi zmian
RSEG STOS
; Segment stosu
DS 20
; Blok pamięci o rozmiarze 20 bajtów
END
; Koniec kodu programu
Wnioski
Samodzielne napisanie programu realizującego zegarek było dla nas bardzo przydatnym
i pouczającym doświadczeniem. Program ten był niejako podsumowaniem dotychczasowej wiedzy
zdobytej na zajęciach laboratoryjnych. Wykorzystaliśmy w nim mechanizm timerów wbudowany
w mikrokontroler 8051, mechanizm obsługi przerwań, różnorodne tryby adresowania oraz operacje na
stosie. Dowiedzieliśmy się jak dzielić kod na segmenty, ułatwiające pracę z większymi plikami oraz
definiowanie własnych przestrzeni danych i flag. Przećwiczyliśmy też udostępnianie i dołączanie procedur
zaimplementowanych w różnych plikach – wykorzystaliśmy stworzone podczas 4. zajęć laboratoryjnych
procedury do utworzenia biblioteki obsługującej wyświetlacz LCD. Po napisaniu wyświetlania czasu
w formacie dziesiętnym postanowiliśmy zaimplementować też możliwość jego wyświetlenia w formacie
heksadecymalnym. Obie wersje działały bez zarzutów.
5
665004865.003.png
Zgłoś jeśli naruszono regulamin