Modul inteligentního LCD displeje ovládaný PC klávesnicí

Navržené zařízení bylo vyvinuto v rámci semestrální práce do předmětu Mikroprocesorová technika na VOŠ v Kutné Hoře.

Autoři: Karel Novosád, Petr Svoboda, Oldřich Tichý

Zadání semestrální práce:
Navrhněte zařízení řízené jednočipovým mikropočítačem Atmel AT89C2051, které bude číst data z připojené klávesnice PC a na prvním řádku inteligentního displeje stisknuté klávesy zobrazovat. V okamžiku, kdy stisknete ENTER nebo zadáte stanovený počet znaků se začnou cyklycky zobrazovat načtené znaky na prvním řádku displeje formou běžícího textu. Na druhém řádku displeje se bude zobrazovat pevná reklama (čtená z paměti programu). Navržené zařízení realizujte na desce plošných spojů.

Požadavky na navrhované zařízení

Začátky jsou vždycky těžké…
Nejprve jsme provedli teoretický rozbor úlohy a rozhodli jsme se zprovoznit jednotku inteligentního LCD displeje a teprve potom řešit přijímání znaků z klávesnice. To se ukázalo jako nešťastná volba, která nám zabrala asi 4/5 času vyhrazeného pro vytvoření semestrální práce. Po mnoha nezdarech (popsaných v dalším textu) jsme však program pto mikroprocesor prakticky přepracovali od úplného začátku, přičemž jsme úplně vyřadili display a zobrazovali výstup na LED diodách. Teprve výstup na LED diodách nám ukázal, že základ programu pro příjem znaků z klávesnice byl dobrý a celý problém spočíval v nesprávných zpožďovacích smyčkách, které potřeboval display pro svou správnou funkci. Celý program se nám tedy podařilo sestavit během 14 dnů intenzivní práce.

Inteligentní displej s řadičem HD44780
Nutno podotknout, že většina inteligentních displejů prodávaných u nás používá přímo řadič HD44780 nebo jeho klon, takže všechny mají stejnou instrukční sadu a stejné ovládání. Obecně lze říci, že veliké rozšířenosti mezi konstruktéry dosáhli díky několika aspektům. Je to především relativně snadné ovládání, možnost definice až osmi vlastních znaků (např. čeština), různorodost ve výběru velikosti (1x16 až 4x40 znaků) při zachování jednotného ovládání a v neposlední řadě se jejich specifikace zavádí jako průmyslový standard, včetně zapojení vývodů (*). To usnadňuje ladění na HW i SW úrovni a případnou výměnu displeje (např. za větší, podsvětlený apod.) Obecně velkou výhodou LC displejů je malý odběr zobrazovací matice (řádově desítky mikroampérů), malé rozměry a nízká hmotnost ve srovnání s klasickou elektronovou obrazovkou, lepší geometrie a ostrost zobrazení, delší životnost, stálost obrazu (LC displeje mají mnohem vyšší kmitočet obnovení informace a jejich fyzikální princip umožňuje mnohem delší "dosvit", takže odpadá klasické blikání, nešvar to elektronových obrazovek a displejů) atd. Nevýhodou, která je však již u některých displejů odstraněna (na úkor ceny), je teplotní závislost, kdy kapalné krystaly při záporných a vysokých kladných teplotách ztrácejí své fyzikální vlastnosti a displej přestává dočasně pracovat.
Za cenu mírného zkomplikování protokolu lze s displejem komunikovat buď osmi nebo čtyřbitově.Náš použitý display byl jeden z nabízených NONAME klonů, který je o několik set levnější než např. značkový SHARP.
V naší úloze jsme použili display MC1602E-SYL s 2x16ti řádky v osmivodičovém (tedy jednodušším) zapojení. Nebyl důvod, proč bychom měli používat čtyřvodičové zapojení, protože procesor 89C2051 (i přesto že je to velmi "odlehčená verze" 8051), měl dostatek vývodů a nebyl důvod, proč si zbytečně komplikovat komunikační protokol (a tím také prodlužovat čas strávený v obslužné smyčce přerušení). Po několikahodinovém laborování a pokusech s displejem se nám podařilo displej inicializovat a posílat na něj znaky, měnit různě typy písem, režimy displeje atd. Musím popravdě říci, že mě možnosti displeje velmi mile překvapili. Z jeho instrukční sady bych chtěl vyzdvihnout instrukci rotace, která zajistí posun displeje (tzv. SHIFT) do požadovaného směru (směr je dán počátečním nastavením registrů displeje) bez změny obsahu paměti kódů znaků (DDRAM).

Zapojujeme displej...

Obr. 1 Použité osmivodičové zapojení displeje

8-vodičové zapojení displeje

Obsluhujeme displej...

Tab. 1 Zapojení vývodů displeje

Číslo
vývodu

Označení

V/V

Význam

1

Vss

-

0V (napájení)

2

Vcc

-

+5V (napájení)

3

Vee

-

Nastavení kontrastu

4

RS

Vstup

0 = vstup je instrukce
1 = vstup jsou data

5

R/W

Vstup

0 = zápis dat do LCD
1 = čtení dat z LCD

6

E

Vstup

Platná data

7

DB0

V/V

Data, bit 0 (nejnižší)

8

DB1

V/V

Data, bit 1

9

DB2

V/V

Data, bit 2

10

DB3

V/V

Data, bit 3

11

DB4

V/V

Data, bit 4

12

DB5

V/V

