Program pro obsluhu tlačítek na jednotce EPS-103 (a ovládání motorů)
Je prima umět napsat na displeji jakýkoliv nápis. EPS-103 nám může hlásit, co náš program dělá. Další periferií, kterou je potřeba zvládnout, je klávesnice. EPS-103 má celkem 16 spínačů. Především dvě velká tlačítka Up a Down. Pod otevíracími dvířky najdeme tlačítka 0 až 9 a tlačítko E. A třípolohový přepínač MODE, který ve skutečnosti funguje jako tři samostatné spínače, z nichž jeden je vždy trvale sepnutý.
16 spínačů je zapojeno do matice 4 x 4 vodičů. Vstup do matice je zapojen na port A obvodu 8255A, tedy na adrese 5800h. Aktivní je jen spodní polovina portu. Horní čtyři bity jsou podle schématu nepoužity. Zde by byla možnost rozšířit klávesnici o další uživatelská tlačítka. Výstup z matice je směrován na port B obvodu 8255A, tedy na adresu 5801h. Opět jsou aktivní jen spodní čtyři bity. Na horní polovině portu jsou podle schématu připojeny interní propojky W1, W2 a W3 s označením Test.
Obsluha maticové klávesnice je celkem jednoduchá. Na vstup se zapíše binární číslo 1110 (1101, 1011, 0111) a potom se přečte výstup z matice. Podle jeho binární hodnoty (1111, 1110, 1101, 1011, 0111) poznáme, který spínač byl aktivovaný. Hodnota 1111 znamená, že žádné tlačítko není sepnuté. V následující tabulce jsou hodnoty jednotlivých spínačů. Hvězdička nahrazuje bity, které na funkci klávesnice nemají vliv. Program si je obvykle přepíše na samé jedničky.
Write Address 5800h |
Read Address 5801h |
Button EPS-103 |
MyCode | ||||
bin | hex | bin | hex | 4600h | 4601h | ||
Group 0 | 1111 1110 | 0FE | **** 1110 | 0*E | 1 | 01h | |
**** 1101 | 0*D | 2/E | 02h | ||||
**** 1011 | 0*B | 3/U | 03h | ||||
**** 0111 | 0*7 | 4/W | 04h | ||||
Group 1 | 1111 1101 | 0FD | **** 1110 | 0*E | 5 | 05h | |
**** 1101 | 0*D | 6 | 06h | ||||
**** 1011 | 0*B | 7 | 07h | ||||
**** 0111 | 0*7 | 8/D | 08h | ||||
Group 2 | 1111 1011 | 0FB | **** 1110 | 0*E | 9 | 09h | |
**** 1101 | 0*D | 0 | 0Ah | ||||
**** 1011 | 0*B | E | 0Bh | ||||
**** 0111 | 0*7 | Not used | - | - | |||
Group 3 | 1111 0111 | 0F7 | **** 1110 | 0*E | PRG | 10h | |
**** 1101 | 0*D | TCH | 20h | ||||
**** 1011 | 0*B | RUN + Up | 30h | 0Ch | |||
**** 0111 | 0*7 | RUN + Down | 30h | 0Dh |
Spínač RUN je zapojený trochu jinak, než ostatní spínače. Je v sérii s tlačítky Up a Down. To znamená, že použití spínače PRG nebo TCH vyřadí tlačítka Up a Down z činnosti. Proto je potřeba nejprve v první smyčce provést identifikaci těchto tří tlačítek a s nimi tlačítka Up a Down. Teprve následně ve druhé smyčce skenujeme zbývající tlačítka. Po identifikaci stisknuté klávesy už můžeme spustit nějakou činnost. Třeba vypsat na displeji, která klávesa je stisknutá.
Tento program bude ke své činnosti potřebovat paměť RAM. Využijeme prostor přidělený zásobníku. Zásobník začíná na adrese 4700h a naplňuje se směrem k nižším hodnotám. Proto jsem vybral adresy 4600h až 4603h. Jsou dost blízko zásobníku, takže původní firmware je určitě k ničemu jinému nepoužívá. Zároveň jsou dost daleko od začátku zásobníku, aby je náš program při používání zásobníku nepřepsal jinými údaji.
Na adresu 4600h zapíšeme kód stisknuté klávesy z trojice PRG, TCH, RUN. Pokud je stisknutá některá ze zbývajících kláves, zapíšeme její kód na adresu 4601h. Vypisovat název stisknutého tlačítka na displeji má smysl pouze tehdy, když došlo k jeho změně. Proto jsou použity ještě kontrolní adresy 4602h a 4603h. Do nich okopírujeme hodnoty hlavních adres 4600h a 4601h. Porovnáním originálů a kopií poznáme, jestli se změnil stav klávesnice.
Skenování klávesnice může být součástí hlavního programu. Obvykle se ale opakované akce řeší pomocí nějakého přerušení. V jednotce EPS-103 je generované HW přerušení RST7.5 od generátoru impulzů NE555. Perioda pulzů a tedy opakování přerušení je 4 ms. To by bylo možné využít k obsluze klávesnice. Jen musíme mít jistotu, že náš obslužný program skončí dříve, než se znovu spustí přerušení. Například smazání LCD displeje trvá podle dokumentace 15 ms. To znamená, že obsluhu displeje během přerušení nestihneme.
Ze studijních důvodů přerušení RST7.5 použijeme, ale jen ke skenování klávesnice. Výsledek skenování zapíšeme do RAM na adresy 4600h a 4601h. V hlavním programu bude smyčka, která přečte tyto adresy a podle obsahu provede zpracování včetně psaní na displej.
Úvod programu je stejný jako u programu Hello world. Nejprve nastavíme adresu zásobníku v RAM na hodnotu 4700h a potom inicializujeme obvod 8255A. Pak musíme správně nastavit přerušení. Je potřeba povolit RST7.5 zapsáním 0 do bitu 2 registru s maskou přerušení. To provede instrukce SIM. Pak už můžeme přerušení spustit instrukcí EI. Nekonečná smyčka bude opakovaně číst adresy 4600h a 4601h a porovnávat je s kopiemi na adresách 4602h a 4603h. V případě jejich rozdílu nejprve aktualizujeme kopie. Potom smažeme celý displej a vypíšeme na něj nastavení přepínače MODE a za ním název stisknutého tlačítka. Program neřeší možnost současného stisknutí více tlačítek.
Aby měl program přece jen nějakou konkrétní funkci, můžeme při stisku tlačítek 2/E, 3/U, 4W a 8/D aktivovat motory v příslušném směru. Po uvolnění tlačítka motor zastavíme. Pokud někdo upraví program tak, aby hledal víc současně stisknutých tlačítek, musí si řádně rozmyslet, jak na ně reagovat. Například spustit současně motor pro azimut i elevaci je v pořádku. Aktivovat otáčení jednoho motoru současně na obě strany ale nedává smysl. Změnit směr otáčení během chodu motoru by pravděpodobně motor poškodilo.
Rozhraní motorů je připojeno na port C obvodu 8255A, tedy na adrese 5802h. Motory se ovládají zápisem řídícího bajtu na tento port. Význam jednotlivých bitů je následující:
Bity adresy 5802h | Funkce |
0 | Směr elevace: 0 = Up; 1 = Down |
1 | Motor elevace: 0 = Off; 1 = On |
2 | High Speed: 0 = Off; 1 = On |
3 | Směr azimutu: 0 = West; 1 = East |
4 | Motor azimutu: 0 = Off; 1 = On |
5 | Nepoužito |
6 | Nepoužito |
7 | Nepoužito |
Odpovídající program by mohl vypadat takto:
jmp Start
.ORG 003Ch
jmp IntRST75
.ORG 0040h
.dseg
nokey .text "and no other key is pressed"
key1 .text "and Key 1 is pressed"
key2 .text "and Key 2/E is pressed"
key3 .text "and Key 3/U is pressed"
key4 .text "and Key 4/W is pressed"
key5 .text "and Key 5 is pressed"
key6 .text "and Key 6 is pressed"
key7 .text "and Key 7 is pressed"
key8 .text "and Key 8/D is pressed"
key9 .text "and Key 9 is pressed"
keyA .text "and Key 0 is pressed"
keyE .text "and Key E is pressed"
keyup .text "and Key Up is pressed"
keydn .text "and Key Down is pressed"
keyprg .text "MODE PRG "
keytch .text "MODE TCH "
keyrun .text "MODE RUN "
.cseg
Start: lxi sp, 4700h ; Set Stack Address
mvi a, 82h ; CW
sta 5803h ; 8255A Write Control Word
xra a ; 0 -> a
sta 5802h ; 0 -> port C: stop motors
mvi a, 0Ch ; LCD Command: Display on, cursor off
call sub_1B70 ; Command -> LCD
mvi a, 00h
sta 4600h ; Initialization RAM
sta 4601h ; Initialization RAM
sta 4602h ; Initialization RAM
sta 4603h ; Initialization RAM
mvi a, 0Bh ; Mask interrupt, 0000 1011, 0 -> bit2
sim ; Set mask
ei ; Enable Interrupt
TestKey: lxi h, 4600h ; Source RAM
mov a, m ; value -> a
lxi h, 4602h
cmp m ; Compare the value with the copy
jnz TestCont ; if value 4600h <> value 4602h, jump
lxi h, 4601h ; Source RAM
mov a, m ; Compare the value with the copy
lxi h, 4603h
cmp m ; Compare the value with the copy
jz TestKey ; if value 4601h = value 4603h, nothing changed, test again
TestCont: lxi h, 4600h ; Source RAM
mov a, m ; value -> a
sta 4602h ; copy
lxi h, 4601h ; Source RAM
mov a, m ; value -> a
sta 4603h ; copy
mvi a, 01h ; LCD Command: Clear Display
call sub_1B70 ; Command -> LCD
lxi h, 4600h ; Mode switch MyCode
mov a, m ; Switch status
cpi 10h ; Is the PRG key?
jnz Test20 ; If not, jump
lxi h, keyprg ; Address of text
mvi b, 09h ; Number of characters
call WrStr ; Text -> LCD
jmp Test01
Test20: lxi h, 4600h ; Mode switch MyCode
mov a, m ; Switch status
cpi 20h ; Is the TCH key?
jnz Test30 ; If not, jump
lxi h, keytch ; Address of text
mvi b, 09h ; Number of characters
call WrStr ; Text -> LCD
jmp Test01
Test30: lxi h, keyrun ; Address of text
mvi b, 09h ; Number of characters
call WrStr ; Text -> LCD
Test01: lxi h, 4601h
mov a, m ; Key MyCode
cpi 01h ; Is the 1 key?
jnz Test02 ; If not, jump
lxi h, key1 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test02: lxi h, 4601h
mov a, m ; Key MyCode
cpi 02h ; Is the 2/E key?
jnz Test03 ; If not, jump
lxi h, key2 ; Address of text
mvi b, 16h ; Number of characters
call WrStr ; Text -> LCD
mvi a, 18h ; 0001 1000 East = On
sta 5802h
jmp TestKey ; Neverending loop
Test03: lxi h, 4601h
mov a, m ; Key MyCode
cpi 03h ; Is the 3/U key?
jnz Test04 ; If not, jump
lxi h, key3 ; Address of text
mvi b, 16h ; Number of characters
call WrStr ; Text -> LCD
mvi a, 02h ; 0000 0010 Up = On
sta 5802h
jmp TestKey ; Neverending loop
Test04: lxi h, 4601h
mov a, m ; Key MyCode
cpi 04h ; Is the 4/W key?
jnz Test05 ; If not, jump
lxi h, key4 ; Address of text
mvi b, 16h ; Number of characters
call WrStr ; Text -> LCD
mvi a, 10h ; 0001 0000 West = On
sta 5802h
jmp TestKey ; Neverending loop
Test05: lxi h, 4601h
mov a, m ; Key MyCode
cpi 05h ; Is the 5 key?
jnz Test06 ; If not, jump
lxi h, key5 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test06: lxi h, 4601h
mov a, m ; Key MyCode
cpi 06h ; Is the 6 key?
jnz Test07 ; If not, jump
lxi h, key6 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test07: lxi h, 4601h
mov a, m ; Key MyCode
cpi 07h ; Is the 7 key?
jnz Test08 ; If not, jump
lxi h, key7 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test08: lxi h, 4601h
mov a, m ; Key MyCode
cpi 08h ; Is the 8/D key?
jnz Test09 ; If not, jump
lxi h, key8 ; Address of text
mvi b, 16h ; Number of characters
call WrStr ; Text -> LCD
mvi a, 03h ; 0000 0011 Down = On
sta 5802h
jmp TestKey ; Neverending loop
Test09: lxi h, 4601h
mov a, m ; Key MyCode
cpi 09h ; Is the 9 key?
jnz Test0A ; If not, jump
lxi h, key9 ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test0A: lxi h, 4601h
mov a, m ; Key MyCode
cpi 0Ah ; Is the 0 key?
jnz Test0B ; If not, jump
lxi h, keyA ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test0B: lxi h, 4601h
mov a, m ; Key MyCode
cpi 0Bh ; Is the E key?
jnz Test0C ; If not, jump
lxi h, keyE ; Address of text
mvi b, 14h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test0C: lxi h, 4601h
mov a, m ; Key MyCode
cpi 0Ch ; Is the Up key?
jnz Test0D ; If not, jump
lxi h, keyup ; Address of text
mvi b, 15h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
Test0D: lxi h, 4601h
mov a, m ; Key MyCode
cpi 0Dh ; Is the Down key?
jnz TestNoK ; If not, jump
lxi h, keydn ; Address of text
mvi b, 17h ; Number of characters
call WrStr ; Text -> LCD
jmp TestKey ; Neverending loop
TestNoK: lxi h, nokey ; Address of text
mvi b, 1Bh ; Number of characters
call WrStr ; Text -> LCD
mvi a, 00h
sta 5802h ; 0 -> PortC: stop motors
jmp TestKey ; Neverending loop
hlt ; For sure
; Write 1 byte command
sub_1B70: call sub_1B7E ; Wait for LCD ready
sta 4802h ; Write command byte
ret
; Write 1 byte data
sub_1B77: call sub_1B7E ; Wait for LCD ready
sta 4803h ; Write data byte
ret
; Wait for LCD ready
sub_1B7E: push psw
loc_1B7F: lda 4800h ; Read LCD status
ani 80h ; Test bit 7: 0 = ready
jnz loc_1B7F ; if not, repeat
pop psw
ret
; Write string on LCD
WrStr: mov a, m
call sub_1B77 ; Character -> LCD
inx h ; Address of next characters
dcr b ; Decrement counter
jnz WrStr
ret
; Interrupt handling RST7.5
IntRST75: push psw ; Save the contents of the registers to the stack
push b
push d
push h
mvi a, 0F7h
sta 5800h ; Scan Group 3
lda 5801h
ori 0F0h
cpi 0FEh ; Is the PRG key?
jnz TCH_20 ; If not, jump
mvi a, 10h ; The key is PRG, MyCode = 10h
sta 4600h
jmp ScanGr0 ; Go scan group 0
TCH_20: cpi 0FDh ; Is the TCH key?
jnz RUN_30 ; If not, jump
mvi a, 20h ; The key is TCH, MyCode = 20h
sta 4600h
jmp ScanGr0 ; Go scan group 0
RUN_30: cpi 0FBh ; Are the RUN + Up keys?
jnz RUN_31 ; If not, jump
mvi a, 30h ; The key is RUN, MyCode = 30h
sta 4600h
mvi a, 0Ch ; and the key is Up, MyCode = 0Ch
sta 4601h
jmp EndISR ; Finish scanning
RUN_31: cpi 0F7h ; Are the RUN + Down keys?
jnz RUN_32 ; If not, jump
mvi a, 30h ; The key is RUN, MyCode = 30h
sta 4600h
mvi a, 0Dh ; and the key is Up, MyCode = 0Dh
sta 4601h
jmp EndISR ; Finish scanning
RUN_32: mvi a, 30h ; The key must be RUN
sta 4600h
ScanGr0: mvi a, 0FEh ; Mask Group 0
sta 5800h ; Write mask
lda 5801h ; Scan Group 0
ori 0F0h
cpi 0FEh ; Is the 1 key?
jnz Gr0_2 ; If not, jump
mvi a, 01h ; The key is 1, MyCode = 01h
sta 4601h
jmp EndISR ; Finish scanning
Gr0_2: cpi 0FDh ; Is the 2/E key?
jnz Gr0_3 ; If not, jump
mvi a, 02h ; The key is 2/E, MyCode = 02h
sta 4601h
jmp EndISR ; Finish scanning
Gr0_3: cpi 0FBh ; Is the 3/U key?
jnz Gr0_4 ; If not, jump
mvi a, 03h ; The key is 3/U, MyCode = 03h
sta 4601h
jmp EndISR ; Finish scanning
Gr0_4: cpi 0F7h ; Is the 4/W key?
jnz Gr1_1 ; If not, jump
mvi a, 04h ; The key is 4/W, MyCode = 04h
sta 4601h
jmp EndISR ; Finish scanning
Gr1_1: mvi a, 0FDh ; Mask Group 1
sta 5800h ; Write mask
lda 5801h ; Scan Group 1
ori 0F0h
cpi 0FEh ; Is the 5 key?
jnz Gr1_2 ; If not, jump
mvi a, 05h ; The key is 5, MyCode = 05h
sta 4601h
jmp EndISR ; Finish scanning
Gr1_2: cpi 0FDh ; Is the 6 key?
jnz Gr1_3 ; If not, jump
mvi a, 06h ; The key is 6, MyCode = 06h
sta 4601h
jmp EndISR ; Finish scanning
Gr1_3: cpi 0FBh ; Is the 7 key?
jnz Gr1_4 ; If not, jump
mvi a, 07h ; The key is 7, MyCode = 07h
sta 4601h
jmp EndISR ; Finish scanning
Gr1_4: cpi 0F7h ; Is the 8/D key?
jnz Gr2_1 ; If not, jump
mvi a, 08h ; The key is 8/D, MyCode = 08h
sta 4601h
jmp EndISR ; Finish scanning
Gr2_1: mvi a, 0FBh ; Mask Group 2
sta 5800h ; Write mask
lda 5801h ; Scan Group 2
ori 0F0h
cpi 0FEh ; Is the 9 key?
jnz Gr2_2 ; If not, jump
mvi a, 09h ; The key is 9, MyCode = 09h
sta 4601h
jmp EndISR ; Finish scanning
Gr2_2: cpi 0FDh ; Is the 0 key?
jnz Gr2_3 ; If not, jump
mvi a, 0Ah ; The key is 0, MyCode = 0Ah
sta 4601h
jmp EndISR ; Finish scanning
Gr2_3: cpi 0FBh ; Is the E key?
jnz NoKeyIs ; If not, jump
mvi a, 0Bh ; The key is E, MyCode = 0Bh
sta 4601h
jmp EndISR ; Finish scanning
NoKeyIs: mvi a, 00h ; no key is pressed, MyCode = 00h
sta 4601h
; Finish scanning
EndISR: pop h ; Restore the contents of the registers from the stack
pop d
pop b
pop psw
ei ; Enable Interrupt
ret
.END
Výsledný binární soubor nahrajeme programátorem do vymazané paměti EPROM a můžeme testovat. Věřím, že informace uvedené v tomto programu pomohou všem, kdo by chtěli zkoumat původní firmware EPS-103. Zdrojový text uvedeného programu si můžete stáhnout v souboru KeyboardEPS103.asm.