<

Alfanumerikus LCD kijelzők vezérlése

A fejezet tartalma:

Alfanumerikus LCD modulok programozása

Ha már megismertük az I/O portok használatát, akkor nyitva áll az út ahhoz is, hogy alfanumerikus (szöveges információ megjelenítésére alkalmas) LCD kijelző modulok  vezérlésével foglalkozzunk. Folyadékkristályos kijelzőkkel (LCD = Liquid Crystal Display) a mindennapi életben számos területén találkozunk, hiszen elterjedten alkalmazzák különféle készülékek (pl. zsebszámológépek, lézernyomtatók, digitális mérlegek, digitális karórák, kenyérsütők, telefonok, italautomaták, digitális lázmérők stb.) kijelzőjeként

A címben is említett "kijelző modul" kifejezés arra utal, hogy mi nem közvetlenül az LCD kijelző szegmenseit vezéreljük, hanem olyan LCD-t használunk, ami egybe van építve az ipari szabványnak tekinthető HD44780, vagy azzal kompatibilis IC-re épülő vezérlő áramkörrel is, ami az LCD kijelzőhöz erősített nyomtatott áramköri lapon helyezkedik el.

Az alfanumerikus LCD modulok bekötése

A háttérvilágítással rendelkező kijelzőknek összesen 16 kivezetése van, a reflexiós típusúaknak pedig csak 14. Előfordul azonban, hogy a  nem használt 15. és 16. kivezetést is elkészítik, hogy a panel csatlakozója kompatibilis legyen a háttérvilágítással rendelkező kijelzőkével. Az alábbi képen egy tipikus 16x2 karakteres (két sorban, soronként 16 karakter megjelenítésére képes) kijelző lábkiosztását mutatjuk be.


1. ábra: Egy tipikus 16x2 LCD modul lábkiosztása

Az LCD modul kivezetéseinek funkcióit az alábbi táblázatban foglalhatjuk össze:

