rnd6_00.jpg
No to jest klawiaturaCo prawda, to klawiatura jest bez klawiatury – dokładniej bez matrycy klawiatury.
rnd6_01.png
Do „obróbki” klawiatury jest dedykowany port B i połowa portu C z układu 8255. Realizacja jest matrycowa. No i nie obyło się bez pomyłek (no jakieś zaćmienie chwilowe zaistniało). Sam napisałem w komentarzu do górnej połówki portu C, że jest output jak i również port B jest input. No to jest pytanie: dlaczego jest drabinka rezystorów w porcie C zamiast być przy porcie B?
Istniejąca drabinka R503 jest zbędna, ale nie będę jej wylutowywać, w końcu 10 kΩ to żadne obciążenie dla wyjść portu. Natomiast jest ona niezbędna w porcie B. Jak żaden przycisk w matrycy nie jest naciśnięty, to port jest niewysterowany i statystycznie chętniej czyta zera niż jedynki. Musiałem dodać ośmiobitową drabinkę.
rnd6_02.jpg
Dodanie drabinki znacząco pomogło. Daje się wczytywać klawisze z matrycy.
Co prawda można by było zamienić całość stronami: port B zrobić jako wyjściowy a port C jako wejściowy (już ma drabinkę). Rozważałem taka kombinację, ale… zrobiłem tak, że port C jest wyjściem. Uzasadnienie jest raczej proste. By oblecieć całą matrycę, to obecnie potrzebuję czerech cykli (kombinacji) na wyjściach do matrycy klawiatury i czterech cykli odczytu portu B (bo po osiem bitów) i jest obleciana cała klawiatura. Inaczej potrzebnych by było 8 cykli z odczytem stanu, gdzie należy brać pod uwagę jedynie 4 bity. Tak jest szybciej.
A w kwestii szybkości, to na razie nie zmieniałem nastaw do częstotliwości generacji przerwań od upływu czasu. Obecnie jest zbyt wolno. Podłączyłem sobie analizator logiczny do wyjść portu C i mamy:
rnd6_03.png
Czasowo to wygląda następująco: 2,5 Hz, to zdecydowanie za mało, może zaistnieć konieczność długiego przytrzymania przycisku, by procek to zatrybił. To jest do korekty.
rnd6_04.png
Klawiatura jest obrabiana w przerwaniach od upływu czasu.
Kod: Zaznacz cały
RST75Service ;ISR RST75Service ;
;begin
push psw ;
push b ;
push d ;
push h ;
call KeybService ; KeybService ;
mvi a , Tim1IrqGenMode ;
out Timer8253 + Port8253.Control
mvi a , lo ( Timer1Prescaler ) ;
out Timer8253 + Port8253.TimerB;
mvi a , hi ( Timer1Prescaler ) ;
out Timer8253 + Port8253.TimerB;
pop h ;
pop d ;
pop b ;
pop psw ;
ei ;
ret ;end ;
Zrealizowana jest w konwencji automatu stanów.
Kod: Zaznacz cały
KeybService ;procedure KeybService ;
;begin
; case KbdModInstance.KbdState of
lda KbdModInstance + KbdModInstanceT.KbdState
cpi KbdIdle ; KbdIdle :
jnz KyebSrv_2 ; begin
call AnyKeyActive ; if AnyKeyActive ( ) then
cpi 0 ;
jz KyebSrv_12 ; begin
mvi a , KbdWaitOnPress ; KbdModInstance.KbdState := KbdWaitOnPress ;
sta KbdModInstance + KbdModInstanceT.KbdState
mvi a , KbdDelayLimit ; KbdModInstance.KbdDelayCt := KbdDelayLimit ;
sta KbdModInstance + KbdModInstanceT.KbdDelayCt
jmp KyebSrv_13 ; end
KyebSrv_12 ; else
; begin
; KbdModInstance.RowCounter := KbdModInstance.RowCounter + 1 ;
lxi h , KbdModInstance + KbdModInstanceT.RowCounter
inr m ;
mov a , m ;
cpi RowCounterLmt ; if KbdModInstance.RowCounter = RowCounterLmt then
jnz KyebSrv_14 ;
mvi a , 0 ; KbdModInstance.RowCounter := 0 ;
mov m , a ;
KyebSrv_14 ;
; if KbdModInstance.HardwKbdSetRowSrv <> nil then
lxi d , KbdModInstance + KbdModInstanceT.HardwKbdSetRowSrv
lhlx ;
mov a , h ;
ora l ;
jz KyebSrv_15 ;
; KbdModInstance.HardwKbdSetRowSrv ( KbdModInstance.RowCounter ) ;
lda KbdModInstance + KbdModInstanceT.RowCounter
call CallHLService ;
KyebSrv_15 ;
KyebSrv_13 ; end ;
jmp KyebSrv_0 ; end ;
KyebSrv_2 ;
cpi KbdWaitOnPress ; KbdWaitOnPress :
jnz KyebSrv_3 ; begin
; KbdModInstance.KbdDelayCt := KbdModInstance.KbdDelayCt - 1 ;
lxi h , KbdModInstance + KbdModInstanceT.KbdDelayCt
dcr m ;
mov a , m ;
cpi 0 ; if KbdModInstance.KbdDelayCt = 0 then
jnz KyebSrv_21 ; begin
call AnyKeyActive ; if AnyKeyActive ( ) then
cpi 0 ;
lxi h , KbdModInstance + KbdModInstanceT.KbdState
jz KyebSrv_22 ;
mvi m , KbdReady ; KbdModInstance.KbdState := KbdReady ;
jmp KyebSrv_23 ;
KyebSrv_22 ; else
mvi m , KbdIdle ; KbdModInstance.KbdState := KbdIdle ;
KyebSrv_23 ;
KyebSrv_21 ; end ;
jmp KyebSrv_0 ; end ;
KyebSrv_3 ;
cpi KbdReady ; KbdReady :
jnz KyebSrv_4 ; begin
; if KbdModInstance.HardwKbdReadSrv <> nil then
lxi d , KbdModInstance + KbdModInstanceT.HardwKbdReadSrv
lhlx ;
mov a , h ;
ora l ;
jz KyebSrv_31 ;
; KbdModInstance.LastKeybInp := KbdModInstance.HardwKbdReadSrv ( ) &
call CallHLService ; KbdModInstance.BitMask
lxi h , KbdModInstance + KbdModInstanceT.BitMask
ana m ;
sta KbdModInstance + KbdModInstanceT.LastKeybInp
jmp KyebSrv_32 ;
KyebSrv_31 ; else
; KbdModInstance.LastKeybInp := KbdModInstance.BitMask ;
lda KbdModInstance + KbdModInstanceT.BitMask
sta KbdModInstance + KbdModInstanceT.LastKeybInp
KyebSrv_32 ;
mvi a , KbdActive ; KbdModInstance.KeybState := KbdActive ;
sta KbdModInstance + KbdModInstanceT.KbdState
jmp KyebSrv_0 ; end ;
KyebSrv_4 ;
cpi KbdActive ; KbdActive :
jnz KyebSrv_5 ; begin
; DecodeKey ( KbdModInstance.LastKeybInp ) ;
lda KbdModInstance + KbdModInstanceT.LastKeybInp
call DecodeKey ;
mvi a , KbdWaitOnFree ; KbdModInstance.KeybState := KbdWaitOnFree ;
sta KbdModInstance + KbdModInstanceT.KbdState
jmp KyebSrv_0 ; end ;
KyebSrv_5 ;
cpi KbdWaitOnFree ; KbdWaitOnFree :
jnz KyebSrv_6 ; begin
call AnyKeyActive ; if not AnyKeyActive ( ) then
cpi 0 ;
jnz KyebSrv_61 ;
mvi a , KbdIdle ; KbdModInstance.KeybState := KbdIdle ;
sta KbdModInstance + KbdModInstanceT.KbdState
KyebSrv_61 ;
KyebSrv_6 ; end ;
KyebSrv_0 ; end (* case *) ;
ret ;end ;
Zostały tu wykorzystane odpowiedniki struktury[C]/rekordów[Pascal] i z tego powodu właśnie napisałem sobie sam kompilera języka asm. Tak przy okazji uwzględnione są nieudokumentowane instrukcje i są nawet zastosowane w programie. Ponieważ program działa, to można powiedzieć, że te instrukcje może i są nieudokumentowane, ale są prawdziwe.
Kod: Zaznacz cały
KbdBuffSz .equ 4
KbdCyclicRecT .struct ; KbdCyclicRecT = record
CycFr .byte ; CycFr : byte ;
CycTo .byte ; CycTo : byte ;
Buffer .byte KbdBuffSz ; Buffer : array[0..KbdBuffSz-1] of char ;
.endstruct ; end (* record *) ;
;*******************************************
KbdIdle .equ 1 ; wartosci typu
KbdWaitOnPress .equ 2 ; wyliczeniowego
KbdReady .equ 3 ; dla automatu
KbdActive .equ 4 ; stanu
KbdWaitOnFree .equ 5 ;
;*******************************************
KbdModInstanceT .struct ; KbdCyclicRecT = record
KbdCyclic .byte sizeof ( KbdCyclicRecT )
; KbdCyclic : KbdCyclicRecT ;
ExtraKbdReadSrv .word ; pointer ; ExtraKbdReadSrv : procedure ;
HardwKbdReadSrv .word ; pointer ; HardwKbdReadSrv : function : byte ;
HardwKbdSetRowSrv .word ; pointer ; HardwKbdSetRowSrv : procedure ( byte ) ;
EncodeTableRow0 .word ; pointer ; EncodeTableRow0 : ^ char ;
EncodeTableRow1 .word ; pointer ; EncodeTableRow1 : ^ char ;
EncodeTableRow2 .word ; pointer ; EncodeTableRow2 : ^ char ;
EncodeTableRow3 .word ; pointer ; EncodeTableRow3 : ^ char ;
KbdState .byte ; KbdState : byte ;
KbdDelayCt .byte ; KbdDelayCt : byte ;
RowCounter .byte ; RowCounter : byte ;
LastKeybInp .byte ; LastKeybInp : byte ;
BitMask .byte ; BitMask : byte ;
.endstruct ; end (* record *) ;
Taka konstrukcja dla kompilera wnosi jedynie wielkości poszczególnych pól i ofsety do nich. Jak trzeba do struktury dodać pole, to się je dodaje, kompiler to łyka (sam przeliczy ofsety):
Kod: Zaznacz cały
KbdModInstanceT STRUCT, size=0019 hex [25 dec], orgin=0000 hex
KbdCyclic size=0006 hex [6 dec], offset=0 dec
ExtraKbdReadSrv size=0002 hex [2 dec], offset=+6 dec
HardwKbdReadSrv size=0002 hex [2 dec], offset=+8 dec
HardwKbdSetRowSrv size=0002 hex [2 dec], offset=+10 dec
EncodeTableRow0 size=0002 hex [2 dec], offset=+12 dec
EncodeTableRow1 size=0002 hex [2 dec], offset=+14 dec
EncodeTableRow2 size=0002 hex [2 dec], offset=+16 dec
EncodeTableRow3 size=0002 hex [2 dec], offset=+18 dec
KbdState size=0001 hex [1 dec], offset=+20 dec
KbdDelayCt size=0001 hex [1 dec], offset=+21 dec
RowCounter size=0001 hex [1 dec], offset=+22 dec
LastKeybInp size=0001 hex [1 dec], offset=+23 dec
BitMask size=0001 hex [1 dec], offset=+24 dec
Przykładowo: pole KbdState jest +20 bajtów od początku zmiennej (KbdModInstance):
Kod: Zaznacz cały
.org RAMSpace
KbdModInstance .KbdModInstanceT
Instrukcja
Kod: Zaznacz cały
lda KbdModInstance + KbdModInstanceT.KbdState
ładuje do akumulatora określone pole: jako adres zmiennej + ofset pola w obszarze zmiennej. Człowiek nie musi się pocić, a kompiler się nie myli.
Powyższa procedura
KeyService, wynajduje wciśnięty przycisk jako scan-kod. Jest on później transponowany na znak ASCII w oparciu o cztery tablice (dla każdego wiersza oddzielnie).
Kod: Zaznacz cały
Row0EncodeTable .defb 11111110b , '1'
.defb 11111101b , '2'
.defb 11111011b , '3'
.defb 11110111b , '4'
.defb 11101111b , '5'
.defb 11011111b , '6'
.defb 10111111b , '7'
.defb 01111111b , '8'
.defb 0 , 0
;-------------------------------------------
Row1EncodeTable .defb 11111110b , 'a'
.defb 11111101b , 'b'
.defb 11111011b , 'c'
.defb 11110111b , 'd'
.defb 11101111b , 'e'
.defb 11011111b , 'f'
.defb 10111111b , 'g'
.defb 01111111b , 'h'
.defb 0 , 0
;-------------------------------------------
Row2EncodeTable .defb 11111110b , 'A'
.defb 11111101b , 'B'
.defb 11111011b , 'C'
.defb 11110111b , 'D'
.defb 11101111b , 'E'
.defb 11011111b , 'F'
.defb 10111111b , 'G'
.defb 01111111b , 'H'
.defb 0 , 0
;-------------------------------------------
Row3EncodeTable .defb 11111110b , '!'
.defb 11111101b , '@'
.defb 11111011b , '#'
.defb 11110111b , '$'
.defb 11101111b , '%'
.defb 11011111b , '^'
.defb 10111111b , '&'
.defb 01111111b , '*'
.defb 0 , 0
Moduł klawiatury jest całkowicie „abstrakcyjny”, to znaczy nie dotyka sprzętu. Za współpracę odpowiedzialne są dołączane w czasie inicjacji funkcje i procedury.
Kod: Zaznacz cały
Init_variable ;procedure Init_variable ;
;begin
mvi a , 0ffh ; SoftKbdInit(0ffh,HardvInpKbd,HardvSetRowKbd) ;
lxi d , HardvInpKbd ;
lxi h , HardvSetRowKbd ;
call SoftKbdInit ;
mvi a , 0 ; SetEncodeTable ( 0 , Row0EncodeTable ) ;
lxi h , Row0EncodeTable ;
call SetEncodeTable ;
mvi a , 1 ; SetEncodeTable ( 1 , Row1EncodeTable ) ;
lxi h , Row1EncodeTable ;
call SetEncodeTable ;
mvi a , 2 ; SetEncodeTable ( 2 , Row2EncodeTable ) ;
lxi h , Row2EncodeTable ;
call SetEncodeTable ;
mvi a , 3 ; SetEncodeTable ( 3 , Row3EncodeTable ) ;
lxi h , Row3EncodeTable ;
call SetEncodeTable ;
ret ;end ;
gdzie
Kod: Zaznacz cały
HardvInpKbd ;function HardvInpKbd : byte[A] ;
;begin
in KBDPort + Port8255.PortB ; HardvInpKbd := KBDPort.PortB ;
ret ;end ;
.
.
.
HardvSetRowKbd ;procedure HardvSetRowKbd(Row[A]:byte);
;begin
call SetAllRowOnHigh ; SetAllRowOnHigh ;
; case KbdModInstance.RowCounter of
lda KbdModInstance + KbdModInstanceT.RowCounter
cpi 0 ; 0 :
jnz HvdSeRSrv_1 ;
mvi a , ClrRow0 ; KBDPort.PortC[4] := 0 ;
out KBDPort + Port8255.Control ;
jmp HvdSeRSrv_0 ;
HvdSeRSrv_1 ;
cpi 1 ; 1 :
jnz HvdSeRSrv_2 ;
mvi a , ClrRow1 ; KBDPort.PortC[5] := 0 ;
out KBDPort + Port8255.Control ;
jmp HvdSeRSrv_0 ;
HvdSeRSrv_2 ;
cpi 2 ; 2 :
jnz HvdSeRSrv_3 ;
mvi a , ClrRow2 ;
out KBDPort + Port8255.Control ; KBDPort.PortC[6] := 0 ;
jmp HvdSeRSrv_0 ;
HvdSeRSrv_3 ;
cpi 3 ; 3 :
jnz HvdSeRSrv_4 ; KBDPort.PortC[7] := 0 ;
mvi a , ClrRow3 ;
out KBDPort + Port8255.Control ;
jmp HvdSeRSrv_0 ;
HvdSeRSrv_4 ; end (* case *) ;
HvdSeRSrv_0 ;
ret ;end ;
Po zainicjowaniu struktury, dalej już „jedzie” bazując na instrukcji
pchl.
rnd6_05.jpg
Wyprowadziłem sobie klawiaturę na stykówkę, ale miałem tylko 8-pinową „smyczkę” zamiast 12-pinowej, więc klawiatura jest trochę ucięta: drutem łączę jedno z czerech wyjść z jednym z 4 wejść (zamiast 8). Program przekodowuje to na znaki i wyświetla je na LCD.
rnd6_06.jpg
No i niech ktoś mnie przekona, że w asm nie da się programować jak w Pascal’u.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.