Data, bit 5

13

DB6

V/V

Data, bit 6

14

DB7

V/V

Data, bit 7 (nejvyšší)

15

 

-

Napájení podsvětlení, anoda

16

 

-

Napájení podsvětlení, katoda

POZNÁMKA: Nutno podotknout, že vývody 15 a 16 nemusí být osazeny nemá-li displej podsvícení. Náš displej ale podsvícení měl, proto byly vývody zapojeny.

Obr. 2 Komunikace řídicího systému s displejem po osmibitové sběrnici (časové průběhy):
Časové průběhy komunikace

Tab. 2 Tabulka použitých znaků o matici 5x7
Tabulka znaků

Paměť displeje pro uložení pozice znaků na zobrazovacím médiu se označuje jako DDRAM (Display Data Random Acces Memory). Níže jsou uvedeny pozice, na kterých jsou uloženy znaky pro zobrazení na řádcích (nižší adresa odpovídá znaku více vlevo).

Tab. 3 Rozložení a adresace paměti displeje pro uložení pozice znaků (DDRAM)

Počet znaků

Pozice v DDRAM (po řádcích)

2 x 16

00h..0Fh

40h..4Fh


Znaky


Tab. 4 Příkazy řadiče displeje:

Příkaz

Kód

Popis

Délka vykonání příkazu

RS

R/W

DB7

DB6

DB5

DB4

DB3

DB2

DB1

DB0

Smaže displej

0

0

0

0

0

0

0

0

0

1

Smaže displej a nastaví kurzor na pozici 0.

1,64mS

Nastaví kurzor na začátek

0

0

0

0

0

0

0

0

1

*

Nastaví kurzor na pozici 0 a vynuluje posun displeje (DDRAM beze změny)

1,64mS

Nastaví vstupní režim

0

0

0

0

0

0

0

1

I/D

S

Určí směr pohybu kurzoru (I/D) a posun displeje (S). Tyto operace se provádějí během čtení/zápisu.

40uS

Zapne/vypne displej, kurzor a jeho blikání

0

0

0

0

0

0

1

D

C

B

Zapíná/vypíná displej (D), kurzor (C) a jeho blikání (B).

40uS

Nastaví pohyb kurzoru/displeje

0

0

0

0

0

1

S/C

R/L

*

*

Nastaví pohyb kurzoru nebo displeje (S/C) a směr pohybu (R/L). Obsah DDRAM zůstane beze změny.

40uS

Nastavení interface

0

0

0

0

1

DL

N

F

*

*

Nastaví délku interface (DL), počet řádků displeje (N) a znakový font (F).

40uS

Nastaví pozici v CGRAM

0

0

0

1

Adresa v CGRAM

Po tomto příkazu jsou data ze vstupu zaznamenávána do CGRAM namísto DDRAM.

40uS

Nastaví pozici v DDRAM

0

0

1

Adresa v DDRAM

Po tomto příkazu jsou data ze vstupu zapisována do a čtena z DDRAM.

40uS

Čte příznak BUSY a hodnotu adresového čítače

0

1

BF

DDRAM address

Čte příznak BUSY (BF) indikující, že displej ještě prování některou operaci, a pozici ukazatele adresy .

0uS

Zapíše do DDRAM nebo CGRAM.

1

0

Data

Zapíše data ze vstupu DDRAM nebo do CGRAM.

40uS

Čte data z DDRAM nebo z CGRAM.

1

1

Data

Čte data z aktuální adresy DDRAM nebo CGRAM.

40uS


Tab. 5 Významy jednotlivých bitů v bytu instrukcí:

Jméno bitu

Nastavení

I/D

0 = Snížení pozice kurzoru

1 = Zvýšení pozice kurzoru

S

0 = displ. nepohybovat

1 = displ. Pohybovat

D

0 = Display vypnut

1 = Display zapnut

C

0 = kursor vypnut

1 = kursor zapnut

B

0 = blikáni kurzoru vypnuto

1 = blikáni kurzoru zapnuto

S/C

0 = pohybovat kurzorem

1 = pohybovat displejem

R/L

0 = rotovat vlevo

1 = rotovat vpravo

DL

0 = 4-bit interface

1 = 8-bit interface

N

0 = 1 řádkový displej

1 = 2 řádkový displej

F

0 = 5x7 bodů

1 = 5x10 bodů

BF

0 = může být zaslána instrukce

1 = Displej není připraven, provádí se vnitřní operace.


Z těchto údajů jsme sestavili inicializační smyčku displeje, kterou nyní vysvětlím:

clr RS
Tato řádka vynuluje pin P3.0 procesoru, který je zapojen na vývod RS displeje. Tento signál říká displeji, že následující přijímaná data budou instrukce, tj. příkazy, které ovládají displej a jeho funkce. Pokud je RS log.1, jedná se o data.

mov A,#00000001b
Instrukce smazání displeje. Smaže displej a nastaví kurzor na pozici 0.Ve skutečnosti se pouze hodnota (byte) instrukce do akumulátoru procesoru, ze kterého potom tuto hodnotu vyzvedne podprogram WRLCD, který fyzicky pošle akumulátor na port procesoru P1 a zajistí další nastavení signálů pro řádný zápis dat do displeje.

call wrlcd
Volání onoho zápisu + zpoždění (viz výpis WRLCD)...

