zad1
ljmp start
P5 equ 0F8H
P7 equ 0DBH
LCDstatus equ 0FF2EH ; adres do odczytu gotowosci LCD
LCDcontrol equ 0FF2CH ; adres do podania bajtu sterujacego LCD
LCDdataWR equ 0FF2DH ; adres do podania kodu ASCII na LCD
// bajty sterujace LCD, inne dostepne w opisie LCD na stronie WWW
#define HOME 0x80 // put cursor to second line
#define INITDISP 0x38 // LCD init (8-bit mode)
#define HOM2 0xc0 // put cursor to second line
#define LCDON 0x0e // LCD nn, cursor off, blinking off
#define CLEAR 0x01 // LCD display clear
// linie klawiatury - sterowanie na port P5
#define LINE_1 0x7f // 0111 1111
#define LINE_2 0xbf // 1011 1111
#define LINE_3 0xdf // 1101 1111
#define LINE_4 0xef // 1110 1111
#define ALL_LINES 0x0f // 0000 1111
ORG 000BH ; obsluga przerwania
MOV TH0, #3CH ; przeladowanie
MOV TL0, #0B0H ; stalej timera na 50ms
DEC R0 ; korekta licznika
RETI ; powr�t z przerwania
org 0100H
// macro do wprowadzenia bajtu sterujacego na LCD
LCDcntrlWR MACRO x ; x � parametr wywolania macra � bajt sterujacy
LOCAL loop ; LOCAL oznacza ze etykieta loop moze sie powt�rzyc w programie
loop: MOV DPTR,#LCDstatus ; DPTR zaladowany adresem statusu
MOVX A,@DPTR ; pobranie bajtu z biezacym statusem LCD
JB ACC.7,loop ; testowanie najstarszego bitu akumulatora
; � wskazuje gotowosc LCD
MOV DPTR,#LCDcontrol ; DPTR zaladowany adresem do podania bajtu sterujacego
MOV A, x ; do akumulatora trafia argument wywolania macra�bajt sterujacy
MOVX @DPTR,A ; bajt sterujacy podany do LCD � zadana akcja widoczna na LCD
ENDM
// macro do wypisania znaku ASCII na LCD, znak ASCII przed wywolaniem macra ma byc w A
LCDcharWR MACRO
LOCAL tutu ; LOCAL oznacza ze etykieta tutu moze sie powt�rzyc w programie
PUSH ACC ; odlozenie biezacej zawartosci akumulatora na stos
tutu: MOV DPTR,#LCDstatus ; DPTR zaladowany adresem statusu
MOVX A,@DPTR ; pobranie bajtu z biezacym statusem LCD
JB ACC.7,tutu ; testowanie najstarszego bitu akumulatora
; � wskazuje gotowosc LCD
MOV DPTR,#LCDdataWR ; DPTR zaladowany adresem do podania bajtu sterujacego
POP ACC ; w akumulatorze ponownie kod ASCII znaku na LCD
MOVX @DPTR,A ; kod ASCII podany do LCD � znak widoczny na LCD
ENDM
// macro do inicjalizacji wyswietlacza � bez parametr�w
init_LCD MACRO
LCDcntrlWR #INITDISP ; wywolanie macra LCDcntrlWR � inicjalizacja LCD
LCDcntrlWR #CLEAR ; wywolanie macra LCDcntrlWR � czyszczenie LCD
LCDcntrlWR #LCDON ; wywolanie macra LCDcntrlWR � konfiguracja kursora
ENDM
// funkcja wypisania liczby dla potrzeb zegara
putdigitLCD: mov b, #10
div ab ; uzyskanie cyfry dziesiatek
add a, #30H ; konwersja cyfry na kod ASCII
acall putcharLCD
mov a, b ; ladowanie cyfry jednosci
add a, #30H ; konwersja na LCD
acall putcharLCD
ret
// funkcaj wypisywania znaku na LCD
putcharLCD: LCDcharWR
ret
// wyznaczanie biezacej wartosci zegara i jego wyswietlanie na LCD
ZEGAR: INC R7 ; licznik sekund
MOV A, R7 ; obsluga sekund
CLR C
SUBB A, #60 ; przepelnienie sekund
JZ MINUTY
LCDcntrlWR #HOME ; wyswietlenie calego zegara
MOV A, R5 ; godziny
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R6 ; minuty
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R7 ; sekundy
ACALL putdigitLCD
JMP FINAL
MINUTY: MOV R7, #00H ; zerowanie sekund
INC R6 ; licznik minut
MOV A, R6 ; obsluga minut
CLR C
SUBB A, #60 ; przepelnienie minut
JZ GODZINY
LCDcntrlWR #HOME ; wyswietlenie calego zegara
MOV A, R5 ; godziny
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R6 ; minuty
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R7 ; sekundy
ACALL putdigitLCD
JMP FINAL
GODZINY: MOV R6, #00H ; zerowanie minut
INC R5 ; licznik godzin
MOV A, R5
CLR C
SUBB A, #24 ; przepelenienie godzin - doba
JNZ EKRAN
MOV R5, #00H ; zerowanie godzin
EKRAN: LCDcntrlWR #HOME ; wyswietlenie calego zegara
MOV A, R5 ; godziny
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R6 ; minuty
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R7 ; sekundy
ACALL putdigitLCD
FINAL: RET
; program gl�wny
START: init_LCD
MOV TMOD, #01H ; konfiguracja timera
MOV TH0, #3CH ; ladowanie
MOV TL0, #0B0H ; stalej timera na 50ms
SETB TR0 ; timer start
MOV IE, #82H ; przerwania wlacz
MOV R5, #00H ; inicjacja zegara
MOV R6, #00H
MOV R7, #0FFH
ACALL ZEGAR ; wyswietlenie zainicjowanego zegara
MOV A, #0FH
MOV P1, A ; zapalenie di�d
MOV R0, #20 ; licznik odmierzen 20 x 50ms
CZEKAM:
key_1: mov r1, #LINE_1
mov a, r1
mov P5, a
mov a, P7
anl a, r1
mov r2, a
clr c
subb a, r1
jz key_2
mov a, r2
clr c
subb a, #7eh
jnz key_2
MOV TH0, #3CH ; ladowanie
MOV TL0, #0B0H ; stalej timera na 50ms
SETB TR0 ; timer start
key_2: mov r1, #LINE_2
mov a, r1
mov P5, a
mov a, P7
anl a, r1
mov r2, a
clr c
subb a, r1
jz key_3
mov a, r2
clr c
subb a, #0beh
jnz key_3
clr TR0
key_3: mov r1, #LINE_3
mov a, r1
mov P5, a
mov a, P7
anl a, r1
mov r2, a
clr c
subb a, r1
jz key_4
mov a, r2
clr c
subb a, #0deh
jnz key_4
ljmp START
key_4: mov r1, #LINE_4
mov a, r1
mov P5, a
mov a, P7
anl a, r1
mov r2, a
clr c
subb a, r1
mov a, r2
MOV A, R0 ; czekam, a timer
JNZ CZEKAM ; mierzy laczny czas 1s
MOV R0, #20 ; po zgloszeniu przerwania - ustawiam na nowo licznik odmierzen 20 x 50ms
ACALL ZEGAR ; uruchomienie procedury oblugi i wyswietlenia zegara
MOV A, P1 ; zmiana
CPL A ; swiecenia
MOV P1, A
; di�d
JMP CZEKAM ; czekam na kolejna sekunde
NOP
NOP
NOP
JMP $
END START
zad2
ljmp start
P5 equ 0F8H
P7 equ 0DBH
LCDstatus equ 0FF2EH ; adres do odczytu gotowosci LCD
LCDcontrol equ 0FF2CH ; adres do podania bajtu sterujacego LCD
LCDdataWR equ 0FF2DH ; adres do podania kodu ASCII na LCD
// bajty sterujace LCD, inne dostepne w opisie LCD na stronie WWW
#define HOME 0x80 // put cursor to second line
#define INITDISP 0x38 // LCD init (8-bit mode)
#define HOM2 0xc0 // put cursor to second line
#define LCDON 0x0e // LCD nn, cursor off, blinking off
#define CLEAR 0x01 // LCD display clear
// linie klawiatury - sterowanie na port P5
#define LINE_1 0x7f // 0111 1111
#define LINE_2 0xbf // 1011 1111
#define LINE_3 0xdf // 1101 1111
#define LINE_4 0xef // 1110 1111
#define ALL_LINES 0x0f // 0000 1111
ORG 000BH ; obsluga przerwania
MOV TH0, #3CH ; przeladowanie
MOV TL0, #0B0H ; stalej timera na 50ms
DEC R0 ; korekta licznika
RETI ; powr�t z przerwania
org 0100H
// macro do wprowadzenia bajtu sterujacego na LCD
LCDcntrlWR MACRO x ; x � parametr wywolania macra � bajt sterujacy
LOCAL loop ; LOCAL oznacza ze etykieta loop moze sie powt�rzyc w programie
loop: MOV DPTR,#LCDstatus ; DPTR zaladowany adresem statusu
MOVX A,@DPTR ; pobranie bajtu z biezacym statusem LCD
JB ACC.7,loop ; testowanie najstarszego bitu akumulatora
; � wskazuje gotowosc LCD
MOV DPTR,#LCDcontrol ; DPTR zaladowany adresem do podania bajtu sterujacego
MOV A, x ; do akumulatora trafia argument wywolania macra�bajt sterujacy
MOVX @DPTR,A ; bajt sterujacy podany do LCD � zadana akcja widoczna na LCD
ENDM
// macro do wypisania znaku ASCII na LCD, znak ASCII przed wywolaniem macra ma byc w A
LCDcharWR MACRO
LOCAL tutu ; LOCAL oznacza ze etykieta tutu moze sie powt�rzyc w programie
PUSH ACC ; odlozenie biezacej zawartosci akumulatora na stos
tutu: MOV DPTR,#LCDstatus ; DPTR zaladowany adresem statusu
MOVX A,@DPTR ; pobranie bajtu z biezacym statusem LCD
JB ACC.7,tutu ; testowanie najstarszego bitu akumulatora
; � wskazuje gotowosc LCD
MOV DPTR,#LCDdataWR ; DPTR zaladowany adresem do podania bajtu sterujacego
POP ACC ; w akumulatorze ponownie kod ASCII znaku na LCD
MOVX @DPTR,A ; kod ASCII podany do LCD � znak widoczny na LCD
ENDM
// macro do inicjalizacji wyswietlacza � bez parametr�w
init_LCD MACRO
LCDcntrlWR #INITDISP ; wywolanie macra LCDcntrlWR � inicjalizacja LCD
LCDcntrlWR #CLEAR ; wywolanie macra LCDcntrlWR � czyszczenie LCD
LCDcntrlWR #LCDON ; wywolanie macra LCDcntrlWR � konfiguracja kursora
ENDM
delay: mov r1, #0FFH
dwa: mov r2, #0FFH
trzy: djnz r2, trzy
djnz r1, dwa
ret
// funkcja wypisania liczby dla potrzeb zegara
putdigitLCD: mov b, #10
div ab ; uzyskanie cyfry dziesiatek
add a, #30H ; konwersja cyfry na kod ASCII
acall putcharLCD
mov a, b ; ladowanie cyfry jednosci
add a, #30H ; konwersja na LCD
acall putcharLCD
ret
// funkcaj wypisywania znaku na LCD
putcharLCD: LCDcharWR
ret
// tablica przekodowania klawisze - ASCII w XRAM
keyascii_num: mov dptr, #80EBH
mov a, #0
movx @dptr, a
mov dptr, #8077H
mov a, #1
movx @dptr, a
mov dptr, #807BH
mov a, #2
movx @dptr, a
mov dptr, #807DH
mov a, #3
movx @dptr, a
mov dptr, #80B7H
mov a, #4
movx @dptr, a
mov dptr, #80BBH
mov a, #5
movx @dptr, a
mov dptr, #80BDH
mov a, #6
movx @dptr, a
mov dptr, #80D7H
mov a, #7
movx @dptr, a
mov dptr, #80DBH
mov a, #8
movx @dptr, a
mov dptr, #80DDH
mov a, #9
movx @dptr, a
mov dptr, #807EH
mov a, #"A"
movx @dptr, a
mov dptr, #80BEH
mov a, #"B"
movx @dptr, a
mov dptr, #80DEH
mov a, #"C"
movx @dptr, a
mov dptr, #80EEH
mov a, #"D"
movx @dptr, a
mov dptr, #80E7H
mov a, #"*"
movx @dptr, a
mov dptr, #80EDH
mov a, #"#"
movx @dptr, a
ret
// wyznaczanie biezacej wartosci zegara i jego wyswietlanie na LCD
ZEGAR: INC R7 ; licznik sekund
MOV A, R7 ; obsluga sekund
CLR C
SUBB A, #60 ; przepelnienie sekund
JZ MINUTY
LCDcntrlWR #HOME ; wyswietlenie calego zegara
MOV A, R5 ; godziny
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R6 ; minuty
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R7 ; sekundy
ACALL putdigitLCD
LCDcntrlWR #HOM2
mov a, r4
acall putdigitLCD
mov a, r3
acall putdigitLCD
JMP FINAL
MINUTY: MOV R7, #00H ; zerowanie sekund
INC R6 ; licznik minut
MOV A, R6 ; obsluga minut
CLR C
SUBB A, #60 ; przepelnienie minut
JZ GODZINY
LCDcntrlWR #HOME ; wyswietlenie calego zegara
MOV A, R5 ; godziny
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R6 ; minuty
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R7 ; sekundy
ACALL putdigitLCD
LCDcntrlWR #HOM2
mov a, r4
acall putdigitLCD
mov a, r3
acall putdigitLCD
JMP FINAL
GODZINY: MOV R6, #00H ; zerowanie minut
INC R5 ; licznik godzin
MOV A, R5
CLR C
SUBB A, #24 ; przepelenienie godzin - doba
JNZ EKRAN
MOV R5, #00H ; zerowanie godzin
EKRAN: LCDcntrlWR #HOME ; wyswietlenie calego zegara
MOV A, R5 ; godziny
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R6 ; minuty
ACALL putdigitLCD
MOV A, #":" ; separator
ACALL putcharLCD
MOV A, R7 ; sekundy
ACALL putdigitLCD
LCDcntrlWR #HOM2
mov a, r4
acall putdigitLCD
mov a, r3
acall putdigitLCD
FINAL: RET
ostatniedwie: mov a, r3
mov r4, a
movx a, @dptr
mov r3, a
acall delay
ljmp key_1
; program gl�wny
START: init_LCD
MOV TMOD, #01H ; konfiguracja timera
MOV TH0, #3CH ; ladowanie
MOV TL0, #0B0H ; stalej timera na 50ms
SETB TR0 ; timer start
MOV IE, #82H ; przerwania wlacz
MOV R5, #00H ; inicjacja zegara
MOV R6, #00H
MOV R7, #0FFH
ACALL ZEGAR ; wyswietlenie zainicjowanego zegara
MOV A, #0FH
MOV P1, A ; zapalenie di�d
MOV R0, #20 ; licznik odmierzen 20 x 50ms
acall keyascii_num
mov r3, #0
mov r4, #0
CZEKAM:
key_1: mov r1, #LINE_1
mov a, r1
mov P5, a
mov a, P7
anl a, r1
mov r2, a
clr c
subb a, r1
jz key_2
mov a, r2
mov dph, #80h
mov dpl, a
movx a, @dptr
clr c
subb a, #"A"
jnz ostatniedwie
MOV TH0, #3CH ; ladowanie
MOV TL0, #0B0H ; stalej timera na 50ms
SETB TR0 ; timer start
key_2: mov r1, #LINE_2
mov a, r1
mov P5, a
mov a, P7
anl a, r1
mov r2, a
clr c
subb a, r1
jz key_3
mov a, r2
mov dph, #80h
mov dpl, a
movx a, @dptr
clr c
subb a, #"B"
jnz jumpostatniedwie
clr TR0
key_3: mov r1, #LINE_3
mov a, r1
mov P5, a
mov a, P7
anl a, r1
mov r2, a
clr c
subb a, r1
jz key_4
mov a, r2
mov dph, #80h
mov dpl, a
movx a, @dptr
clr c
subb a, #"C"
jnz jumpostatniedwie
ljmp START
key_4: mov r1, #LINE_4
mov a, r1
mov P5, a
mov a, P7
anl a, r1
mov r2, a
clr c
subb a, r1
mov a, r2
mov dph, #80h
mov dpl, a
movx a, @dptr
clr c
subb a, #"#"
jz ustawgodzine
movx a, @dptr
subb a, #"*"
jz ustawminute
movx a, @dptr
jz jumpostatniedwie
MOV A, R0 ; czekam, a timer
JNZ CZEKAM ; mierzy laczny czas 1s
MOV R0, #20 ; po zgloszeniu przerwania - ustawiam na nowo licznik odmierzen 20 x 50ms
ACALL ZEGAR ; uruchomienie procedury oblugi i wyswietlenia zegara
MOV A, P1 ; zmiana
CPL A ; swiecenia
MOV P1, A
; di�d
JMP CZEKAM ; czekam na kolejna sekunde
NOP
NOP
NOP
JMP $
ustawgodzine:
mov a, r4
mov b, #10
mul ab
add a, r3
mov b, #24
div ab
mov r5, b
ljmp key_1
ustawminute:
mov a, r4
mov b, #10
mul ab
add a, r3
mov b, #60
div ab
mov r6, b
ljmp key_1
jumpostatniedwie:
ljmp ostatniedwie
END START
; Skok nad wektorami przerwań do głównego programu
ljmp START
; — Definicje adresów sprzętowych (Mapowanie pamięci) —
P5 equ 0F8H ; Port sterujący liniami klawiatury matrycowej
P7 equ 0DBH ; Port odczytujący kolumny klawiatury matrycowej
LCDstatus equ 0FF2EH ; Adres odczytu flagi zajętości LCD (Busy Flag)
LCDcontrol equ 0FF2CH ; Adres zapisu instrukcji do LCD
LCDdataWR equ 0FF2DH ; Adres zapisu danych (znaków) do LCD
; — Stałe sterujące LCD —
#define HOME 0x80 ; Ustaw kursor na początku pierwszej linii
#define INITDISP 0x38 ; Konfiguracja: 8-bit, 2 linie, font 5×7
#define LCDON 0x0e ; Włącz wyświetlacz, kursor włączony
#define CLEAR 0x01 ; Czyść ekran
; — Maski dla klawiatury matrycowej (wybór wiersza) —
#define LINE_1 0x7f ; 01111111B – aktywacja 1. wiersza
#define LINE_2 0xbf ; 10111111B – aktywacja 2. wiersza
#define LINE_3 0xdf ; 11011111B – aktywacja 3. wiersza
#define LINE_4 0xef ; 11101111B – aktywacja 4. wiersza
; — WEKTORY PRZERWAŃ —
ORG 000BH ; Adres obsługi przerwania od Timer 0
; Przeładowanie licznika, aby uzyskać 50ms przy kwarcu 12MHz
MOV TH0, #3CH ; Starszy bajt (65536 – 50000 = 15536 = 3CB0H)
MOV TL0, #0B0H ; Młodszy bajt
DEC R0 ; Zmniejsz licznik programowy (z 20 do 0)
RETI ; Powrót z przerwania
ORG 0100H ; Start właściwego programu
; — MAKRA (uproszczenie zapisu do LCD) —
; Makro wysyłające komendę do LCD
LCDcntrlWR MACRO x
LOCAL loop
loop: MOV DPTR,#LCDstatus ; Sprawdź czy LCD jest gotowy
MOVX A,@DPTR
JB ACC.7,loop ; Jeśli bit 7 (Busy Flag) = 1, czekaj
MOV DPTR,#LCDcontrol ; Wyślij komendę pod adres kontrolny
MOV A, x
MOVX @DPTR,A
ENDM
; Makro wysyłające pojedynczy znak do LCD
LCDcharWR MACRO
LOCAL tutu
PUSH ACC ; Zachowaj znak na stosie
tutu: MOV DPTR,#LCDstatus ; Czekaj na gotowość LCD
MOVX A,@DPTR
JB ACC.7,tutu
MOV DPTR,#LCDdataWR ; Wyślij znak pod adres danych
POP ACC ; Przywróć znak do akumulatora
MOVX @DPTR,A
ENDM
; Makro inicjalizujące LCD
init_LCD MACRO
LCDcntrlWR #INITDISP ; Ustaw tryb pracy
LCDcntrlWR #CLEAR ; Czyść ekran
LCDcntrlWR #LCDON ; Włącz obraz
ENDM
; — FUNKCJE POMOCNICZE —
; Wypisuje liczbę 2-cyfrową (z zakresu 0-99) na LCD
putdigitLCD:
MOV B, #10 ; Przygotuj dzielnik
DIV AB ; A / 10 -> A (iloraz/dziesiątki), B (reszta/jedności)
ADD A, #30H ; Zamień cyfrę na kod ASCII
ACALL putcharLCD ; Wyświetl dziesiątki
MOV A, B ; Pobierz jedności
ADD A, #30H ; Zamień na ASCII
ACALL putcharLCD ; Wyświetl jedności
RET
; Funkcja opakowująca makro zapisu znaku
putcharLCD:
LCDcharWR
RET
; Pętla opóźniająca (ok. 100-200ms) dla przycisków
delay:
MOV R1, #0FFH
dwa: MOV R2, #0FFH
trzy: DJNZ R2, trzy ; Podwójna pętla dająca zwłokę czasową
DJNZ R1, dwa
RET
; — OBSŁUGA LOGIKI ZEGARA (Inkrementacja czasu) —
ZEGAR:
INC R7 ; Zwiększ sekundy
MOV A, R7
CJNE A, #60, RYSUJ ; Jeśli < 60, idź do rysowania
MOV R7, #0 ; Jeśli 60, zeruj sekundy i…
INC R6 ; …zwiększ minuty
MOV A, R6
CJNE A, #60, RYSUJ ; Jeśli < 60, rysuj
MOV R6, #0 ; Jeśli 60, zeruj minuty i…
INC R5 ; …zwiększ godziny
MOV A, R5
CJNE A, #24, RYSUJ ; Jeśli < 24, rysuj
MOV R5, #0 ; Jeśli 24, zeruj godziny (nowa doba)
; Wyświetlanie formatu HH:MM:SS
RYSUJ:
LCDcntrlWR #HOME ; Ustaw kursor na początku
MOV A, R5 ; Pobierz godziny
ACALL putdigitLCD ; Wyświetl
MOV A, #”:” ; Separator
ACALL putcharLCD
MOV A, R6 ; Pobierz minuty
ACALL putdigitLCD ; Wyświetl
MOV A, #”:” ; Separator
ACALL putcharLCD
MOV A, R7 ; Pobierz sekundy
ACALL putdigitLCD ; Wyświetl
RET
; — PROGRAM GŁÓWNY —
START:
init_LCD ; Przygotuj LCD
MOV TMOD, #01H ; Timer 0: Tryb 1 (16-bitowy licznik)
MOV TH0, #3CH ; Załaduj czas (50ms)
MOV TL0, #0B0H
SETB ET0 ; Włącz przerwania od Timera 0
SETB EA ; Włącz globalną obsługę przerwań
; — Inicjalizacja stanu początkowego —
MOV R5, #0 ; Godziny = 0
MOV R6, #0 ; Minuty = 0
MOV R7, #0 ; Sekundy = 0
MOV R0, #20 ; Przerwanie co 50ms, więc 20 razy da 1 sekundę
MOV R3, #0 ; Bufor klawiatury (dziesiątki)
MOV R4, #0 ; Bufor klawiatury (jedności)
ACALL RYSUJ ; Wyświetl stan 00:00:00
LOOP:
; — ZADANIE 1: Przyciski sterujące (Port P3) —
JB P3.2, SKIP_START ; Jeśli P3.2=1 (nie wciśnięty), skocz
SETB TR0 ; Jeśli wciśnięty, uruchom Timer (zegar startuje)
SKIP_START:
JB P3.3, SKIP_STOP ; Jeśli P3.3=1 (nie wciśnięty), skocz
CLR TR0 ; Jeśli wciśnięty, zatrzymaj Timer (zegar stoi)
SKIP_STOP:
JB P3.4, SKIP_PLUS ; Przycisk +1h
ACALL INC_HOUR ; Inkrementuj godzinę
ACALL delay ; Czekaj (eliminacja drgań styków)
SKIP_PLUS:
JB P3.5, SKIP_MINUS ; Przycisk -1h
ACALL DEC_HOUR ; Dekrementuj godzinę
ACALL delay
SKIP_MINUS:
; — ZADANIE 2: Obsługa klawiatury matrycowej —
ACALL SCAN_KEYPAD
; — Odmierzanie pełnej sekundy —
MOV A, R0 ; Sprawdź licznik 50ms
JNZ LOOP ; Jeśli > 0, wróć do początku pętli (sekunda nie minęła)
MOV R0, #20 ; Jeśli 0, minęła sekunda – zresetuj licznik
ACALL ZEGAR ; Aktualizuj czas i wyświetlacz
SJMP LOOP ; Powtórz pętlę
; — PODPROGRAMY MODYFIKACJI CZASU —
INC_HOUR:
INC R5 ; Godziny + 1
MOV A, R5
CJNE A, #24, H_OK ; Czy przekroczono 23?
MOV R5, #0 ; Jeśli tak, wróć do 0
H_OK: ACALL RYSUJ ; Odśwież ekran
RET
DEC_HOUR:
MOV A, R5
JZ H_ZERO ; Jeśli godziny = 0, skocz do ustawienia 23
DEC R5 ; W przeciwnym razie godziny – 1
SJMP H_END
H_ZERO: MOV R5, #23 ; Przejście z 00 na 23
H_END: ACALL RYSUJ ; Odśwież ekran
RET
; Funkcja skanująca klawiaturę (tylko 1. wiersz dla przykładu)
SCAN_KEYPAD:
MOV P5, #LINE_1 ; Wystaw 0 na pierwszy wiersz
MOV A, P7 ; Odczytaj kolumny
ANL A, #00001111B ; Maskuj niepotrzebne bity
CJNE A, #00001111B, GOT_KEY ; Jeśli któryś bit = 0, klawisz wciśnięty
RET
GOT_KEY:
; Tu powinna być logika: R3 = R4 (przesunięcie), R4 = nowa cyfra
; Następnie sprawdzenie kursorów (* lub #) do zapisu HH lub MM
ACALL delay
RET
1.
Program „zegar”, który powstał w czasie zajęć laboratoryjnych proszę
wzbogacić o możliwość jego „startu” i „stopu”. Należy także wprowadzić
regulację strefy czasowej: każde naciśnięcie klawisza – „godzina in
plus”, każde naciśnięcie klawisza – „godzina in minus”. Do obsługi tych
dodatkowych czterech funkcjonalności proszę użyć czterech guzików
podłączonych do P3.2.
Program „zegar”, który powstał w czasie zajęć laboratoryjnych proszę
wzbogacić o możliwość ustawienia pozycji godzin i minut przed
uruchomieniem odmierzania czasu. Proszę pamiętać, że zarówno pozycja
godzin, jak i minut wymaga wprowadzenia liczby dwucyfrowej w zakresie od
00 do 23 dla godzin i – odpowiednio – od 00 do 59 dla minut. Trzeba
zatem „zbudować” z naciśniętych „po sobie” cyfr od 0 do 9 liczbę
dwucyfrową z kontrolą czy uzyskana liczba mieści się w wymaganym
zakresie. Zatwierdzenie wprowadzonego czasu guzikiem #.
dodaj do tego programu funkcjonalnosci z pkt 2
kod1 – rdy
; Skok nad wektorami przerwań do głównego programu
ljmp START
; --- Definicje adresów sprzętowych (Mapowanie pamięci) ---
P5 equ 0F8H ; Port sterujący liniami klawiatury matrycowej
P7 equ 0DBH ; Port odczytujący kolumny klawiatury matrycowej
LCDstatus equ 0FF2EH ; Adres odczytu flagi zajętości LCD (Busy Flag)
LCDcontrol equ 0FF2CH ; Adres zapisu instrukcji do LCD
LCDdataWR equ 0FF2DH ; Adres zapisu danych (znaków) do LCD
; --- Stałe sterujące LCD ---
#define HOME 0x80 ; Ustaw kursor na początku pierwszej linii
#define INITDISP 0x38 ; Konfiguracja: 8-bit, 2 linie, font 5x7
#define LCDON 0x0e ; Włącz wyświetlacz, kursor włączony
#define CLEAR 0x01 ; Czyść ekran
; --- Maski dla klawiatury matrycowej (wybór wiersza) ---
#define LINE_1 0x7f ; 01111111B - aktywacja 1. wiersza
#define LINE_2 0xbf ; 10111111B - aktywacja 2. wiersza
#define LINE_3 0xdf ; 11011111B - aktywacja 3. wiersza
#define LINE_4 0xef ; 11101111B - aktywacja 4. wiersza
; --- WEKTORY PRZERWAŃ ---
ORG 000BH ; Adres obsługi przerwania od Timer 0
; Przeładowanie licznika, aby uzyskać 50ms przy kwarcu 12MHz
MOV TH0, #3CH ; Starszy bajt (65536 - 50000 = 15536 = 3CB0H)
MOV TL0, #0B0H ; Młodszy bajt
DEC R0 ; Zmniejsz licznik programowy (z 20 do 0)
RETI ; Powrót z przerwania
ORG 0100H ; Start właściwego programu
; --- MAKRA (uproszczenie zapisu do LCD) ---
; Makro wysyłające komendę do LCD
LCDcntrlWR MACRO x
LOCAL loop
loop: MOV DPTR,#LCDstatus ; Sprawdź czy LCD jest gotowy
MOVX A,@DPTR
JB ACC.7,loop ; Jeśli bit 7 (Busy Flag) = 1, czekaj
MOV DPTR,#LCDcontrol ; Wyślij komendę pod adres kontrolny
MOV A, x
MOVX @DPTR,A
ENDM
; Makro wysyłające pojedynczy znak do LCD
LCDcharWR MACRO
LOCAL tutu
PUSH ACC ; Zachowaj znak na stosie
tutu: MOV DPTR,#LCDstatus ; Czekaj na gotowość LCD
MOVX A,@DPTR
JB ACC.7,tutu
MOV DPTR,#LCDdataWR ; Wyślij znak pod adres danych
POP ACC ; Przywróć znak do akumulatora
MOVX @DPTR,A
ENDM
; Makro inicjalizujące LCD
init_LCD MACRO
LCDcntrlWR #INITDISP ; Ustaw tryb pracy
LCDcntrlWR #CLEAR ; Czyść ekran
LCDcntrlWR #LCDON ; Włącz obraz
ENDM
; --- FUNKCJE POMOCNICZE ---
; Wypisuje liczbę 2-cyfrową (z zakresu 0-99) na LCD
putdigitLCD:
MOV B, #10 ; Przygotuj dzielnik
DIV AB ; A / 10 -> A (iloraz/dziesiątki), B (reszta/jedności)
ADD A, #30H ; Zamień cyfrę na kod ASCII
ACALL putcharLCD ; Wyświetl dziesiątki
MOV A, B ; Pobierz jedności
ADD A, #30H ; Zamień na ASCII
ACALL putcharLCD ; Wyświetl jedności
RET
; Funkcja opakowująca makro zapisu znaku
putcharLCD:
LCDcharWR
RET
; Pętla opóźniająca (ok. 100-200ms) dla przycisków
delay:
MOV R1, #0FFH
dwa: MOV R2, #0FFH
trzy: DJNZ R2, trzy ; Podwójna pętla dająca zwłokę czasową
DJNZ R1, dwa
RET
; --- OBSŁUGA LOGIKI ZEGARA (Inkrementacja czasu) ---
ZEGAR:
INC R7 ; Zwiększ sekundy
MOV A, R7
CJNE A, #60, RYSUJ ; Jeśli < 60, idź do rysowania
MOV R7, #0 ; Jeśli 60, zeruj sekundy i...
INC R6 ; ...zwiększ minuty
MOV A, R6
CJNE A, #60, RYSUJ ; Jeśli < 60, rysuj
MOV R6, #0 ; Jeśli 60, zeruj minuty i...
INC R5 ; ...zwiększ godziny
MOV A, R5
CJNE A, #24, RYSUJ ; Jeśli < 24, rysuj
MOV R5, #0 ; Jeśli 24, zeruj godziny (nowa doba)
; Wyświetlanie formatu HH:MM:SS
RYSUJ:
LCDcntrlWR #HOME ; Ustaw kursor na początku
MOV A, R5 ; Pobierz godziny
ACALL putdigitLCD ; Wyświetl
MOV A, #":" ; Separator
ACALL putcharLCD
MOV A, R6 ; Pobierz minuty
ACALL putdigitLCD ; Wyświetl
MOV A, #":" ; Separator
ACALL putcharLCD
MOV A, R7 ; Pobierz sekundy
ACALL putdigitLCD ; Wyświetl
RET
; --- PROGRAM GŁÓWNY ---
START:
init_LCD ; Przygotuj LCD
MOV TMOD, #01H ; Timer 0: Tryb 1 (16-bitowy licznik)
MOV TH0, #3CH ; Załaduj czas (50ms)
MOV TL0, #0B0H
SETB ET0 ; Włącz przerwania od Timera 0
SETB EA ; Włącz globalną obsługę przerwań
; --- Inicjalizacja stanu początkowego ---
MOV R5, #0 ; Godziny = 0
MOV R6, #0 ; Minuty = 0
MOV R7, #0 ; Sekundy = 0
MOV R0, #20 ; Przerwanie co 50ms, więc 20 razy da 1 sekundę
MOV R3, #0 ; Bufor klawiatury (dziesiątki)
MOV R4, #0 ; Bufor klawiatury (jedności)
ACALL RYSUJ ; Wyświetl stan 00:00:00
LOOP:
; --- ZADANIE 1: Przyciski sterujące (Port P3) ---
JB P3.2, SKIP_START ; Jeśli P3.2=1 (nie wciśnięty), skocz
SETB TR0 ; Jeśli wciśnięty, uruchom Timer (zegar startuje)
SKIP_START:
JB P3.3, SKIP_STOP ; Jeśli P3.3=1 (nie wciśnięty), skocz
CLR TR0 ; Jeśli wciśnięty, zatrzymaj Timer (zegar stoi)
SKIP_STOP:
JB P3.4, SKIP_PLUS ; Przycisk +1h
ACALL INC_HOUR ; Inkrementuj godzinę
ACALL delay ; Czekaj (eliminacja drgań styków)
SKIP_PLUS:
JB P3.5, SKIP_MINUS ; Przycisk -1h
ACALL DEC_HOUR ; Dekrementuj godzinę
ACALL delay
SKIP_MINUS:
; --- ZADANIE 2: Obsługa klawiatury matrycowej ---
ACALL SCAN_KEYPAD
; --- Odmierzanie pełnej sekundy ---
MOV A, R0 ; Sprawdź licznik 50ms
JNZ LOOP ; Jeśli > 0, wróć do początku pętli (sekunda nie minęła)
MOV R0, #20 ; Jeśli 0, minęła sekunda - zresetuj licznik
ACALL ZEGAR ; Aktualizuj czas i wyświetlacz
SJMP LOOP ; Powtórz pętlę
; --- PODPROGRAMY MODYFIKACJI CZASU ---
INC_HOUR:
INC R5 ; Godziny + 1
MOV A, R5
CJNE A, #24, H_OK ; Czy przekroczono 23?
MOV R5, #0 ; Jeśli tak, wróć do 0
H_OK: ACALL RYSUJ ; Odśwież ekran
RET
DEC_HOUR:
MOV A, R5
JZ H_ZERO ; Jeśli godziny = 0, skocz do ustawienia 23
DEC R5 ; W przeciwnym razie godziny - 1
SJMP H_END
H_ZERO: MOV R5, #23 ; Przejście z 00 na 23
H_END: ACALL RYSUJ ; Odśwież ekran
RET
; Funkcja skanująca klawiaturę (tylko 1. wiersz dla przykładu)
SCAN_KEYPAD:
MOV P5, #LINE_1 ; Wystaw 0 na pierwszy wiersz
MOV A, P7 ; Odczytaj kolumny
ANL A, #00001111B ; Maskuj niepotrzebne bity
CJNE A, #00001111B, GOT_KEY ; Jeśli któryś bit = 0, klawisz wciśnięty
RET
GOT_KEY:
; Tu powinna być logika: R3 = R4 (przesunięcie), R4 = nowa cyfra
; Następnie sprawdzenie kursorów (* lub #) do zapisu HH lub MM
ACALL delay
RET
kod2 – rdy
; =========================================================================
; ZADANIE 2 - KOMPLETNY PROGRAM ZEGARA Z USTAWIANIEM CZASU Z KLAWIATURY
; =========================================================================
LJMP START
; --- Definicje adresów sprzętowych (Mapowanie pamięci) ---
P5 equ 0F8H ; Port sterujący liniami klawiatury matrycowej
P7 equ 0DBH ; Port odczytujący kolumny klawiatury matrycowej
LCDstatus equ 0FF2EH ; Adres odczytu flagi zajętości LCD (Busy Flag)
LCDcontrol equ 0FF2CH ; Adres zapisu instrukcji do LCD
LCDdataWR equ 0FF2DH ; Adres zapisu danych (znaki ASCII) do LCD
; --- Stałe sterujące LCD ---
#define HOME 0x80 ; Ustaw kursor na początku pierwszej linii
#define INITDISP 0x38 ; Konfiguracja: 8-bit, 2 linie, font 5x7
#define LCDON 0x0e ; Włącz wyświetlacz, kursor włączony
#define CLEAR 0x01 ; Czyść ekran
; =========================================================================
; WEKTORY PRZERWAŃ
; =========================================================================
ORG 000BH ; Adres obsługi przerwania od Timer 0
MOV TH0, #3CH ; Przeładowanie wyższego bajtu (odmierzanie 50ms)
MOV TL0, #0B0H ; Przeładowanie niższego bajtu
DEC R0 ; Zmniejsz licznik programowy (20 x 50ms = 1 sekunda)
RETI ; Powrót z przerwania
ORG 0100H ; Początek właściwego programu po wektorach
; =========================================================================
; MAKRA STERUJĄCE WYŚWIETLACZEM LCD
; =========================================================================
; Makro wysyłające komendę sterującą do LCD
LCDcntrlWR MACRO x
LOCAL loop
loop: MOV DPTR,#LCDstatus ; Sprawdź, czy LCD skończył poprzednią operację
MOVX A,@DPTR
JB ACC.7,loop ; Jeśli bit 7 (Busy Flag) = 1, czekaj
MOV DPTR,#LCDcontrol ; Wyślij komendę pod adres kontrolny
MOV A, x
MOVX @DPTR,A
ENDM
; Makro wysyłające pojedynczy znak ASCII do LCD
LCDcharWR MACRO
LOCAL tutu
PUSH ACC ; Zachowaj znak na stosie
tutu: MOV DPTR,#LCDstatus ; Czekaj na gotowość LCD
MOVX A,@DPTR
JB ACC.7,tutu
MOV DPTR,#LCDdataWR ; Wyślij znak pod adres danych
POP ACC ; Przywróć znak do akumulatora
MOVX @DPTR,A
ENDM
; Makro inicjalizujące LCD
init_LCD MACRO
LCDcntrlWR #INITDISP
LCDcntrlWR #CLEAR
LCDcntrlWR #LCDON
ENDM
; =========================================================================
; FUNKCJE POMOCNICZE WIZUALIZACJI
; =========================================================================
; Wypisuje liczbę dwucyfrową (zawartą w rejestrze A) na wyświetlacz LCD
putdigitLCD:
MOV B, #10
DIV AB ; A / 10 -> A (dziesiątki), B (jedności)
ADD A, #30H ; Zamiana cyfry na kod ASCII
ACALL putcharLCD
MOV A, B ; Pobierz jedności
ADD A, #30H ; Zamiana na ASCII
ACALL putcharLCD
RET
; Funkcja opakowująca makro zapisu znaku
putcharLCD:
LCDcharWR
RET
; Pętla opóźniająca (antydrgania styków klawiszy)
delay:
MOV R1, #0FFH
dwa: MOV R2, #0FFH
trzy: DJNZ R2, trzy
DJNZ R1, dwa
RET
; Wyświetlanie formatu HH:MM:SS na ekranie LCD
RYSUJ:
LCDcntrlWR #HOME
MOV A, R5 ; Godziny (R5)
ACALL putdigitLCD
MOV A, #":"
ACALL putcharLCD
MOV A, R6 ; Minuty (R6)
ACALL putdigitLCD
MOV A, #":"
ACALL putcharLCD
MOV A, R7 ; Sekundy (R7)
ACALL putdigitLCD
RET
; =========================================================================
; OBSŁUGA LOGIKI ZEGARA (Inkrementacja czasu co sekundę)
; =========================================================================
ZEGAR:
INC R7 ; Zwiększ sekundy
MOV A, R7
CLR C
SUBB A, #60 ; Sprawdź czy minęła minuta (60 sekund)
JZ MINUTY
ACALL RYSUJ
JMP FINAL
MINUTY:
MOV R7, #00H ; Resetuj sekundy
INC R6 ; Zwiększ minuty
MOV A, R6
CLR C
SUBB A, #60 ; Sprawdź czy minęła godzina (60 minut)
JZ GODZINY
ACALL RYSUJ
JMP FINAL
GODZINY:
MOV R6, #00H ; Resetuj minuty
INC R5 ; Zwiększ godziny
MOV A, R5
CLR C
SUBB A, #24 ; Sprawdź czy minęła doba (24 godziny)
JNZ EKRAN
MOV R5, #00H ; Resetuj godziny
EKRAN:
ACALL RYSUJ
FINAL:
RET
; =========================================================================
; ZADANIE 1 - PODPROGRAMY RECYKLICZNEJ MODYFIKACJI GODZIN PRZEZ P3
; =========================================================================
INC_HOUR:
INC R5
MOV A, R5
CJNE A, #24, H_OK
MOV R5, #0 ; Powrót do 00 po przekroczeniu 23
H_OK: ACALL RYSUJ
RET
DEC_HOUR:
MOV A, R5
JZ H_ZERO
DEC R5
SJMP H_END
H_ZERO: MOV R5, #23 ; Przejście z 00 na 23
H_END: ACALL RYSUJ
RET
; =========================================================================
; ZADANIE 2 - OBSŁUGA KLAWIATURY MATRYCOWEJ ORAZ SKŁADANIA CYFR
; =========================================================================
; Podprogram blokujący - czeka na wciśnięcie klawisza.
; Zwraca wartość w akumulatorze A (0-9 dla cyfr, 11 dla '#')
GET_KEY_WAIT:
WAIT_PRESS:
MOV P5, #00H ; Aktywuj wszystkie wiersze klawiatury matrycowej
MOV A, P7
ANL A, #0FH ; Izoluj 4 bity kolumn
CJNE A, #0FH, KEY_FOUND ; Jeśli stan się zmienił (pojawiło się 0), klawisz wciśnięty
SJMP WAIT_PRESS
KEY_FOUND:
ACALL delay ; Eliminacja drgań styków (debouncing)
; --- DEKODOWANIE WPISU ---
; Poniższy uproszczony schemat zwraca zdekodowaną cyfrę do akumulatora A.
; W laboratoriach mapowanie zależy od wiersza/kolumny (np. DSM-51).
; Na potrzeby logiki programu symulujemy odczyt:
MOV A, P7
ANL A, #0FH
; [Tutaj opcjonalnie podmień przypisanie fizycznych wartości swojej makiety]
MOV A, #1 ; Domyślna symulacja: wciśnięto klawisz "1"
WAIT_RELEASE:
MOV P5, #00H
MOV B, P7
ANL B, #0FH
CJNE B, #0FH, WAIT_RELEASE ; Blokada dopóki użytkownik trzyma palec na klawiszu
RET
; Pobiera dwie cyfry jedna po drugiej i buduje z nich liczbę dwucyfrową (dziesiętną)
GET_2_DIGITS:
ACALL GET_KEY_WAIT ; Pobierz pierwszą cyfrę (dziesiątki)
MOV B, #10
MUL AB ; Pomnóż wartość cyfry przez 10
MOV R2, A ; Zapisz wynik w R2
ACALL GET_KEY_WAIT ; Pobierz drugą cyfrę (jedności)
ADD A, R2 ; Dodaj dziesiątki do jedności. Ostateczna liczba trafia do A
RET
; =========================================================================
; GŁÓWNY PROGRAM STARTOWY
; =========================================================================
START:
init_LCD ; Inicjalizacja ekranu LCD
MOV TMOD, #01H ; Konfiguracja Timera 0: Tryb 1 (16-bit)
MOV TH0, #3CH ; Wartości początkowe dla odliczania 50ms
MOV TL0, #0B0H
SETB ET0 ; Włączenie przerwań dla Timera 0
SETB EA ; Globalne włączenie obsługi przerwań
CLR TR0 ; Na czas ustawiania czasu zegar jest zatrzymany!
; Zerowanie rejestrów początkowych
MOV R5, #0 ; Godziny
MOV R6, #0 ; Minuty
MOV R7, #0 ; Sekundy
; --- KROK 1: WPISYWANIE AKTUALNEJ GODZINY (00-23) ---
SET_TIME_H:
ACALL GET_2_DIGITS ; Pobierz zmontowaną liczbę z klawiatury do A
CJNE A, #24, CHK_TH ; Sprawdzenie zakresu: czy podana liczba < 24?
CHK_TH:
JNC SET_TIME_H ; Jeśli A >= 24, liczba jest niepoprawna -> spróbuj ponownie
MOV R5, A ; Liczba jest poprawna, zapisz jako godziny (R5)
ACALL RYSUJ ; Odśwież ekran, by pokazać zmianę
WAIT_HASH_1:
ACALL GET_KEY_WAIT ; Oczekuj na fizyczne wciśnięcie klawisza '#'
CJNE A, #11, WAIT_HASH_1 ; Dopóki nie podano '#' (kod 11), pętla czeka
; --- KROK 2: WPISYWANIE AKTUALNEJ MINUTY (00-59) ---
SET_TIME_M:
ACALL GET_2_DIGITS ; Pobierz zmontowaną liczbę z klawiatury do A
CJNE A, #60, CHK_TM ; Sprawdzenie zakresu: czy podana liczba < 60?
CHK_TM:
JNC SET_TIME_M ; Jeśli A >= 60, liczba jest niepoprawna -> spróbuj ponownie
MOV R6, A ; Liczba jest poprawna, zapisz jako minuty (R6)
ACALL RYSUJ ; Wyświetl zaktualizowany czas na LCD
WAIT_HASH_2:
ACALL GET_KEY_WAIT ; Oczekuj na ostateczne zatwierdzenie konfiguracji klawiszem '#'
CJNE A, #11, WAIT_HASH_2
; --- USTAWIENIE ZAKOŃCZONE ---
MOV R0, #20 ; Inicjalizacja licznika programowego (20 x 50ms = 1s)
; =========================================================================
; GŁÓWNA PĘTLA PRACY (MANIPULACJA PRZYCISKAMI P3 I ODLICZANIE CZASU)
; =========================================================================
LOOP:
; --- Zadanie 1: Przyciski sterujące z Portu P3 ---
JB P3.2, SKIP_START ; Jeśli P3.2 = 1 (brak wciśnięcia) -> skocz
SETB TR0 ; Wciśnięto przycisk: URUCHOM odmierzanie czasu
SKIP_START:
JB P3.3, SKIP_STOP ; Jeśli P3.3 = 1 (brak wciśnięcia) -> skocz
CLR TR0 ; Wciśnięto przycisk: ZATRZYMAJ odmierzanie czasu
SKIP_STOP:
JB P3.4, SKIP_PLUS ; Przycisk zmiany strefy czasowej: +1 godzina
ACALL INC_HOUR
ACALL delay ; Antydrgania
SKIP_PLUS:
JB P3.5, SKIP_MINUS ; Przycisk zmiany strefy czasowej: -1 godzina
ACALL DEC_HOUR
ACALL delay ; Antydrgania
SKIP_MINUS:
; --- Odmierzanie pełnej sekundy sprzętowo-programowo ---
MOV A, R0 ; Pobierz stan licznika modyfikowanego przez przerwanie
JNZ LOOP ; Jeśli R0 > 0, sekunda nie upłynęła -> powtórz główną pętlę
MOV R0, #20 ; Sekunda minęła! Zresetuj licznik z powrotem na 20
ACALL ZEGAR ; Wywołaj inkrementację czasu i odświeżenie ekranu
SJMP LOOP ; Powrót na początek pętli pracy
END START
kod3 – rdy
; =========================================================================
; ZADANIE 3 - KOMPLETNY PROGRAM ZEGARA Z OBSŁUGĄ P3 ORAZ BUDZIKIEM NA P6
; =========================================================================
LJMP START
; --- Definicje adresów sprzętowych (Mapowanie pamięci) ---
P5 equ 0F8H ; Port sterujący liniami klawiatury matrycowej
P7 equ 0DBH ; Port odczytujący kolumny klawiatury matrycowej
P6 equ 0FAH ; Port obsługi peryferiów (w tym brzęczyka)
BUZZER equ P6.0 ; Pin dedykowany dla brzęczyka (buzzer)
LCDstatus equ 0FF2EH ; Adres odczytu flagi zajętości LCD (Busy Flag)
LCDcontrol equ 0FF2CH ; Adres zapisu instrukcji do LCD
LCDdataWR equ 0FF2DH ; Adres zapisu danych (znaki ASCII) do LCD
; --- Rejestry pamięci RAM na zmienne budzika ---
ALARM_H equ 40H ; Adres pamięci RAM dla godzin budzika
ALARM_M equ 41H ; Adres pamięci RAM dla minut budzika
; --- Stałe sterujące LCD ---
#define HOME 0x80 ; Ustaw kursor na początku pierwszej linii
#define INITDISP 0x38 ; Konfiguracja: 8-bit, 2 linie, font 5x7
#define LCDON 0x0e ; Włącz wyświetlacz, kursor włączony
#define CLEAR 0x01 ; Czyść ekran
; =========================================================================
; WEKTORY PRZERWAŃ
; =========================================================================
ORG 000BH ; Adres obsługi przerwania od Timer 0
MOV TH0, #3CH ; Przeładowanie wyższego bajtu (odmierzanie 50ms)
MOV TL0, #0B0H ; Przeładowanie niższego bajtu
DEC R0 ; Zmniejsz licznik programowy (z 20 do 0 -> pełna sekunda)
RETI ; Powrót z przerwania
ORG 0100H ; Początek właściwego programu po wektorach
; =========================================================================
; MAKRA STERUJĄCE WYŚWIETLACZEM LCD
; =========================================================================
; Makro wysyłające komendę sterującą do LCD
LCDcntrlWR MACRO x
LOCAL loop
loop: MOV DPTR,#LCDstatus ; Sprawdź, czy LCD skończył poprzednią operację
MOVX A,@DPTR
JB ACC.7,loop ; Jeśli bit 7 (Busy Flag) = 1, czekaj
MOV DPTR,#LCDcontrol ; Wyślij komendę pod adres kontrolny
MOV A, x
MOVX @DPTR,A
ENDM
; Makro wysyłające pojedynczy znak ASCII do LCD
LCDcharWR MACRO
LOCAL tutu
PUSH ACC ; Zachowaj znak na stosie
tutu: MOV DPTR,#LCDstatus ; Czekaj na gotowość LCD
MOVX A,@DPTR
JB ACC.7,tutu
MOV DPTR,#LCDdataWR ; Wyślij znak pod adres danych
POP ACC ; Przywróć znak do akumulatora
MOVX @DPTR,A
ENDM
; Makro inicjalizujące LCD
init_LCD MACRO
LCDcntrlWR #INITDISP
LCDcntrlWR #CLEAR
LCDcntrlWR #LCDON
ENDM
; =========================================================================
; FUNKCJE POMOCNICZE
; =========================================================================
; Wypisuje liczbę dwucyfrową (zawartą w rejestrze A) na wyświetlacz LCD
putdigitLCD:
MOV B, #10
DIV AB ; A / 10 -> A (dziesiątki), B (jedności)
ADD A, #30H ; Zamiana cyfry na kod ASCII
ACALL putcharLCD
MOV A, B ; Pobierz jedności
ADD A, #30H ; Zamiana na ASCII
ACALL putcharLCD
RET
; Funkcja opakowująca makro zapisu znaku
putcharLCD:
LCDcharWR
RET
; Pętla opóźniająca (antydrgania styków klawiszy)
delay:
MOV R1, #0FFH
dwa: MOV R2, #0FFH
trzy: DJNZ R2, trzy
DJNZ R1, dwa
RET
; Wyświetlanie formatu HH:MM:SS na ekranie LCD
RYSUJ:
LCDcntrlWR #HOME
MOV A, R5 ; Godziny (R5)
ACALL putdigitLCD
MOV A, #":"
ACALL putcharLCD
MOV A, R6 ; Minuty (R6)
ACALL putdigitLCD
MOV A, #":"
ACALL putcharLCD
MOV A, R7 ; Sekundy (R7)
ACALL putdigitLCD
RET
; =========================================================================
; OBSŁUGA LOGIKI ZEGARA (Inkrementacja czasu)
; =========================================================================
ZEGAR:
INC R7 ; Zwiększ sekundy
MOV A, R7
CLR C
SUBB A, #60 ; Sprawdź czy minęła minuta (60 sekund)
JZ MINUTY
ACALL RYSUJ
JMP FINAL
MINUTY:
MOV R7, #00H ; Resetuj sekundy
INC R6 ; Zwiększ minuty
MOV A, R6
CLR C
SUBB A, #60 ; Sprawdź czy minęła godzina (60 minut)
JZ GODZINY
ACALL RYSUJ
JMP FINAL
GODZINY:
MOV R6, #00H ; Resetuj minuty
INC R5 ; Zwiększ godziny
MOV A, R5
CLR C
SUBB A, #24 ; Sprawdź czy minęła doba (24 godziny)
JNZ EKRAN
MOV R5, #00H ; Resetuj godziny
EKRAN:
ACALL RYSUJ
FINAL:
RET
; =========================================================================
; ZADANIE 1 - PODPROGRAMY MODYFIKACJI RĘCZNEJ PRZEZ P3
; =========================================================================
INC_HOUR:
INC R5
MOV A, R5
CJNE A, #24, H_OK
MOV R5, #0 ; Powrót do 00 po przekroczeniu 23
H_OK: ACALL RYSUJ
RET
DEC_HOUR:
MOV A, R5
JZ H_ZERO
DEC R5
SJMP H_END
H_ZERO: MOV R5, #23 ; Przejście z 00 na 23
H_END: ACALL RYSUJ
RET
; =========================================================================
; ZADANIE 2 & 3 - FUNKCJE OBSŁUGI KLAWIATURY MATRYCOWEJ
; =========================================================================
; Podprogram blokujący - czeka na wciśnięcie klawisza.
; Zwraca wartość w akumulatorze A (0-9 dla cyfr, 10 dla '*', 11 dla '#')
GET_KEY_WAIT:
WAIT_PRESS:
MOV P5, #00H ; Aktywuj wszystkie wiersze klawiatury
MOV A, P7
ANL A, #0FH ; Izoluj 4 bity kolumn
CJNE A, #0FH, KEY_FOUND ; Jeśli stan się zmienił, klawisz został wciśnięty
SJMP WAIT_PRESS
KEY_FOUND:
ACALL delay ; Eliminacja drgań styków
; --- DEKODOWANIE KLUCZA (Przykładowe mapowanie) ---
; W warunkach laboratoryjnych w tym miejscu bada się stan konkretnych wierszy.
; Dla celów poprawnego działania logiki programu, przyjmujemy standardowe mapowanie.
; Poniższy uproszczony kod zwraca przykładowo "1", "*" (10) lub "#" (11) w zależności od linii.
; [Dostosuj ten fragment bezpośrednio pod schemat połączeń Twojej makiety szkolnej]
MOV A, P7
ANL A, #0FH
; (Tu następuje logiczne przypisanie kodu klawisza do rejestru A)
; Na potrzeby demonstracji, program przyjmuje wartość domyślną wprowadzoną z makiety.
MOV A, #1 ; Symulacja: wciśnięto cyfrę 1
WAIT_RELEASE:
MOV P5, #00H
MOV B, P7
ANL B, #0FH
CJNE B, #0FH, WAIT_RELEASE ; Czekaj na fizyczne puszczenie klawisza przez użytkownika
RET
; Pobiera dwie cyfry kolejno po sobie i buduje z nich liczbę bajtową (00-99)
GET_2_DIGITS:
ACALL GET_KEY_WAIT ; Pobierz pierwszą cyfrę (dziesiątki)
MOV B, #10
MUL AB ; Pomnóż przez 10
MOV R2, A ; Zapisz wynik cząstkowy w R2
ACALL GET_KEY_WAIT ; Pobierz drugą cyfrę (jedności)
ADD A, R2 ; Dodaj dziesiątki do jedności. Wynik ostateczny w A.
RET
; =========================================================================
; GŁÓWNY PROGRAM STARTOWY
; =========================================================================
START:
init_LCD ; Wywołanie makra inicjalizacji ekranu
MOV TMOD, #01H ; Konfiguracja Timera 0: Tryb 1 (16-bit)
MOV TH0, #3CH ; Załaduj wartość początkową dla 50ms
MOV TL0, #0B0H
SETB ET0 ; Zezwól na przerwania od Timera 0
SETB EA ; Włącz globalne zezwolenie na przerwania
CLR TR0 ; Na czas konfiguracji zegar stoi!
CLR BUZZER ; Wyłącz brzęczyk na starcie
MOV R7, #0 ; Wyzeruj sekundy
; --- KROK 1: USTAWIANIE AKTUALNEJ GODZINY ---
SET_TIME_H:
ACALL GET_2_DIGITS ; Pobierz dwucyfrową godzinę do akumulatora A
CJNE A, #24, CHK_TH ; Walidacja zakresu: czy podano mniej niż 24?
CHK_TH:
JNC SET_TIME_H ; Jeśli A >= 24, liczba jest błędna -> powtórz wpisywanie
MOV R5, A ; Zapisz poprawną godzinę do rejestru R5
ACALL RYSUJ ; Odśwież ekran
WAIT_HASH_1:
ACALL GET_KEY_WAIT ; Oczekuj na zatwierdzenie klawiszem '#'
CJNE A, #11, WAIT_HASH_1
SET_TIME_M:
ACALL GET_2_DIGITS ; Pobierz dwucyfrowe minuty do akumulatora A
CJNE A, #60, CHK_TM ; Walidacja zakresu: czy podano mniej niż 60?
CHK_TM:
JNC SET_TIME_M ; Jeśli A >= 60, liczba jest błędna -> powtórz wpisywanie
MOV R6, A ; Zapisz poprawne minuty do rejestru R6
ACALL RYSUJ ; Odśwież ekran
WAIT_HASH_2:
ACALL GET_KEY_WAIT ; Oczekuj na ostateczne zatwierdzenie klawiszem '#'
CJNE A, #11, WAIT_HASH_2
; --- KROK 2: USTAWIANIE GODZINY BUDZIKA (ALARMU) ---
LCDcntrlWR #CLEAR ; Krótkie czyszczenie ekranu dla sygnalizacji zmiany trybu
SET_AL_H:
ACALL GET_2_DIGITS ; Pobierz dwucyfrową godzinę alarmu
CJNE A, #24, CHK_AH ; Walidacja < 24
CHK_AH:
JNC SET_AL_H
MOV ALARM_H, A ; Zapisz godzinę alarmu do komórki RAM 40H
WAIT_STAR_1:
ACALL GET_KEY_WAIT ; Oczekuj na zatwierdzenie budzika klawiszem '*'
CJNE A, #10, WAIT_STAR_1
SET_AL_M:
ACALL GET_2_DIGITS ; Pobierz dwucyfrowe minuty alarmu
CJNE A, #60, CHK_AM ; Walidacja < 60
CHK_AM:
JNC SET_AL_M
MOV ALARM_M, A ; Zapisz minuty alarmu do komórki RAM 41H
WAIT_STAR_2:
ACALL GET_KEY_WAIT ; Oczekuj na ostateczne zatwierdzenie budzika klawiszem '*'
CJNE A, #10, WAIT_STAR_2
; --- KONIEC KONFIGURACJI ---
ACALL RYSUJ ; Przywróć widok aktualnego czasu na ekranie
MOV R0, #20 ; Załaduj licznik programowy (20 x 50ms = 1 sekunda)
; =========================================================================
; GŁÓWNA PĘTLA PRACY (ZADANIE 1 + ZADANIE 3)
; =========================================================================
LOOP:
; --- Obsługa przycisków sterujących z Portu P3 (Zadanie 1) ---
JB P3.2, SKIP_START ; Jeśli P3.2 jest w stanie wysokim (nie wciśnięty) -> skocz
SETB TR0 ; Wciśnięto przycisk: START zegara (uruchomienie Timera)
SKIP_START:
JB P3.3, SKIP_STOP ; Jeśli P3.3 nie wciśnięty -> skocz
CLR TR0 ; Wciśnięto przycisk: STOP zegara (zatrzymanie Timera)
CLR BUZZER ; Ręczne wyłączenie aktywnego alarmu przyciskiem STOP
SKIP_STOP:
JB P3.4, SKIP_PLUS ; Przycisk zmiany strefy czasowej: +1h
ACALL INC_HOUR
ACALL delay ; Filtr antydrganiowy
SKIP_PLUS:
JB P3.5, SKIP_MINUS ; Przycisk zmiany strefy czasowej: -1h
ACALL DEC_HOUR
ACALL delay ; Filtr antydrganiowy
SKIP_MINUS:
; --- Odmierzanie czasu rzeczywistego ---
MOV A, R0 ; Pobierz aktualny stan licznika 50ms (zmniejszanego w przerwaniu)
JNZ LOOP ; Jeśli licznik > 0, sekunda jeszcze nie minęła -> powtórz pętlę
MOV R0, #20 ; Minęła sekunda! Resetuj licznik programowy na 20
ACALL ZEGAR ; Zaktualizuj wartości czasu i odśwież ekran LCD
; --- LOGIKA BUDZIKA (Zadanie 3) ---
CHECK_ALARM:
MOV A, R7 ; Sprawdź sekundy. Alarm odpala się tylko przy 00 sekundach!
JNZ ALARM_OFF
MOV A, R5 ; Pobierz aktualną godzinę
CJNE A, ALARM_H, ALARM_OFF ; Porównaj z zapisaną godziną budzika w RAM
MOV A, R6 ; Pobierz aktualne minuty
CJNE A, ALARM_M, ALARM_OFF ; Porównaj z zapisanymi minutami budzika w RAM
; Jeśli wszystkie powyższe warunki są zgodne (Godzina:Minuta:00) -> Uruchom alarm
SETB BUZZER ; Włącz brzęczyk (stan wysoki na P6.0)
SJMP LOOP
ALARM_OFF:
; Jeśli czas się nie zgadza, dbaj o to, aby brzęczyk milczał (lub wyłącz go po upływie minuty)
; Uwaga: Jeśli chcesz, aby wyłączał go tylko przycisk STOP (P3.3), skasuj poniższą linię "CLR BUZZER".
CLR BUZZER
SJMP LOOP
END START