1. táblázat: Az LCD modul kivezetései
Sorszám Jel Funkció
1. VSS A tápegység negatív sarka, GND
2. VCC Tápfeszültség (többnyire +5 V)
3. VEE Kontraszt szabályozó jel (többnyire 0,5 V körül)
4. RS Parancs/adat választó (Register Select
5. R/W Olvasás/írás (adatáramlási irány választása)
6. E Enegedélyező jel (Enable)
7. D0 Adatbusz 0.bit
8. D1 Adatbusz 1.bit
9. D2 Adatbusz 2.bit
10. D3 Adatbusz 3.bit
11. D4 Adatbusz 4.bit
12. D5 Adatbusz 5.bit
13. D6 Adatbusz 6.bit
14. D7 Adatbusz 7.bit
15. A A háttérvilágítás pozitív sarka (LED anód)
16. K A háttérvilágítás negatív sarka (LED katód)
A D7-D0 lábak a kétirányú adatvonalak,amelyeken keresztül a mikrovezérlő és az LCD modul adatokat cserél. A HD44780 vezérlőnek kívülről két regiszterét érhetjük el. A parancsregiszterbe a vezérlő utasításokat tölthetjük bele (pl.képernyőtörlés, kurzor mozgatása, üzemmód beállítása, stb). Az adatregiszterbe a kiírandó karakterek kódját, vagy a felhasználó által definiálható karakterek bitképét írhatjuk be. A regiszterek közötti választás az RS vonal (RS = Register Select) beállításával történhet: ha az RS lábra magas szintet kapcsolunk (logikai '1'), akkor az adatregisztert, ha pedig alacsony szintet (logikai '0') kapcsolunk, akkor a parancsregiszter választhatjuk ki.

Az R/W vonalon az adatáramlás irányát állíthatjuk be: R/W = 0 írást, R/W = 1 olvasást jelent. Az E (Enable) vonal alaphelyzetben alacsony (logikai '0') szinten van. Az adatvonalak és az RS, R/W vezérlő vonalak beállítása után rövid időre az E vonalat magas szintre, majd újra alacsony szintre állítjuk (pulzálás). A tényleges adatátvitel ekkor, az E jel magas szintre váltásakor történik. Ügyelnümk kell az időzítések betartására.


 2. ábra: A HD44780 vezérlő jeleinek ídőzítése íráskor


A tAS (Address Set = cím beállás) és a tAH (Address Hold = cím tartása) előírt minimális idejének kivárása, valamint az E jel magas szinten történő tartása érdekében rövid késleltetési ciklusokat kell beiktatnunk az E jelet beállító utasítások elé, közé és után! Más típusú (HD44780 kompatibilis) vezérlő esetén azonban a 4. ábrán láthatótól eltérő időzítést is előírhat az adott vezérlő adatlapja.

Az alábbi ábrán bemutatjuk, hogy az LCD modult hogyan kapcsoljuk össze a kísérleti áramkörünkkel.


3. ábra: Az LCD modul bekötési vázlata (4 bites adatút esetén) 

Megjegyzések:

Négybites bekötés

Az LCD modulok HD44780 vezérlője a 3. ábra szerinti kapcsolásban 4-bites üzemmódban működik:
További egyszerűsítést jelentene az, ha a foglaltság figyelése helyett fix idejű késleltetéssel (min 40 us) várnánk ki, amíg az LCD vezérlője a legutoljára kiküldött parancsot végrehajtja. Ilyenkor az R/W bemenetet nem kell a mikrovezérlővel állítgatni, fixen földre köthetjük (logikai '0'). Mi azonban az alább bemutatott példaprogramokban nem élünk ezzel az egyszerűsítési lehetőséggel, mert azt is be kívánjuk mutatni, hogyan kell figyelni az LCD vezérlőjének foglaltságát jelző BUSY jelzőbitet.

Figyelmeztetés: Vigyáznunk kell arra, hogy az adatvonalakat 5 V-ot is toleráló kivezetésekre kössük, hogy a foglaltság ellenőrzésekor az 5 V-os tápfeszültségű LCD modulból érkező jelek ne károsítsák a 3,3 V-os mikrovezérlőt! Ha nem tudunk 5 V-ot is toleráló kivezetésket használni az LCD modul adatvonalaihoz, akkor válaszzuk mégis  a fix késleltetésű módot, s kössük az LCD modul R/W bemenetét a földre. Így 3,3 V-os jelszinttel dolgozó mikrovezérlőt is használhatunk az LCD modul vezérlésére, s nem kell attól tartanunk, hogy az LCD modul olvasáskor 5 V-os jeleket ad a mikrovezérlőnek az 5 V-os jelszintet nem toleráló bemeneteire (lásd: Explorer16 Demo Board Errata).

Az alfanumerikus LCD modul vezérlőjének parancskészlete

HD44780 vezérlő által elfogadott parancsokat az alábbi táblázatban foglaltuk össze, ahol a nyolc adatbiten kívül az RS és az R/W vezérlő jelek állapotát is feltüntettük. Vegyük észre, hogy a parancsokat a legmagasabb helyiértéken található 1-es azonosítja (lásd az alábbi táblázat átlóját!). A parancs esetleges paraméterei az aacsonyabb helyiértékeken helyezkednek el. A "*" jellel megjelölt pozíciók értéke közömbös (don't care bit).

2. táblázat: A HD44780 vezérlő parancskészlete
RW RS D7 D6 D5 D4 D3 D2 D1 D0 Funkció
0 0 0 0 0 0 0 0 0 1 Clear display: Törli a képernyőt és alaphelyzetbe helyezi a mutatót (0 cím)
0 0 0 0 0 0 0 0 1 * Cursor home: Alaphelyzetbe állítja a mutatót és a képernyő eltolást.
0 0 0 0 0 0 0 1 I/D S Entry mode set: Beírási mód (I/D = mutató léptetés, S= képernyő eltolás) beállítása. 
0 0 0 0 0 0 1 D C B Display control: Display ki/be (D), mutató ki/be (C), villogó kurzor ki/be (B)
0 0 0 0 0 1 S/C R/L * * Shift cursor/screen: Mutató vagy a képernyő (S/C) eltolása jobbra/balra (R/L)
0 0 0 0 1 DL N F * * Function set: adatút-szélesség (DL), sorok száma (N) és fontméret (F) beállítása
0 0 0 1 A5 A4 A3 A2 A1 A0 CGRAM cím beállítása
0 0 1 A6 A5 A4 A3 A2 A1 A0 DDRAM cím beállítása
1 0 BF A6 A5 A4 A3 A2 A1 A0 Read busy flag: foglaltság figyelése
0 1 D7 D6 D5 D4 D3 D2 D1 D0 Write data: adat írása a korábban megcímzett CGRAM vagy DDRAM területre
1 1 D7 D6 D5 D4 D3 D2 D1 D0 Read data: adat olvasása a korábban megcímzett CGRAM vayg DDRAM területről
A paraméterek jelentése az alábbi táblázatból olvasható ki:

3. táblázat: A fenti parancsok paramétereinek jelentése
I/D 0: csökkent a kurzor pozícióját 1: növeli a kurzor pozícióját
S 0: nincs képernyő eltolás 1: van képernyő eltolás
D 0: megjelenítés letiltása 1: megjelenítésengedélyezése
C 0: kurzor megjelenítés letiltása 1: kurzor megjelenítés engedélyezése
B 0: kurzorvillogás letiltása 1: kurzorvillogás bekapcsolása
S/C 0: kurzor mozgatása 1: képernyő eltolása
R/L 0: balra mozgatás 1: jobbra mozgatás
DL 0: 4 bites adatáramlás 1: 8 bites adatáramlás
N 0: egysoros megjelenítés 1: kétsoros megjelenítés
F 0: 5x7 font 1: 5x10 font
BF 0: parancsfogadásra kész 1: foglalt, belső művelet végrehajtása zajlik

Az LCD vezérlő utasításai kicsit részletesebben

A Clear Display utasítás szóközzel tölti fel az adatmemóriát (DDRAM). Utána nullára állítja a címregiszter és alaphelyzetbe állítja a képernyőeltolást. Más szavakkal: törli a képernyőt és a kurzor a bal felső sarokban jelenikmeg (ha láthatónak van beállítva). Az I/D bitet pedig növekményes módba állítja (a kurzor pozíció eggyel nő minden beíráskor).

A Cursor Home utasítás nullára állítja a címregisztert és a képernyő eltolást. A DDRAM tartalma nem változik. a kurzor a bal felső sarokba kerül.

Az Entry mode Set utasítás a beviteli módot állítja be. Az I/D bit értéke szabja meg, hogy karakter beíráskor a kurzor jobbra lépjen (I/D=1) vagy balra lépjen (ennek megfelelően a következő karakter kórja a DDRAM következő, eggyel nagyobb vagy kisebb című rekeszébe kerüljön). A beállított kurzorléptetési irány vonatkozika CGRAM-ba történő írásra is. Az S bit a képernyő eltolását szabályozza. A képernyő eltolását az S = 1 beállítás engedélyezi. Egyébként a képernyő nemtolódik el. Ha a képernyő eltolást engedélyezzük, úgy látjuk, mintha a kurzor nem mozdulna el beíráskor, a képernyő viszont igen. A képernyő nem tolódik el, amikor olvasunk a DDRAM memóriából. Nem tolódik el aképernyő a CGRAM írása vagy olvasása esetén sem.
 
A Display Control utasításnak három paramétere van: D, C és B, amelyekel a megjelenítést (D=display), a kurzort (C=cursor) és a kurzor villogását (B=blink) kapcsolhatjuk ki (0) vagy be (1).

A Shift Cursor/Screen utasítással a  kurzort vagy a képernyőt tolhatjuk jobbra vagy balra, anélkül, hogy az adatmemóriár (DDRAM) írnánk vagy olvasnánk. Az S/C paraméter '0' értéke a kurzot, '1' értéke a képernyőt választja ki, az R/L bit pedig a léptetés irányát szabja meg (0: balra, 1: jobbra). Eltoláskor a címregiszter (a következő beírás helye a memóriában) nem változik meg.

A Function Set utasításnak nagyon fontos szerepe van: az LCD üzemmódját állítja be a vezérlő inicializálásakor.
Fontos tudni, hogy az üzemmód inicializálásonként csak egyszer állítható be! DL az adatút szélességét állítja be (0: 4 bites, 1: 8 bites). Ha 4 bites üzemmódot állítunk be, akkor a D7, D6, D5, D4 adatvonalakat kell használni, s a kiírandó adatnak először a magasabb helyiértékű felét írjuk ki, ezt követően pedig az alacsonyabb helyiértékű felét. Az N paraméter a sorok számát szabja meg: N=0 egysoros mód, N=1 kétsoros mód. Az F paraméter '0' értéke az 5x7-s fontot, az '1' értéke pedig az 5x10-es fontot választja ki.

A CGRAM cím beállításására akor van szükség, ha a felhsználó által definálható karakterek bitképét akarjuk feltölteni. A 0. címre írjuk az első karakter legfelső pontsorát (csak az alsó 5 helyiérték bitje számít), az 1.címre az első karakter felülről második pontsorát, és így tovább.

A DDRAM cím beállítását a kurzor pozicionálására használhatjuk. Például kétsoros megjelenítőnél a 0x40 címen kezdődik a második sor. Egy lcd_gotoxy(sor,oszlop) kurzorpozicionáló függvény pedig a sor*0x40+oszlop címre kell, hogy állítsa a DDRAM címet. (A példáknál feltételeztük, hogy nincs eltolva a képernyő, s az lcd_gotoxy() függvény paramétereinél a sorok és oszlopok számozását 0-tól kezdjük.

Az LCD alfanumerikus modul inicializálása

Az LCD modul vezérlője a tápfeszültség megjelenésekor inicializálja magát (Power on Reset), de ha bekapcsoláskor túl lassú a tápfeszültség felfutása, vagy bekapcsolt állapotban van szükségünk újrainicializálásra ("meleg" RESET), akkor szoftveres parancsokkal is kikényszeríthatjük, hogy az LCD modul alaphelyzetbe kerüljön. Ennek pontos módját a HD44780 vezérlő adatlapja írja le, s van néhány apró különbség a nyolc-, illetve négybites üzemmódban végzett inicializálás között. Az LCD használatának talán ez a legkritikusabb része, mivel helytelen inicializálás után nem fog működni a modul, vagy zagyvaságok jelennek meg a képernyőn.

Inicializálás 8 bites üzemmódban

  1. Várunk legalább 15 ms-ot azután, hogy a tápfeszültség elérte a  4,5 V-ot, majd RS=0, R/W=0 valamint D7...D4 = 0b0011 mellett (D3..D0 beállítása itt közömbös) pulzáljuk az E vezérlőjelet (E-t előbb magas szintre, majd alacsony szintre kapcsoljuk). Magyarul ez azt jelenti, hogy az LCD-nek kiadjuk a Function set parancsot DL=1 beállítással. A foglaltságot (BF bit) most nem vizsgálhatjuk!
  2. Várunk legalább 4,1 ms-ot, majd ismét kiadjuk a fenti módon a Function set parancsot. A foglaltságot (BF bit) most nem vizsgálhatjuk!
  3. Várunk legalább 100 µs-ot, és harmadszor is kiadjuk az 1. pontban leírt módon a Function set parancsot. A foglaltságot (BF bit) most sem vizsgálhatjuk!
  4. Újabb 100 µs-os várakozás után negyedszer is kiadjuk a Function set parancsot, ezúttal azonban a D3 és D2 adatbitek tartalma is számít (N és F paraméterek, lásd 2.és 3. táblázat!).  A 8 bites üzemmódra való tekintettel DL=1, kétsoros kijelzőnél pedig N=1 és F=0 (kétsoros kijelzőnél csak 5x7 pontos karakterek használhatók). Végeredményben tehát esetünkben az adatvonalakra 0x38-et kell küldeni. Ezután a parancs után már figyelhető a foglaltság. A sorok száma és a fontméret csak újrainicializálás után változtatható meg!
  5. Ideiglenesen letiltjuk a megjelenítést egy Display control paranccsal, D=0 beállítással. 
  6. Töröljük a képernyőt egy Clear Display paranccsal.
  7. Újraengedélyezzük a megjelenítést egy Display control paranccsal, D = 1 beállítással. Ugyanitt engedélyezhetjük a kurzor megjelenítését és villogását is.
A fentiekhez hasonlóan történik a 4 bites üzemmód beállítása is, de a fenti 4. pont helyett itt két lépést kell beiktatni, melyekben már DL=0 beállítás szerepel, ami a 4 bites üzemmódot állítja be. A másik nyilvánvaló különbség,  hogy a lenti lista 5. pontjától kezdődően (a 4 bites üzemmódra történő kapcsolástól kezdve) az adatokat vagy parancsokat két részletben, félbájtonként kell kiküldeni.

Inicializálás 4 bites üzemmódban

  1. Várunk legalább 15 ms-ot azután, hogy a tápfeszültség elérte a  4,5 V-ot, majd RS=0, R/W=0 valamint D7...D4 = 0b0011 mellett (D3..D0 beállítása itt közömbös) pulzáljuk az E vezérlőjelet (E-t előbb magas szintre, majd alacsony szintre kapcsoljuk). Magyarul ez azt jelenti, hogy az LCD-nek kiadjuk a Function set parancsot DL=1 beállítással. A foglaltságot (BF bit) most nem vizsgálhatjuk! Megjegyzés:az LCD itt még 8 bites módban van.
  2. Várunk legalább 4,1 ms-ot, majd ismét kiadjuk a fenti módon a Function set parancsot. A foglaltságot (BF bit) most nem vizsgálhatjuk! Megjegyzés:az LCD itt még 8 bites módban van.
  3. Várunk legalább 100 µs-ot, és harmadszor is kiadjuk az 1. pontban leírt módon a Function set parancsot. A foglaltságot (BF bit) most sem vizsgálhatjuk! Megjegyzés:az LCD itt még 8 bites módban van.
  4. Újabb 100 µs-os várakozás után negyedszer is kiadjuk a Function set parancsot, ezúttal azonban D4 = 0 legyen (DL = 0)! A D3..D0 bitek állapota még mindig közömbös. Amikor ezt a parancsot kiadjuk, az LCD még 8 bites módban van. Ennek a parancsnak a hatására kapcsol át 4 bites módba. A következő ponttól kezdődően tehát a parancsokat két részletben (előbb a magas helyiértékű félbájt, majd az alacsonyabb) kell kiküldeni. 
  5. Újabb 100 µs-os várakozás után ötödször is kiadjuk a Function set parancsot, ezúttal azonban a D3 és D2 adatbitek tartalma is számít (N és F paraméterek, lásd 2.és 3. táblázat!).  A 4 bites üzemmódra való tekintettel DL =0, kétsoros kijelzőnél pedig N=1 és F=0 (kétsoros kijelzőnél csak 5x7 pontos karakterek használhatók). Végeredményben tehát esetünkben az adatvonalakra 0x28-at kell küldeni, két részletben. Ezután a parancs után már figyelhető a foglaltság. A sorok száma és a fontméret csak újrainicializálás után változtatható meg!
  6. Ideiglenesen letiltjuk a megjelenítést egy Display control paranccsal, D=0 beállítással. 
  7. Töröljük a képernyőt egy Clear Display paranccsal.
  8. Újraengedélyezzük a megjelenítést egy Display control paranccsal, D=1 beállítással. Ugyanitt engedélyezhetjük a kurzor megjelenítését és villogását is.

Megjegyzés: A Tankönyv mintapéldáiban a szerzők nemes egyszerűséggel arra hagyatkoznak, hogy az LCD modul vezérlője a tápfeszültség megjelenésekor inicializálja magát (Power on Reset), tehát az initLCD() eljárásban csak az "Inicializálás 4 bites módban" c. adatlapi ajánlás 4. pontjától, az üzemmód beállításától kezdődő lépéseket hajtja végre (azt sem pontosan úgy...). Az adatlap ajánlása szerinti szabályos inicializálást a PIC-kwik projekt keretében kidolgozott lcd4bit_cgram.c mintaprogram lcd_init() eljárásában láthatunk.

2x16 karakateres LCD kijelző vezérlése

Az alábi program az ipari szabványnak tekinthető, párhuzamos illesztővel ellátott 2x16 karakateres LCD kijelző használatát mutatja be, 4 bites módban. A főprogramban két sornyi szöveget kiírunk (mindkét sor hosszabb, mint 16 karakter, tehát egyszerre nem férnek ki a kijezőre). A végtelen ciklusban pedig ciklikusan balra görgetjük a képernyőt, majd egy rövid várakozás után frissítjük az életjelző LED állapotát. A szöveg fényújság-szerű görgetését az teszi lehetővé, hogy az LCD modul Hitachi HD44780  vezérlője 80 karakter tárolására alkalmas RAM-mal rendelkezik, s balra léptetésnél az eredetileg nem látszódó pozícióban levő karakterek is előtűnnek. 

Tekintettel arra, hogy az LCD kijelző 5V-os tápfeszültségű, kizárólag a "csak digitális funkciójú", tehát az 5 V-os jelszintet toleráló kivezetéseket használhatjuk a 4 bites adatbusz vezérlésére, mivel olvasáskor (az LCD foglaltságának ellenőrzésekor) az 5 V-os jelszintet el kell, hogy viseljék.
 
A PIC-kwik projekt keretében egy EW162G0GR LCD kijelzővel próbáltuk ki a programot, de működnie kell minden olyan LCD kijelzővel, ami Hitachi HD44780 vagy ezzel kompatibilis vezérlőt tartalmaz.

Hardver követelmények:
Hardver különbségek az alapértelmezettől elérő kártyáknál:
1. lista: A 2x16 katakteres LCD kezelése 4 bites üzemmódban (lcd4bit.c)#include "pic24_all.h"

#define RS_HIGH()        _LATB12 = 1
#define RS_LOW()         _LATB12 = 0
#define CONFIG_RS()      CONFIG_RB12_AS_DIG_OUTPUT()

#define RW_HIGH()        _LATB13 = 1
#define RW_LOW()         _LATB13 = 0
#define CONFIG_RW()      CONFIG_RB13_AS_DIG_OUTPUT()

#define E_HIGH()         _LATB14 = 1
#define E_LOW()          _LATB14 = 0
#define CONFIG_E()       CONFIG_RB14_AS_DIG_OUTPUT()

#define LCD4O          _LATB6
#define LCD5O          _LATB7
#define LCD6O          _LATB8
#define LCD7O          _LATB9
#define LCD7I          _RB9

#define CONFIG_LCD4_AS_INPUT() CONFIG_RB6_AS_DIG_INPUT()
#define CONFIG_LCD5_AS_INPUT() CONFIG_RB7_AS_DIG_INPUT()
#define CONFIG_LCD6_AS_INPUT() CONFIG_RB8_AS_DIG_INPUT()
#define CONFIG_LCD7_AS_INPUT() CONFIG_RB9_AS_DIG_INPUT()

#define CONFIG_LCD4_AS_OUTPUT() CONFIG_RB6_AS_DIG_OUTPUT()
#define CONFIG_LCD5_AS_OUTPUT() CONFIG_RB7_AS_DIG_OUTPUT()
#define CONFIG_LCD6_AS_OUTPUT() CONFIG_RB8_AS_DIG_OUTPUT()
#define CONFIG_LCD7_AS_OUTPUT() CONFIG_RB9_AS_DIG_OUTPUT()

#define GET_BUSY_FLAG()  LCD7I

///--- Kimenetnek állítja be a 4 bites adatbuszt
void configBusAsOutLCD(void) {
  RW_LOW();                  //RW=0 letiltja az LCD kimenetvezérlését
  CONFIG_LCD4_AS_OUTPUT();   //D4
  CONFIG_LCD5_AS_OUTPUT();   //D5
  CONFIG_LCD6_AS_OUTPUT();   //D6
  CONFIG_LCD7_AS_OUTPUT();   //D7
}

///--- Bemenetnek állítja be a 4 bites adatbuszt
void configBusAsInLCD(void) {
  CONFIG_LCD4_AS_INPUT();   //D4
  CONFIG_LCD5_AS_INPUT();   //D5
  CONFIG_LCD6_AS_INPUT();   //D6
  CONFIG_LCD7_AS_INPUT();   //D7
  RW_HIGH();                // R/W = 1 az olvasáshoz
}

///--- Az u8_c adat alsó 4 bitjét küldi ki az LCD adatvonalaira
void outputToBusLCD(uint8_t u8_c) {
  LCD4O = u8_c & 0x01;          //D4
  LCD5O = (u8_c >> 1)& 0x01;    //D5
  LCD6O = (u8_c >> 2)& 0x01;    //D6
  LCD7O = (u8_c >> 3)& 0x01;    //D7
}

///--- Az LCD-t vezérlő vonalakat konfigurálja
void configControlLCD(void) {
  CONFIG_RS();     //RS
  CONFIG_RW();     //RW
  CONFIG_E();      //E
  RW_LOW();
  E_LOW();
  RS_LOW();
}

///--- Pulzálja az E kimenetet, 1 us várakozás mindegyik élnél a beálláshoz/tartáshoz
void pulseE(void) {
  DELAY_US(1);
  E_HIGH();
  DELAY_US(1);
  E_LOW();
  DELAY_US(1);
}

/** Kiküld egy bájtot (u8_Cmd)  az LCD-re.
 *
 * u8_DataFlag = '1' ha adat, '0' ha parancs;
 * u8_CheckBusy = '1' ha írás előtt le kell kérdezni a foglaltság bitet,
 *                 egyébként szimplán késletetünk írás előtt;
 * u8_Send8Bits = '1' ha 8 bitet küldünk, egyébként csak a felső 4-bitet
 */
void writeLCD(uint8_t u8_Cmd, uint8_t u8_DataFlag,
              uint8_t u8_CheckBusy, uint8_t u8_Send8Bits) {

  uint8_t u8_BusyFlag;
  uint8_t u8_wdtState;
  if (u8_CheckBusy) {      // --- Foglaltság ellenőrzése
    RS_LOW();              //RS = 0
    configBusAsInLCD();    //minden adatvonalat bemenetnek állít
    u8_wdtState = _SWDTEN; //elmenti a Watchdog állapotát
    CLRWDT();              //törli a wachdog időzítőt
    _SWDTEN = 1;           //engedélyezi a watchdogot, nehogy a végtelenségig várjunk.
    do {
      E_HIGH();
      DELAY_US(1);         // a 4 felső bit olvasása
      u8_BusyFlag = GET_BUSY_FLAG();
      E_LOW();
      DELAY_US(1);
      pulseE();            //mégegyszer pulzálja E-t az alsó 4-bit miatt
    } while (u8_BusyFlag);
    _SWDTEN = u8_wdtState; //visszaállítja a watchdog állapotát
  } else {
    DELAY_MS(10);          //--- Nem nézzük a foglaltságot, csak késleltetünk
  }
  configBusAsOutLCD();     //adatbusz kimenet legyen
  if (u8_DataFlag) RS_HIGH();   // RS=1, adatküldés
  else    RS_LOW();             // RS=0, parancs küldés
  outputToBusLCD(u8_Cmd >> 4);  // a felső 4 bit küldése
  pulseE();
  if (u8_Send8Bits) {
    outputToBusLCD(u8_Cmd);     // az alsó 4 bit küldése
    pulseE();
  }
}

///--- Inicializáljuk az LCD-t ------------------------
void initLCD() {
  DELAY_MS(50);             //vár az eszköz beállására
  writeLCD(0x20,0,0,0);     // 4 bites interface
  writeLCD(0x28,0,0,1);     // 2 soros display, 5x7 karakter
  writeLCD(0x28,0,0,1);     // mégegyszer
  writeLCD(0x06,0,0,1);     // display engedélyezés
  writeLCD(0x0C,0,0,1);     // a display be-, kurzor és villogás kikapcsolva
  writeLCD(0x01,0,0,1);     // képernyő törlése, kurzor alaphelyzetbe állítás
  DELAY_MS(3);
}

//--Kiír egy karakterfüzért az LCD-re
void outStringLCD(char *psz_s) {
  while (*psz_s) {
    writeLCD(*psz_s, 1, 1,1);
    psz_s++;
  }
}

int main (void) {
  configBasic(HELLO_MSG);    // Oszcillátor,életjel LED, UART konfigurálása, kezdő üzenet
  configControlLCD();        //az LCD vezérlő vonalainak beállítása
  initLCD();                 //az LCD inicializálása

  outStringLCD("******Hello, my name is Bob********");
  writeLCD(0xC0,0,1,1);     // kurzor a második sorba
  outStringLCD("-----these lines are moving!-------");
  while (1) {
    writeLCD(0x18,0,1,1);   // balra lépteti a kijelzett szöveget
    DELAY_MS(200);
    doHeartbeat();
  }
}

A program elején elhelyezett #define makrók képviselik a hardver absztrakciós réteget. Itt definiáljuk az LCD-hez csatlakozó kivezetéseket, s makrókat is definiálink hozzájuk, amivel ezen kivezetések konfigurálhatók, az adatvonalak ki- vagy bemenetnek állthatók, s vezérlő kimenetek alacsony vagy magas szintre húzhatók.

Az LCD kezelésének központi eleme a writeLCD() függvény, amelyben beállítjuk a vezérlő vonalakat, majd az adatvonalakat, s az E verérlő kimenet pulzálásával (magas szintre állítjuk, majd egy rövid késleltetés után alacsony szintre húzzuk) beírjuk az adatot (esetünkben a 4 bites vezérlés miatt egy fél bájtot) az LCD vezérlő parancs- vagy adatregiszterébe.

A writeLCD() függvény bemenő paraméterei:
Az LCD kezelésének sarkalatos pontja a helyes inicializálás és üzemmód-beállítás. Ez a fenti programban két részből áll: a configControlLCD() függvény segítségével beállítjuk a vezérlő és adatvonalakat, majd az initLCD() függvénnyel beáálítjuk az LCD vezérlő üzemmódját. Arra ügyelnünk kell, hogy a bekapcsolástól a writeLCD(0x20,0,0,0); függvényhívás végrehajtásának végéig az LCD vezérlője még 8 bites módban van, tehát csak egyszer pulzáljuk az E kimenetet, s nem küldjük ki külön az alsó félbájtot. Ezért itt a writeLCD() függvény utolsó paramétere (u8_Send8Bits) '0' kell, hogy legyen. A további paracsok kiküldésénél azonban már 4 bites módban van az LCD vezérlője, ezért a további kiírásoknál már '1' lesz az u8_Send8Bits paraméter értéke. 

Megjegyzés: A főprogramban látható "Hello, my name is Bob!" üzenet a Tankönyv első szerzőjének, Robert (Bob) Reesének a bemutatkozása.


4. ábra: Az lcd4bit.c program futtatása a PIC-kwik kísérleti áramkörön


5. ábra: Az 5V-os és 3,3V-os tápfeszültséget a CP2102 USB-TTL átalakító adja


Az alábbi rövid videón a működés is megfigyelhető:



4x20 karakateres LCD kijelző vezérlése

Ha egy alkalmazásnál az egyidejűleg megjelenítendő karakterek száma meghaladja a 2x16 karakteres kijelző kapacitását, akkor használhatunk 4x20 karakteres kijelzőt is, melynek vezérlője és parancskészlete ugyanaz, mint a 2x16 karakteres kijelzőé, de egyidejűleg mind a 80 karakter (ami az LCD vezérlő adatmemóriájában tárolható) látható rajta. Az alábi program inicializáljuk a 4x20 karakateres LCD kijelzőt, majd minden sorába írunk egy-egy rövid szöveget.

A program hardver követelményei és a kapcsolás megegyeznek az előző programéval, annyi eltéréssel, hogy itt 4x20 karakteres kijelzőt használunk. A programot EW20400YLY kijelzővel próbáltuk ki, de működnie kell minden HD44780 vagy ezzel kompatibilis vezérlőt tartalmazó kijelzővel. Maga a program is 90 %-ban megegyezik az előzővel, ezért itt kizárólag az eltéréseket mutatjuk be.

2. lista: Részletek a 4x20 katakteres LCD-t kezelő programból (lcd4bit_4lines.c)//-- Ezek a  makrók a Hantronix 20x4 LCD-hez kellenek
#define GOTO_LINE1() writeLCD(0x80,0,1,1)
#define GOTO_LINE2() writeLCD(0xC0,0,1,1)
#define GOTO_LINE3() writeLCD(0x94,0,1,1)
#define GOTO_LINE4() writeLCD(0xD4,0,1,1)

int main (void) {
  configBasic(HELLO_MSG);
  configControlLCD();          //az LCD vezérlő vonalainak beállítása
  initLCD();                   //az LCD inicializálása

  GOTO_LINE1();                // kurzor az 1. sor elejére
  outStringLCD("Line 1");
  GOTO_LINE2();                // kurzor a 2. sor elejére
  outStringLCD("Line 2");
  GOTO_LINE3();                // kurzor a 3. sor elejére
  outStringLCD("Line 3");
  GOTO_LINE4();                // kurzor a 4. sor elejére
  outStringLCD("Line 4");
  while (1) {
    doHeartbeat();
  }
}
A GOTO_LINEx() típusú makrók degítségével az egyes sorok elejére pozícionálhatjuk a kurzort. A kiíráshoz az előző programban is használt outStringLCD() függvényt használjuk.


6. ábra: A chap08/lcd4bit_4lines.c program futtatása a 28-pin Starter Board fejlesztői kártyán

Ékezetes karakterek megjelenítése alfanumerikus LCD kijelzőn

Ebben a programban azt mutatjuk be, hogy hogyan definiálhatunk ékezetes betűket (vagy tetszőleges karaktert) a CGRAM-ban, s hogyan írhatunk ki ékezetes szöveget automatikus karakter konverzióval (a konverzió ANSI kódtáblához készült, s csak a kisbetűs magyar ékezetes karakterekkel foglalkozik). Az alfanumerikus LCD kijelző vezérlése most is 4 bites módban történik.

A CGRAM-ban 8 db karaktert definiálhatunk, s ez az ékezetes kisbetűkhöz is kevés. Szerencsére az általunk használt LCD modul karaktergenerátora az 'ö' betűt már tartalmazta. A két- vagy többsoros alfanumerikus kijelzőknél 5x8 pontos mátrixban definiálhatunk karaktereket. A betűknél a legalsó sort üresen kell hagyni, tehát a karakterkép megtervezésénél 5x7 pontos mátrixban kell gondolkodnunk. A 2. táblázatból kiolvasható, hogy  a karakterek definiálásánál a "CGRAM cím beállítása" parancsot hogyan adhatjuk ki. A CGRAM egy 64 bájtos terület, ahová ugyanúgy írhatjuk be az adatokat, mint a DATA RAM-ba. A CGRAM első 8 bájtba beírt karakterkép a 0-ás karakterkóddal jeleníthető majd meg, a következő 8 bájtban definiált karakter a DATA RAM-ba írt 1-es karakterkódra jelenik meg, és így tovább. A karakterek képét 8 bájtban kell megadni, melyek közül az első a legfelső pontsort képviseli, amásodik bájt a felülről második pontsort, stb. A bájtokban az adatok jobbra igazítva helyezkedjenek el, s csak a legalsó 5 bit értéke számít.


Hardver követelmények:
Megjegyzések:
  1. A programot egy EW20400YLY LCD kijelzővel próbáltuk ki, de működnie kell minden olyan LCD kijelzővel, ami Hitachi HD44780 vagy ezzel kompatibilis vezérlőt tartalmaz.
  2. A program a közzétett formájában 4x20 karakteres kijelzőre készült, de minimális munkával módosítható 2x16 karakteres kijezőhöz (a szöveget kétsoronként kell megjeleníteni, s további várakozásokat kell beiktatni).
 Az alábbi program az előző programok kibővített változata, tehát itt is csak az eltéréseket mutatjuk be. A hardver absztrakciós réteg és az adatok kiírása az LCD-re pontosan ugyanúgy történik, ahogyan az előző programokban.

3. lista: Részletek a 4x20 katakteres LCD-t kezelő programból (lcd4bit_cgram.c)//-- Az ékezetes kisbetűk karakterképeinek definiálása
const unsigned char betwk[] = {0x02,0x04,0x0E,0x01,0x0F,0x11,0x0F,0x00,    //á
                               0x02,0x04,0x0E,0x11,0x1F,0x10,0x0E,0x00,    //é
                               0x02,0x04,0x0C,0x04,0x04,0x04,0x0E,0x00,    //í
                               0x02,0x04,0x0E,0x11,0x11,0x11,0x0E,0x00,    //ó
                               0x02,0x04,0x11,0x11,0x11,0x13,0x0D,0x00,    //ú
                               0x0A,0x00,0x11,0x11,0x11,0x13,0x0D,0x00,    //ü
                               0x05,0x0A,0x11,0x11,0x11,0x13,0x0D,0x00,    //ű
                               0x05,0x0A,0x0E,0x11,0x11,0x11,0x0E,0x00};   //ő

....

///-- LCD inicializálás a HD44780 vezérlő adatlapjának ajánlása szerint
//    ezt módosítsa, ha másfajta LCD vezérlőhöz kell alkalmazkodnia!
void lcd_init() {
//-- Az LCD modul szoftveres resetelése --------
  DELAY_MS(50);                // 50 ms várakozás az eszköz beállására
  writeLCD(0x30,0,0,0);        // 8 bites üzemmód  
  DELAY_MS(5);                 // >4.1 ms várakozás
  writeLCD(0x30,0,0,0);        // 8 bites üzemmód  
  DELAY_US(150);               // >100 us várakozás
  writeLCD(0x30,0,0,0);        // 8 bites üzemmód  
  DELAY_US(150);               // >100 us várakozás
//-- Az LCD modul üzemmódjának beállítása --------
  writeLCD(0x20,0,0,0);        // 4 bites interface
//-- Innen kezdve a 4 bites üzemmód él (küldésnél dflag=1 kell)
  writeLCD(0x28,0,0,1);        // 2 soros display, 5x7 karakter
//-- Innen kezdve figyelhető a BF jelzőbit (chk_busy=1)
  writeLCD(0x08,0,1,1);        // display letiltás
  writeLCD(0x01,0,0,1);        // képernyő törlése, kurzor alaphelyzetbe állítás
  writeLCD(0x0C,0,0,1);        // a display be-, kurzor és villogás kikapcsolva
}

//-- A CGRAM terület feltöltése az ékezetes betűkkel
void lcd_init_cgram(void) {
uint16_t i;
    writeLCD(0x40,0,1,1);      // kurzor a CGRAM elejére
    for(i=0; i<64; i++) {
      writeLCD(betwk[i],1,1,1);// definiálható karakterek feltöltése
    }                          // ékezetes karakterekkel
    writeLCD(0x80,0,1,1);      // kurzor vissza, a DDRAM elejére   
}

//-- Karakterfüzér kiírása az LCD-re
void outStringLCD(char *psz_s) {
char cx;
  while (*psz_s) {
    cx = *psz_s;
    switch(cx) {
        case 'á':  cx = 0x00; break;
        case 'é':  cx = 0x01; break;
        case 'í':  cx = 0x02; break;
        case 'ó':  cx = 0x03; break;
        case 'ú':  cx = 0x04; break;
        case 'ü':  cx = 0x05; break;
        case 'ű':  cx = 0x06; break;
        case 'ő':  cx = 0x07; break;
        case 'ö':  cx = 0xEF; break;
    }
    writeLCD(cx,1,1,1);
    psz_s++;
  }
}
 
//-- Ezek a  makrók a 4x20 karakteres LCD-hez kellenek
#define GOTO_LINE1() writeLCD(0x80,0,1,1)
#define GOTO_LINE2() writeLCD(0xC0,0,1,1)
#define GOTO_LINE3() writeLCD(0x94,0,1,1)
#define GOTO_LINE4() writeLCD(0xD4,0,1,1)

int main (void) {
uint16_t i;
  configBasic(HELLO_MSG);      // Oszcillátor,HB_LED, UART konfigurálás, kezdő üzenet
  configControlLCD();          // az LCD vezérlő vonalainak beállítása
  lcd_init();                  // az LCD inicializálása
  lcd_init_cgram();            // ékezetes betűk definiálása a CGRAM-ban
  while(1) {
    writeLCD(0x01,0,1,1);      // képernyőtörlés 
    GOTO_LINE1();              // kurzor az 1. sor elejére
    outStringLCD("Adjon az Isten");
    GOTO_LINE2();              // kurzor a 2. sor elejére
    outStringLCD("szebb jövőt,");
    GOTO_LINE3();              // kurzor a 3. sor elejére
    outStringLCD("legyen úgy,");
    GOTO_LINE4();              // kurzor a 4. sor elejére
    outStringLCD("mint régen!");
    for(i=0; i<25; i++) {
      DELAY_MS(200);           // 25 x 200 ms várakozás
      doHeartbeat();           // életjelző LED állapotának aktualizálása
    }
    writeLCD(0x01,0,1,1);      // képernyőtörlés 
    GOTO_LINE1();              // kurzor az 1. sor elejére
    outStringLCD("Piros, fehér, zöld");
    GOTO_LINE2();              // kurzor a 2. sor elejére
    outStringLCD("szivárvány");
    GOTO_LINE3();              // kurzor a 3. sor elejére
    outStringLCD("ragyogjon újra");
    GOTO_LINE4();              // kurzor a 4. sor elejére
    outStringLCD("az égen!");
    for(i=0; i<25; i++) {
      DELAY_MS(200);           // 25 x 200 ms várakozás
      doHeartbeat();           // életjelző LED állapotának aktualizálása
    }
  }
}

A program elején létrehozunk egy betwk[] nevű, konstansokat tartalmazó tömböt, amelyet a programmemória területén helyezünk el (a const rom módosítóval deklarált konstansok a programmemóriába kerülnek).

Az LCD-t kezelő függvényeket kiegészítettük az lcd_init_cgram() függvénnyel, amellyel a 64 bájtos betwk[] nevű tömböt feltöltjük az LCD modul vezérlőjének CGRAM területére.

Módosítottuk az outStringLCD() függvényt, hogy az ékezetes betűk ANSI kódját cserélje ki a CGRAM-ban definált (vagy már eleve meglevő) ékezetes betűk kódjára, hogy a kívánt betűkép jelenjen meg az LCD kijelzőjén.

Nem volt szükség az LCD inicializálásának módosítására. Itt most csak azért cseréltük le a korábbi initLCD() függvényt a módosított lcd_init() függvényre, hogy bemutassuk a szabályos, az adatlapi ajánlásokat követő inicializálást.

A főprogram elején a korábbi programokban is alkalmazott inicializálások után meg kell hívnunk az lcd_init_cgram() függvényt is. A végtelen ciklusban pedig kiírunk négy sort a kijelzőre, magd kb 5 s várakozás után töröljük a képernyőt, s kiírjuk a maradék szöveget. A várakozást 25 időszeletre osztottuk, hogy közben meghívhassuk a doHeartbeat() függvényt. A képernyő törlését a Clear display paranccsal végezhetjük (lásd 2. táblázat).

A program egy futtatásának eredménye az alábbi képen látható. Ebben a kísérletben egy Microstick fejlesztői kártyát használtunk. A szalagkábellel egy USB-TTL átalakító kapcsolódik a kártyához. Az LCD modul számára az 5 V-os tápfeszültséget a számítógép USB csatlakozójáról vettük. A képen látható, hogy a kontraszt beállítása nem optimális, mivel lustaságból közvetlenül a földre kötöttük a VEE bemenetet (egy 1 kOhmos ellenállást sorba kellett volna kötni...).

7. ábra: A chap08/lcd4bit_cgram program futtatása Microstick felhasználásával

A program egy másik futtatásának eredménye látható a következő fényképen. Ebben a kísérletben a Microchip 16-bit 28-pin Starer Board fejlesztői kártyáját használtunk. Az LCD modul számára az 5 V-os tápfeszültséget a kártya  JP2 átkötésének bal szélső tüskéjéről vettük. A kontrasztbeállító feszültség optimális értékének megtalálásához a kártyán levő potméterről (JP5 baloldali tüskéje) vettük. A képen látható, hogy a kontraszt beállítása tökéletes, a felirat zavaró árnyak nélkül jelenik meg.



8. ábra: A chap08/lcd4bit_cgram program futtatása a 16-bit 28-pin Starter Board fejlesztői kártyán