call pause
Protože procesor je buzen hodinami o kmitočtu 12Mhz (tj. jeden systémový cyklus trvá 1us (1 mikrosekundu) (1/12 kmitočtu Xtalu - dáno procesorem)), je nutné "podržet" na portu P1 (a tím i na vstupu displeje) zapisovanou hodnotu. Displej totiž potřebuje také určitý čas na vykonání požadované instrukce (čas uveden v tabulce č.4 příkazů displeje) a pokud mu dodáváme data nebo instrukce rychleji, než je stačí zpracovat, logicky se musí data, která přijdou ve stavu nepřipravenosti displeje ignorovat (vlastně se ztratí). Naše zpoždění je 310 ms + 2x310ms v podprogramu zápisu(přestože výrobce uvádí potřebný čas pro inicializaci 1,64ms, nám se display bezpečně zinicializoval i po 930 ms (+ čas pro vykonání dalších instrukcí) a hlavně další příkazy se také vykonávaly (tj žádný nebyl z časových důvodů ztracen)). Domnívám se, že čas není zas až tak kritický. Výrobce nejspíš uvádí pouze maximální čas zpracování instrukce, který náš displej ani nepotřeboval.

mov A,#00000010b
Zapsání další instrukce displeje do akumulátoru. Tato instrukce nastaví kurzor na pozici 0 a vynuluje posun displeje (tzn. pokud displej rotoval, vrátí se displej do stavu před rotací (adresa 0 v DDRAM bude odpovídat prvnímu znaku v prvním řádku displeje)) (DDRAM zůstane beze změny)

call WrLCD
zápis do displeje + zpoždění (viz výpis WRLCD)...

call pause
Nutné zpoždění...

mov A,#00000110b
Tento příkaz displeje určuje směr pohybu kurzoru/displeje (bit I/D) a určí, zda se displej bude při zápisu posunovat (bit S). Tyto operace se provádějí během čtení/zápisu, to znamená, že v našem případě se při zadávání znaků z klávesnice bude posunovat kurzor stále dopředu. Kurzor (nebo celý obsah displeje) se posune pouze při poslání a zobrazení znaku na displeji a ne po poslání této instrukce. Tato instrukce pouze říká displeji, co má udělat, když přijme znak.

call WrLCD
zápis do displeje + zpoždění (viz výpis WRLCD)...

mov A,#00001101b
Ta instrukce umožňuje vypnout display (bit D=1 - zapnut), dále umožňuje zapnout/vypnout kurzor (C) a jeho blikání (B). Přestože ale máme v programu vypnuto zobrazování kurzoru (ale je zapnuto jeho blikání), kurzor je přesto viditelný. Po zaplnění displeje proto pouze nulujeme bit blikání a kurzor zmizí.

call WrLCD
zápis do displeje + zpoždění (viz výpis WRLCD)...

mov A,#00111100b
Tato instrukce nastavuje délku komunikačního interface (bit DL), tzn. 4bit nebo 8bit. Dále počet řádků displeje a použitý znakový font (bity A a F).

call WrLCD
zápis do displeje + zpoždění (viz výpis WRLCD)...

call pause2
Celý proces inicializace je zakončen větším zpožděním (asi 390ms). Toto zpoždění si však již nežádá display, je zde spíše pro jistotu.

Popis procedury pro zápis do displeje WrLCD:

Wrlcd:
setb E
Zajistí displeji platná data až v okamžiku zápisu. Bit E je jakýsi povolovací vstup displeje ENABLE či DATA VALID.

mov P1,A
Fyzický zápis dat z akumulátoru, kde je připravena instrukce pro display, na port, na který je připojen displej.

call Pause
Zpoždění pro jistotu, nikam nechvátáme. Tyto různé podprogramy, obsahující zpoždění, které jsou volány z přerušovací smyčky by mohli ohrozit chod programu. Avšak náš program má na starosti pouze ovládání klávesnice a displeje (navíc klávesnice ovládané "pomalým" člověkem), takže se toto zpoždění vůbec neprojeví a je jedno, jestli je tento čas stráveném v přerušení, nebo mimo něj. Člověk by musel umět vysílat znaky v intervalu kratším než 1ms, aby se program začal chovat nesprávně. Tato hranice je jistě nedosažitelná i pro vnitřní obvody klávesnice. I při stále stisknutém tlačítku klávesnice se znaky vysílají v mnohem delším intervalu než kritická 1ms (odhadnutý čas, pravděpodobně bude větší). Vzhledem ke kmitočtu hodin klávesnice 10-20kHz, trvá vyslání jednoho bitu dat 0,1ms - 0,05ms, takže máme na celé přijmutí a zpracování "spoustu" času.

clr E
Zajistí displeji platná data až v okamžiku zápisu. Bit E je jakýsi povolovací vstup displeje ENABLE či DATA VALID.

call Pause
Opět si trochu počkáme… pro jistotu…

V průběhu ladění programu jsme všude dávali pro jistotu větší časové prodlevy. To ale byla obrovská chyba, která, troufám si říct, zpozdila dokončení našeho programu o zhruba 3 měsíce. Základním omylem v koncepci programu bylo umístění celého těla programu (včetně všech zpoždění) do přerušovací smyčky, která však nemůže být pokud je přerušení obsluhováno, znovu přerušena. V přerušovací smyčce však bylo tolik zpoždění, že byl zpracován pouze první vyslaný bit a ostatní procesoru "utekly", protože se ještě "flákal" ve zpoždění v přerušovací smyčce. Umístění prakticky celého programu jsme ponechali v obslužné smyčce přerušení z několika důvodů. Jedním z nich je větší jednoduchost a přehlednost programu. Druhým důvodem je, že i když jsme program rozdělili a v přerušovací smyčce jsme nechali pouze program pro příjem znaků, začal se celý přípravek chovat velmi zvláštně. Proto jsme zachovali původní koncepci. Dalším důvodem pro zachování této koncepce byl i kritický nedostatek času a pro naší aplikaci řekl bych zbytečným plýtváním času (protože systémový čas strávený v přerušení nebyl v naší aplikaci kritický). Přesto cítím, že umístění hlavního programu mimo přerušení by bylo programátorsky správnější a lepší. Bohužel, díky počátečním problémům na to nebyl čas.
Většinu problémů se zpožděními jsme mohli vyřešit již na začátku. A to tak, že bychom četli příznakový bit displeje BUSY, který indikuje, že displej není připraven přijímat data nebo instrukce, a že provádí nějakou vnitřní operaci. Použitím tohoto řešení bychom odstranili použití podprogramů PAUSE a přesně bychom věděli, kdy bude možné displeji vysílat další data. Takto bychom ještě více zvětšili časové rezervy při obsluze přerušení.
POZNÁMKA: Jak bude patrné z následujícího textu, klávesnici jsme připojili tak, že hodiny CLK klávesnice (které jsou vysílány jenom při vysílání, tedy při stisku znaku) jsme připojili na přerušení INT0 procesoru a sériová data z klávesnice pak na port P3.7. T oto řešení se nám zdálo nejvýhodnější: při příchodu invertovaného impulsu z CLK (a tudíž vyvolání přerušení) jsou na P3.7 platná data. Jaká nádhera…

Obsluha displeje po přijmutí všech znaků nebo po stisku ENTER:
Zpočátku se nám rotace na displeji jevila jako poměrně obtížný úkol. Avšak po pečlivém prostudování dokumentace k displeji jsme zjistili, že vlastně vůbec nemusíme zajišťovat rotaci programově, ale že pouze stačí displeji poslat instrukci a on zajistí rotaci sám (a to vše beze změny DDRAM). Toto velmi výhodné řešení však ztratilo svoje kouzlo po vyzkoušení. Naše původní zadání totiž znělo tak, že první řádek má rotovat a druhý řádek s konstantní reklamou má stát. Displej totiž po přijetí instrukce rotace rotuje oběma řádky (a to i při rotaci během vkládání znaků) a neumí rotovat každým řádkem zvlášť. Pokusili jsme se proto zajistit stojatý druhý řádek programově, ale narazili jsme na další problémy, které pramenili z malé velikosti DDRAM pro druhý řádek. Problém jsme vyřešili tak, že jsme po rotaci na druhý řádek zapsali celou reklamu znovu. Ale to byl právě ten problém. Adresa počátku prvního řádku se neměnila, druhého však ano (po každé rotaci se zvětšila o jednu). Pokud tedy byla délka reklamy 16 řádků a displej měl za sebou 24 rotací, nastal problém, kam s přebývajícími znaky nad adresu 40. Bylo tedy nutné, aby rotující text v prvním řádku nebyl delší než 24 znaků, což se nám zdálo příliš málo. Řešení s programovým řízením posunu jsme také z důvodů časových zavrhli a vytvořili jsme program, kde rotuje i reklama.

Princip zobrazení reklamy:
Princip zobrazeni reklamy je velmi jednoduchý. Nejprve se nastaví jako aktivní pozice displeje počátek druhého řádku, tj. adresa 40H. Dále se rozhodne (podle bitu rotuj), zda se načte jako počáteční adresa tabulky adresa tabulky UVOD nebo UVOD2. Nyní se spustí cyklus, který postupně zapisuje na displej všechny znaky z tabulky (inkrementace počáteční adresy tabulky, zápis na displej, který inkrementuje aktivní adresu sám). Cyklus končí zjištěním znaku "'" (apostrof), který se již nezobrazí.

Zobrazování dalších znaků a textů:
Princip zobrazení jednotlivých znaků a víceznakých textů (zadávaných z klávesnice) na displeji je velice podobný principu zobrazení reklamy.
Po přijmutí a dekódování znaku z klávesnice máme v akumulátoru tzv. scan kód znaku (Ovšem pomineme-li různé vyjímky, zda jde o klávesy z rozšířené sady kláves, či o jiné - popsáno v sekci o dekódování dat přijmutých z klávesnice). Princip spočívá v tom, že máme data (tabulky) rozdělené do dvou částí, podle toho, zda jde o znaky normální nebo EXTENDED. Každá tato skupina má ještě dvě podskupiny (tabulky). Tyto tabulky jsou rozděleny podle toho, zda jde o jeden znak nebo tzv. víceznakou klávesu (na displeji se bude zobrazovat při stisku jedné klávesy krátký text, např. Při stisku klávesy END se na displeji zobrazí END). Vcelku jednoduchý princip.
Takže po stisku klávesy jsou nastaveny příznakové bity (zda jde o znak extended nebo ne) a v akumulátoru máme scan kód znaku. Podle příznakového bitu EXTEND načteme počátek odpovídající tabulky. V této tabulce jsou rozmístěny znaky na pozicích odpovídajícím scan kódům. Z odpovídající pozice se potom pouze načte zobrazovaný znak. V tabulce můžeme uvádět přímo znaky, protože displej umí naštěstí přímo přijímat v ASCII. Takto je velmi jednoduše vytvořena dekódovací tabulka, která překládá scan kódy na ASCII displeje.
Obrovským omylem pro nás bylo zjištění (které pramenilo z naší nepozornosti), že víceznakové znaky nelze použít v původní tabulce, kde byly jednotlivé znaky. Naše filozofie přístupu do tabulky byla podobná PASCALovskému přístupu do pole STRING. Uvedu malý příklad:

Tabulka např.: "a'", "f"' , "END'" , "x'" , "u'"

Náš původní program však předpokládal, že přijme-li od klávesnice 0, dekóduje se písmeno "a", přijme-li 1, dekóduje se písmeno "f", přijme-li 2, dekóduje se slovo "END", přijme-li 3, dekóduje se písmeno "x"… A TO BYL OBROVSKÝ OMYL, který nás opět neskutečně zbrzdil. Program totiž dekódoval první dva znaky správně (tj. 'a' a ‚f' a END), přijmul-li však procesor od klávesnice 3, na displeji se vypsalo ND (a ne x), přijmul-li 4, zobrazilo se D (místo u ) atd. Tato tabulka je však jenom pro příklad a je příliš jednoduchá. Ve skutečné tabulce je mnoho "prázdného místa" (tzn. nuly, které klávesnice nemůže nikdy vyslat) a je i bez dekódování příliš nepřehledná. Naším závěrem byla domněnka, že je chybná přijímací část dat z klávesnice. Skutečnost byla taková, že chybné byla naše interpretace tabulky. Teď už víme, že tabulka se ve skutečnosti nechová jako pole, ale jako posloupnost na sobě nezávislých dat, která mohou být prakticky jakákoli a ve kterých neexistují nějaké zákonitosti, natož dělení na buňky. Procesor tuto část chápe úplně stejně jako např. část programu (na kterou se však nikdy nedostane, kterou nikdy nebude vykonávat).
Po tomto zjištění jsme měli prakticky vyhráno a mohli jsme pokračovat dále. Zdržení, které jsme ovšem tímto nabrali jsme nemohli již dohnat a práci dokončit tak, aby vypadala podle našich představ.
Po těchto peripetiích jsme tedy museli rozdělit obě tabulky (EXTENDED i normální) na další dvě části. Jedna část v každé z nich obsahovala jednotlivé znaky a druhá část víceznaké sekvence. Tyto sekvence však museli být všechny stejně dlouhé, aby bylo možno vytvořit vzorec pro výpočet adresy, kde se nachází počátek hledané posloupnosti znaků, hledaného textu, hledané klávesy. Jako uspokojující a rozumnou délku jsme zvolili 7 znaků.
Nyní ale bylo nutné rozlišit, z jaké tabulky se má číst. Máme nastavené rozlišovací bity a v akumulátoru máme kód klávesy. Ze scan kódu se však délka poznat nedá a jednoznakové i víceznakové kódy jsou promíchané. Jak tedy na to? Napadla nás jedna myšlenka, kterou jsme zdárně dovedli až do konce. V tabulce znaků, které umí displej jsme našli hranici takových, které určitě nebudeme dekódovat a zobrazovat. Hranice byla číslo 192D. Všechny zobrazované jednoznakové klávesy měli ASCII kód menší než 192. Do tabulky jsme tedy zařadili na odpovídající pozice scankódů (víceznakových kláves) čísla od 192D až do potřebné výše.
Při dalším dekódování se tedy nejprve zjistí, zda je ASCII kód zobrazovaného znaku větší nebo roven 192D. Pokud ano, nezobrazí se a pokračuje se výpočtem pozice víceznakového spojení v tabulce podle vzorce:

(A-192D)*7D = pozice počátku víceznakového spojení

kde A je akumulátor (,který obsahuje číslo, které odpovídá scan kódů a koresponduje s víceznakou tabulkou), 192D je konstanta kterou je nutné odečíst, abychom dostali posloupnost od 0 a 7D je délka každé "buňky" ve víceznaké tabulce.
Po tomto výpočtu máme v akumulátoru adresu pozice požadovaného víceznakového spojení z víceznaké tabulky. Následuje podobný princip "nasypání" textu do displeje, jako u pevné reklamy.
Díky použití vzorce a umístění víceznakých textů do tabulky, je nutné, aby každý samostatný text měl v tabulce stejnou délku. To jsme vyřešili tak, že jsme každé slovo doplnili na délku 7 znaků znakem "'" (apostrof). Narazí-li procedura, která zapisuje na display na tento znak, vyskočí se z ní a další apostrofy se ignorují (mají pouze funkci výplně, aby byla zachována struktura tabulky).
Tento princip byl použit u tabulky s EXTENDED znaky a i u tabulky s normálními znaky. Rozhodování, která skupina tabulek se použije se provádí podle nastavení bitu EXTEND. Dále se provede rozhodnutí, zda jde o jeden znak, či skupinu znaků (>=192). Pokud jde o jeden znak, zapíše se na displej a vyskočí se z přerušovací procedury. Pokud jde o více znaků, provede se výpočet adresy počátku sekvence znaků odpovídající tabulky (víceznaková EXTENDED nebo víceznaková normální). Z této adresy se potom "sype" sekvence na displej.
Malé zjednodušení by mohlo nastat v souvislosti s tabulkou EXTENDED znaků. Ani jedna klávesa EXTENDED totiž nemá jednoznakový výstup. Pokud bychom ale udělali každou buňku tabulky EXTENDED 7mi znakovou, nevešel by se program díky této tabulce do procesoru.

Seznamujeme se s klávesnicí:
Klávesnic je několik různých druhů, jak jsme postupně zjistili některé klávesnice mají data platná při náběžné, jiné při sestupné hraně, některé potřebují počáteční inicializaci, některé ne, liší se i dokonce ve způsobu kódování jedlotlivých kláves, ani rychlost hodinového signálu není všude stejná. Kdo ví, jak se v tom může počítač vyznat... Naše klávesnice měla data platná při náběžné hraně. Co se týče kódování, na HW serveru byly popsány dva druhy, ani jeden však nebyl ten náš. Toto jsme zjistili až po velmi dlouhé době. S inicializací klávesnice jsme podnikali také různé experimenty jak bude popsáno níže.

Připojujeme klávesnici:
Klávesnice je připojena pomocí konektoru DIN 5P VK.

Konektor klávesnice
Obr. 3 Rozmístění jednotlivých signálů na špičkách konektoru DIN 5P VK

CLK: hodinový signál, který synchronizuje komunikaci mezi mikroprocesorem a klávesnicí
DATA: při sestupné hraně signálu CLK jsou na této špičce platná data
RESET: je pozůstatek z dob starých XT klávesnic, dnes už se nepoužívá
GND: signálová a napájecí zem
Ucc: klávesnice je napájena z napětí +5V, úrovně signálů jsou kompatibilní s úrovněmi obvodů CMOS

Signál CLK jsme připojili na port externího přerušení INT0 P3.2 přes tranzistorový invertor z důvodu, že DATA jsou platná pouze při náběžné hraně signálu CLK a mikroprocesor AT89C2051 umí reagovat jen na hranu sestupnou. Klávesnice vysílá data tak, že při stisku klávesy se na této špičce generují hodiny o frekvenci 10 až 20kHz. Při každé už invertované náběžné hraně se vyvolá externí přerušení, kde načítáme stav špičky DATA, která je zapojena na port P3.7.

Obr. 4 Časové průběhy při komunikaci klávesnice a počítače
Časové průběhy
Obsluhujeme klávesnici:
Komunikuje se vždy po 11 bitech. Nejprve je vyslán StartBit (logická nula), poté 8 datových bitů (první je LSB, poslední MSB), dále parita a nakonec StopBit (logická jedna). V přerušovací proceduře si při prvním přerušení vynuluji počet přijatých bitů, který při každém dalším přerušení inkrementuji. Pokud se jedná o bity 1 až 8 (0 byl start bit) přečtu stav datového vodiče do jednoho znaku, další 9 bit ignoruji, po 10 bitu provádím dekódování přijatého znaku.Toto řešení s využitím přerušení se dá použít pro jakékoli jednočipové procesory a jeho velikou výhodou je minimální režie, kterou si klávesnice z celého systému zabere.
Minimální mezera mezi dvěma kódy vyslanými po sobě je 1,2 ms.
Tato synchronní sériová komunikace je obousměrná, to znamená že stejným způsobem můžeme vysílat data i do klávesnice. Příjem znaku z klávesnice probíhá při stisku klávesy a vysílani znaku probíha při vzniku požadavku zaslání řídícího znaku do klávesnice. Změna dat se tedy musí provádět při sestupné hraně. Při vyslání znaku z klávesnice řídí signál DATA klávesnice, při vysílání znaku do klávesnice řídí signál DATA mikroprocesor. V případě požadavku na vyslaní řídicího znaku do klávesnice je nutné signál DATA stáhnout na uroveň logické nuly a klávesnice si sama začne generovat hodinové pulsy na signálu CLK. Potom stačí tento řídící signál CLK sledovat a poslat mu data jako při příjmu.

Řídící kódy vyslané z mikropočítače do klávesnice :
(v hexadecimálnim formátu)

FFH reset klávesnice - spustí se power-on test
FEH žádost o zaslání posledního zaslaného scan kódu klávesy
FAH potvrzení - ACK
F6H obnovení implicitního nastavení klávesnice
F5H implicitní zablokovaní klávesnice - provede reset klávesnice, vrací kód ACK (FAH, provede přerušení scanovaní klávesnice a čeká na další příkaz
F4H odblokovaní klávesnice - nuluje výstupní buffer, odblokuje klávesnici a vrací kód (FAH)
EEH echo - klávesnice odpoví zpět také EEHjako echo - pro test
F2H čtení ID klávesnice - klávesnice odpoví ACK se dvěmi ID byty (83H, ABH) a pokračuje ve scanovaní po předcházejícím zablokovaní klávesnice
EDH zapnutí nebo vypnutí LED indikátoru klávesnice
b0 - Scrollock - 1 zapnuto/0 vypnuto
b1 - Numlock - 1 zapnuto/0 vypnuto
b2 - Capslock - 1 zapnuto/0 vypnuto
b3..b7 - 0

Řídící kódy vyslané z klávesnice do mikropočítače :
(v hexadecimálnim formátu)

FFH přetečení bufferu,klávesnice detekuje chybu
FEH žádost o zaslaní posledního zaslaného znaku, špatně přijatý příkaz, parita apod.
FAH potvrzení - ACK
F0H kód uvolnění klavesy
AAH uspěšný power-on test
EEH echo - klávesnice odpoví zpět také EEH jako echo - pro test
00H přetečení bufferu,klávesnice detekuje chybu

Z hlediska kódování kláves klávesnicí je možné rozdělit klávesy do tří skupin. Jsou to skupina základní, skupina rozšířená a skupina speciální. Do skupiny základní patří 83 kláves. Při stisku některé z těchto kláves je vyslán kód této klávesy a po vypuštění je vyslán kód vypuštění a znovu kód vypuštěné klávesy. Při delším stisku je kód klávesy neustále vysílán až do vypuštění. Kód klávesy v hranatých závorkách se vysílá dokud je klávesa stisknuta.

Obr.5 Sekvence základní skupiny vysílaná klávesnicí
Sekvence základní skupiny
Skupina rozšířená zahrnuje 14 kláves. Tyto klávesy mají kódy shodné s klávesami numerické klávesnice, patřících do základní skupiny, ale před tímto kódem je předřazen kód F8, který je od těchto odlišuje. Dva kódy v hranatých závorkách se vysílají dokud je klávesa stisknuta.

Obr. 6 Sekvence rozšířené skupiny vysílaná klávesnicí
Sekvence rozšířené skupiny
Speciální skupina zahrnuje všechny ostatní klávesy. Jedná se o klávesy Pause a Print Screen. Ty vysílají ještě různé kombinace kódů při stisku Ctrl + Print Screen, Alt + Print Screen a Ctrl + Break.

Při současném stisku dvou a více ostatních kláves (základní a rozšířená skupina) jsou kódy vysílány v odpovídajícím pořadí v jakém se jednotlivé akce stisku a vypuštění stanou a tyto kódy se vzájemně nijak neovlivňují, kromě případu kdy je přerušeno cyklické vysílání kódu klávesy (autorepeat). To nastane, když je jedna klávesa dlouho stisknuta a cyklicky vysílá a poté je stlačena jiná. Pak se přestane vysílat kód dříve stlačené klávesy. Kódy všech kláves jsou uvedeny v tabulce na konci této kapitoly.

Obr. 7 Rozmístění kláves a názvy jejich kódů
Rozložení kláves a jejich kódy

Na obrázku je klávesnice se jmény kódů, které byly zvoleny a při jejich volbě bylo většinou použito potisku klávesy. Klávesy, které jsou zdvojené, jsou rozlišeny předponou před jménem. N pro klávesy na numerické části a R pro ostatní. Každá klávesa má svou vlastní nezaměnitelnou kombinaci kódů.

SCAN kódy kláves:
Základní skupina:

Klávesa

Kód

Klávesa

Kód

Klávesa

Kód

`

8F

S

27

F1

5F

1

97

D

3B

F2

9F

2

87

F

2B

F3

DF

3

9B

G

D3

F4

CF

4

5B

H

33

F5

3F

5

8B

J

23

F6

2F

6

93

K

BD

F7

3E

7

43

L

2D

F8

AF

8

83

;

CD

F9

7F

9

9D

'

B5

F10

6F

0

5D

Shift

B7

F11

E1

-

8D

Z

A7

F12

1F

=

55

X

BB

Scroll lock

81

Backspace

99

C

7B

Num lock

11

Tab

4F

V

AB

*

C1

Q

57

B

B3

N-

21

W

47

N

73

+

61

E

DB

M

A3

N0

F1

R

4B

,

7D

N1

69

T

CB

.

6D

N2

B1

Y

53

/

AD

N3

A1

U

C3

Pshift

65

N4

29

I

3D

\

79

N5

31

O

DD

Ctrl

D7

N6

D1

P

4D

Alt

77

N7

C9

[

D5

Space

6B

N8

51

]

25

Enter

A5

N9

41

Caps lock

E5

Esc

91

N.

71

A

C7


Rozšířená skupina:

Klávesa

Kód

Odpovídá zkrácenému

Ralt

77

Alt

Rctrl

D7

Ctrl

Ins

F1

N0

Home

C9

N7

Pgu

41

N9

Del

71

N.

End

69

N1

Pgd

A1

N3

Left

29

N4

Right

D1

N6

Up

51

N8

Down

B1

N2

/

AD

/?

Nenter

A5

Enter


Speciální skupina:

Klávesa

Kód

Print screen

F8 B7 F8 C1

Break

78 D7 11 F0 D7 F0 11


Přijímáme a dekódujememe znaky:
Nejprve bych se chtěl zastavit nad otázkou inicializace klávesnice. Na klávesnici jsme zkoušeli posílat různé stringy včetně inicializačního FFH, ale klávesnice reagovala pořád stejně. Zjistili jsme, že klávesnice se inicializuje sama po zapnutí napájení. Signalizuje to probliknutím LED diod a ještě navíc sama posílá do mikroprocesoru kód 0FFH. V programu ignorujeme tento string časovou smyčkou. Z toho plyne, že naše klávesnice nepotřebuje softwarově inicializovat.
Teď už slíbené přijímání znaků. Na začátku hlavního programu nastavím bit EA, který povoluje všechny přerušení. Dále bit EX0, který povoluje externí přerušení INT0, na které je připojen signál CLK a bit IT0, aby mikroprocesor reagoval na sestupnou hranu, nikoliv na úroveň signálu.
Dále už vše probíhá v přerušovací proceduře. Při stisku klávesy se vyvolá přerušení, kde čtu stav signálu DATA. Na poprvé je to startbit, ten ignoruji, dalších 8 přerušení jsou datové bity, které pomocí příkazu RLC postuně zapisuji do akumulátoru, paritu ignoruji a při stopbitu provádím dekódování.
Myslím si, že paritu není nutné kontrolovat, protože přenos dat z klávesnice v tomto případě nemá vliv na další činnosti nějakého systému a neovlivňuje další zařízení. Jde jenom o zobrazení dat na displeji. Navíc parita rozpozná chybu pouze v jednom bitu. V naší aplikaci, v našem způsobu dekódování je velmi pravděpodobné, že pokud nastane chyba přenosu, načte se z tabulky nula, která se ignoruje a nezobrazuje se. Data v dekódovací tabulce jsou navíc rozmístěny s velmi malou hustotou informace (tzn. je zde více buněk, které nenesou žádnou informaci, než -li buněk s informací). Zvyšovat tuto hustotu nebyl důvod, protože místa v paměti procesoru bylo dost. Byl by to však podnět na zamyšlení… (že by kompresní algoritmus?)
Když už mám přijatý kód klávesy, nejprve porovnám, jesli kód není F0H (kód puštění klávesy). Za tímto kódem následuje opět kód znaku. Oba ignoruji. Dále zjišťiji, jestli kód není F8H (z rozšířené skupiny). Když ano, nastavuji bity EXTEND a EXTEND2 a ignoruji všechny nepotřebné kódy (F8H ,F8H, F0H a kód klávesy). Pak v závislosti na bitu EXTEND čtu z dekódovacích tabulkek jak je popsáno výše.
Jakmile mám dekódovaný znak zapisuji přímo do DDRAM displeje pomocí procecury WRLCD.

Zhodnocení:
I přes mnoho problémů se nám podařilo úlohu uspokojivě vyřešit. Aby ale nebylo problémů málo, měli jsme jich ještě několik s překladačem. Po vytvořeni tabulky jsme měli v sekci čísel (které jsou nahoře, pod funkčními) jako znaky odpovídající čísla. Program takto bezchybně fungoval. My jsme se ovšem rozhodli udělat malou kosmetickou úpravu: za čísla jsme do tabulky dosadili znaky, které se vypisují na PC se stisknutou klávesou SHIFT (tj. !@#$%^*()) (bohužel tato změna nebyla jediná, v tabulce jsme prováděli více kosmetických změn, které nás později ještě více zmátly). Předpokládali jsme, že to bude pouze kosmetická změna, která nebude mít vliv na chod programu. Byl to ale omyl. Po dosazení těchto znaků se začal program chovat opět poněkud "splašeně". Padlo podezření opět na přijímaní dat z klávesnice. Po mnoha pokusech jsme se vrátili opět k poslední funkční verzi a začali znovu kousek po kousku provádět ony kosmetické změny.
Nakonec jsme přišli na to, že překladač bere znak procent (přesto, že byl v apostrofech) nějak jinak než jako znak. Znak procent jsme proto nahradili číslem 37D, což je ASCII kód pro procenta. Zajímavá chyba…
Překladač nás ještě trápil s tím, že odmítal skákat instrukcí JMP dál než o zhruba 100 řádků. Museli jme mu udělat jakýsi "meziskok", kterým se tato vzdálenost snížila.
Při našich prvních neúspěšných pokusech z klávesnicí jsme dospěli k závěru, že se asi bude muset klávesnice nejprve inicializovat. Klávesnici jsme proto inicializovali, ale ta se chovala naprosto stejně. Po napsání a odladění celého programu jsem se odhodlal k pokusu s mojí klávesnicí, která je poměrně nového data výroby (na rozdíl od naší pokusné školní). A ouha klávesnice vůbec nereagovala. Proto jsem dospěl k závěru, že některé klávesnice se inicializovat musí a některé ne. Záleží na výrobci, datumu výroby atd. Mimochodem nedávno jsem se na Internetu dočetl, že některé klávesnice mají platná data při náběžné hraně a některá při sestupné. Chudák počítač… Proto náš program určitě není univerzální (ani nemůže být) a bude fungovat jenom s určitým typem klávesnice.
Jen tak pro zajímavost by se ještě zmínil o vytváření dekódovacích tabulek. Z Internetu jsme měli stažený popis vysílaných kódů v textové formě. Využili jsme proto znalostí jazyka pascal a místo otrockého vytváření tabulky jsme otrocky vytvářeli pascalovský program, který dekódovací tabulky vytvořil z textového souboru za nás.

Celkové schéma zapojení modulu
kliknutím na obrázek získáte schéma zapojení v plné velikosti
Náhled schématu zapojení

Seznam použitých součástek
(značení součástek dle katalogu GM Electronic)

Položka

Množství

Referenční označení

Typ

1

1

C1

E 10uF/16V

2

2

C2,C3

CK 33pF/500V

3

1

C4

E 1000uF/16V

4

1

J1

svorkovnice ARK 110/2 (napájení)

5

1

J2

konektor DIN 5P ZP

6

1

J3

jednořadá objímka AW 14

7

1

J4

jednořadá objímka AW 10

8

1

U1

Atmel AT89C2051

9

1

Y1

krystal 12 MHz

10

1

R1

8k2

11

3

R2,R3,R4

1k2

12

1

R5

trimr 50k

13

1

D1

LED červená 3mm

14

1

S1

tlačítko P-B1720

15

1

T1

KC 508

16

1

-

displej MC1602E-SYL

17

1

-

patice SOKL 20


Program ASM  Výpis programu pro mikrokontroler AT89C2051 (displej.asm)

Zpět na hlavní stránku