LCD kijelzõ vezérlése 3 vezetéken
avagy...
2x16-os LCD kijelzõ vezérlése ESP8266-al és ARDUINO-val
Tervem szerint ez a cikk a közismert és gyakran használt, alfanumerikus, HD44780 vezérlõjû vagy azzal
kompatibilis kijelzõ, nem a leginkább szokványos módon történõ vezérlésérõl fog szólni, némi kitérõvel
az ESP8266 wifi chip és az arduino programozás felé.
A dolog azzal kezdõdött, hogy elkezdtem ismerkedni az ESP8266 Wifi chippel. Ez egy nagyon olcsón
(szinte fillérekért) beszerezhetõ áramkör, az ezzel készült fejlesztõ panelhez, legolcsóbban jelenleg
(2017 jan.) kb. 1000Ft körüli áron lehet hozzájutni. Az általam beszerzett wifi chip egy 80MHz-es órajelû
mikrovezérlõt tartalmaz, I/O portokkal, 96Kb RAM-al, 4Mb flash memóriával, 2,4GHz-es wifiel, és integrált antennával.
A fejlesztõ panel ezen kívül tartalmazza az USB illesztõt a programozáshoz, a 3,3V-os tápegységet a wifi chip számára,
reset és flash nyomógombot.
Szóval ez egy olcsó "elekrtomos programozós LEGO" :), lehet vele játszani.
A netet böngészve a programozási lehetõségekrõl, nagyjából 3 lehetõség körvonalazódik:
- AT parancsokkal történõ vezérlés.
Ezzel a módszerrel soros porton keresztül pl. a GSM chipekhez, vagy modemekhez hasonlóan, úgynevezett AT parancsokkal
vezérelhetjük a wifi chipet. Megfelelõ parancs van a wifi csatlakozáshoz, hálózat listázáshoz és minden egyébhez.
Teljesen jól használható ez a módszer, de a verzérlõ parancsok küldéséhez kell egy mikrovezérlõt illeszteni az eszközhöz.
- LUA parancsokkal vagy programmal történõ vezérlés.
Az ESP chipre LUA fordító is rátölthetõ, a LUA nyelven írt programunkat soros porton keresztül fel lehet tölteni az
eszközünkre, és futtatni tudja a tárolt programot. Kisebb vezérlési feladatokra, egyszerübb weboldal megjelenításre
ezzel simán alkalmas önmagában is, külön mikrovezérlõ nélkül. Ahoz, hogy az ESP-t programozni tudjuk
nem kell profinak lenni a LUA rejtelmeiben, az alapok pedig könnyen megtanulhatók. Érzésre persze nekem kicsit olyan
mintha egy régi számítógé BASIC nyelvû interpreterét használnám: azaz jópofa játék, de egy idõ után ez is kevés.
- Programozás ARDUINO IDE-vel
A közismert ARDUINO-val foglakozó fejlesztõk hamar felismerték az ESP8266-ban rejlõ lehetõségeket, és elkészültek a
megfelelõ kiegészítõk ahoz, hogy ugyanúgy programozhassuk mint bármelyik arduino fejlesztõpanelt. Ezzel gyakorlatilag
egy C vagy C++ nyelvhez nagyon hasonló programnyelven készíthetünk programokat, aminek a futtatásához az ESP-n
kívül semmilyen külsõ mikrovezérlõre nincs szükség. Az ESP flash memóriájában filerendszer hozható létre igy a saját
programján kívül fájlokat is tárolhatunk rajta (pl ha webszervert készítünk belõle, tárolhatjuk rajta a HTML fájljainkat,
képeket, bármit)
Miután a lehetõségeket megismertem, az AT parancsos megoldást kapásból elvetettem, mert kissé pazarlásnak érzem, hogy
egy 80MHz-es mikrovezérlõ mindössze AT parancsokat dekódoljon és hajtson végre amikor sokkal többre is képes lehet.
Arról nem beszélve, hogy sokkal több energia kell hozzá mire egy olyan mûködõ dolgot készítünk így, ami már használható
is valamire és nem csak játszottunk vele. A LUA nyelvvel próbálkoztam egy darabig, de nem tudtam igazán megszeretni,
és ezzel is kevésnek éreztem a lehetõségeimet. Aztán jött az arduino, ami véleményem szerint jelenleg a legjobb fejlesztõ
környezet az ESP-hez, ezzel lehet a leginkább kihasználni a lehetõségeit, és ezzel lehet akár tobábbi komolyabb hardver
nélkül is a legjobb dolgokat készíteni ezzel a chippel. Készítettem is egy webszervert, ami jelenleg annyit tud, hogy
a webfelületrõl konfigurálhatók a wifi beállítások, több hálózat megjegyzésére képes, a lehetõségek közül megkeresi
a legjobban vehetõ hálózatot és ahoz próbál elõször csatlakozni. Létrehoz egy AP-t is, ha nincs olyan wifi hálózat
amihez sikerül csatlakoznia, akkotr ezen keresztül is el lehet érni. A HTML és egyéb fájlokat a meglevõ webfelületrõl
is fel lehet tölteni az ESP fájlrendszerébe, vagy éppen törölni róla ami már nem kell. Tehát mostmár sokszor a
fejlesztõ környezet sem kell a webszerver által megjelenített oldalak bövítéséhez.
Most jön a képbe az LCD kijelzõ. Arról van szó, hogy pl. amikor egy wifi hálózathoz csatlakozik az eszköz,
akkor kap egy dinamikus, hálózat specifikus IP címet, amin keresztül elérhetjük pl. a webfelületét. Ahoz, hogy ezt a
címet ne kelljen keresni, jó lenne kiírni egy kijelzõre, más egyéb adatokkal együtt. A legegyszerübb megoldás erre a
célra az lenne, ha egy mezei 2x16-os LCD kijelzõt kapcsolnánk rá az ESP-re. Ennek a kezelése simán belefér az ESP
mikrovezérlõjének feladataiba. Azonban itt beleütközünk abba, hogy egy apró olcsó chippel dolgozunk, aminek nincs túl
sok I/O portja, az LCD kijelzõ meg minimum 6 adatvonalat igényel 4bits módban, és 10 adatvonalat 8 bites üzemmódban.
Ha az ESP I/O lábait másra is akarjuk használni, akkor célszerû spórolni vele, tehát szerettem volna viszonylag kevés
I/O láb felhasználásával megoldani a dolgot. Ráadásul nagyon egyszerû, gyors, és nagyon olcsó megoldást kerestem,
és meg is találtam: 74HCT595 Ez egy olcsó, soros bemenetû, soros/párhuzamos kimenetû léptetõregiszter, tárolóval egybeépítve.
Lehet, hogy ez így nem sokat mond, de arról van szó, hogy egy órajel ütemében egyetlen adatvonalon kiküldhetjük sorban az
adatbiteket, az IC-nek majd egy impulzussal ez beíródik a tárolóba, és megjelenik az IC kimenõ lábain. Ebbõl annyi a
lényeg, hogy ha az alánbbi kapcsolást megépítjük, akkor a mikrovezérlõtõl mindössze 3 I/O lábra lesz szükségünk ahhoz,
hogy az LCD kijelzõn megjelenítsük amit szeretnénk. Az I/O lábak funkciói: órajel, adat, és beírás/ENable. A dologban még az
a vicces, hogy ilyen módon még könnyebb is leprogramozni az LCD vezérlését, ahhoz képest mintha közvetlenül a
mikrovezérlõre lennen kapcsolva.
Tehát a kijelzõhöz úgy jut el a 8 bit adat, hogy az elsõ bitet beállítjuk a data lábon, majd a clock
lábra adunk egy impulzust, majd ugyanígy járunk el a többi adatbit esetében is. Ezután a data lábat beállítjuk
aszerint, hogy adat vagy parancs bájtot küldünk-e a kijelzõnek, és adunk egy impulzust az enable lábra.
Az impulzus felfutó élére a tárolóba beíródik a 8 adatbit amit kiléptettünk, és megjelenik az LCD kijelzõ lábain, a
lefutó élére pedig megjelenik/végrehajtódik a kijelzõn. Ebbõl a mûködésból szointe adódik a kapcsolás is:
Az IC SCK lába lesz az órajel bemenet, a SERial input lába az adatbemenet ami egyúttal az LCD parancs/adat lábával
is össze van kötve, a beírás/ENable bemenet pedig az IC tároló bemenete és az LCD EN lába összekötve.
A léptetõregiszternek több változata is van, pl: 74HC595 és 74HCT595 a fenti áramkörhöz a HCT verzióra van szükség.
Az ESP 3,3V-os digitáslis jelekkel dolgozik, az LCD kijelzõ 5V-os tápot kap, bár mûködik 3,3V-os jelszintekkel is.
Ha a léptetõregiszter 5V-ról mûködik akkor csak a HCT verzió fogadja el a 3,3V-os jelszintet. A sima HC verziónak
csak akkor "kötelessége"jól mûködnie 3,3V-os jelszintekkel, ha errõl a tápról üzemel. A gyakorlatban ugyan 5V-ról
is mûködött sima HC verzióval az áramkör, de ez nem garantálható, ezért nem ajánlott. A léptetõregiszter létezik
SMD és normál lábas tokozással is, minkettõhöz készitettemm NYÁK tervet:
Szokásomtól eltérõen a kapcsolási rajzot és a nyáktervet is közzéteszem az EAGLE nyáktervezõ program formátumában is:
SKORI_LCD_SHIFT
A kijelzõbe tüskesort, a nyákba pedig tüskesor aljzatot ültettem be, így a nyák egyszerüen rádugható a kijelzõre.
A bemeneti pontokba pedig szintén tüskéket ültettem be, de végülis vezetékek is beforraszthatók. A nyákterv THT IC
verzióhoz készült változata úgy van kialakítva, hogy raszteres próbanyákon is könnyedén megéíthetõ
legyen - azaz minden furat a nyákterven pontosan raszterre eseik.
Léteznek persze más megoldások is erre a feladatra, pl. a mindössze 1 adatvonalat igénylõ soros portos megoldás,
ehez azonban egy külön mikrovezérlõt kellene felprogramozni. A fenti áramkört viszont csak össze kell rakni és egybõl
használható is. A léptetõregsiszter sokkal gyorsabb mint ahogy kijelzõ tudná fogadni az adatokat, így a sebességet
továbbra is az utbbi határozza meg. Tehát a léptetõregiszter használata nem kíván semmivel sem több
processzoridõt a mikrovezérlõtõl, mint az LCD közvetlen vezérlése. Mint kicsit lejjebb látható is lesz a programkód
még egyszerûbb is lesz mint ami a közvetlen vezérléshez kellene. Nézzük a mûkedéshez szükséges
programkódot ill. függvényeket:
#define LCDSH_data 13
#define LCDSH_clock 14
#define LCDSH_en 12
void lcdsh_wrbyte(unsigned char data, int delay=0) //1 bájt adat vagy parancs küldése az LCD-nek
{
for (byte mask=128; mask ; mask >>=1) //8-szor fog lefutni
{
digitalWrite(LCDSH_data, mask & data); //az aktuális adatbit beállítása
delayMicroseconds(1); digitalWrite(LCDSH_clock, 1); //órajel impulzus felfutó él
delayMicroseconds(1); digitalWrite(LCDSH_clock, 0); //órajel impulzus lefutó él -> 1 órajel impulzus
} //ciklus vége, mind a 8 adatbit elküldve
digitalWrite(LCDSH_data, !delay); //LCD-RS beállítása, ha nincs delay, akkor sima adatbyte volt
digitalWrite(LCDSH_en, 1); //1db EN impulzus: felfutó él az adatok->tárolva
delayMicroseconds(1);
digitalWrite(LCDSH_en, 0); //EN impulzus lefutó él az adatok->tárolva->LCD-be írva
if (delay) delayMicroseconds(delay); //ha volt megadva várakozás, akkor parancsot küldtünk,
//és várakozás az LCD parancs befejezõdésére)....
}//end fv
A fenti programrészletben elõször definiáltam a 3db I/O portkivezetést amikett használok az LCD vezérlésáhez.
Az lcdsh_wrbyte() függvény funkciója 1 bájt elküldése az LCD kijelzõnek.
Tehát pl. az lcdsh_wrbyte('a'); parancs egy kis a betût fog kiírni
a kijelzõre. Ha a függvénynek két paramétert adunk, akkor az elsõ bájtot parancsként elküldni a kijelzõnek,
majd a második paraméter szerinti usec. ideig várakozik (a parancs végrahajtási idejét adjuk meg itt).
Teháp pl. az lcdsh_wrbyte(0x80,50); parancssal a kurzort az esõ sor elsõ poziciójába teszi,
és megvárja amíg a kijelzõ végrehajtja a parancsot. Tehát ezzel a függvénnyel kezelni tudjuk az LCd kijelzõ szinte összes
funkcióját. Nagyjából erre a fügvényre épül az összes többi LCD kijelzõt kezelõ függvény. A mûködése: beállít egy
mask változót 128-ra (gyak. a felsõ bit 1, a többi nulla) Ezt a bitet lépteti jobbra míg a változó nulla nem lesz.
A mask-al éselve a küldendõ bájtot, mindíg a következõ bitjét kapjuk meg a ciklusban. Ezzel be is állíthatjuk a DATA lábat,
majd egy órajelimpulzus és el is küldtünk 1 bitet. A ciklus 8szor lefut és ezzel el is küldi a bájtot. Ezután beállítja
az LCD RS lábát (adat/parancs mód), majd egy impulzus az EN lábra, és az LCD kijelzõ meg is kapta az adatokat. Ha
parancsot küldtünk, akkor célszerû megvárni amíg végrehajtja a kijelzõ, mert ha hamarabb küldünk uj adatot/parancsot
akkor azt nem fogja értelmezni a kijelzõ. Azonban mielõtt használnmi kezdhetjük a kijelzõt a programban, még
inicializálni kell, ez annyit jelent, hogy be kell állítani a kijelzõ üzemmódját, és egyéb paramétereit. erre
szolgál az alábbi függvény:
void lcdsh_Init(){
pinMode(LCDSH_data, OUTPUT);
pinMode(LCDSH_clock, OUTPUT);
pinMode(LCDSH_en, OUTPUT); //vezérlõlábak beállítása: kimenet
lcdsh_wrbyte(B00111000,5000); //8bites mód, 2soros mód, 5msec várakozás
lcdsh_wrbyte(B00001000,50); //disp. off, 50usec
lcdsh_wrbyte(B00111000,50); lcdsh_wrbyte(B00111000,50); //8bites mód, 2sor , duplán kell kiadni a parancsot
lcdsh_wrbyte(B00001100,50); //disp. on, cursor off, blink off, 50usec
lcdsh_wrbyte(B00000001,2000); //clear, 2msec
lcdsh_wrbyte(B00000010,2000); //cursor home, 2msec
lcdsh_wrbyte(B00000110,50); //entry mod increase, no shift, 50usec
lcdsh_wrbyte(B10000000,50); //set DD ram address, 50usec
}//end fv
Tehát a fenti függvényt elégendõ egyszer lefuttatni, mielõtt használni kezdjük a kijelzõt. Azonban elõpfordulhat olyan eset
hogy bizonyos kijelzõk érzékenyek a környezeti zavarokra és "kiakadnak" ilyenkor ujra lehet inicializálni.
Elõfordulhat olyan eset is, hogy menet közben dugjuk rá, vagy cseréljuk le a kijelzõt a készüléken, ez ugyan nem szép dolog,
de ha ilyenkor a programban ujra lefut az inicializálás, akkor már használható is a kijelzõ.
A hardvert és a programot kipróbáltam sokféle LCD kijelzõvel, és kompatibilis, alfanumerikus OLED kijelzõvel is,
és mindegyikkel jibátlanul mûködött. A késleltetési ídõkbõl talán lehetne lefaragni, de jelenleg sem mondható lassúnak
a kijelzés, kb. 40usec egy karakter kiírása, azaz alig több mint 1msec (1 ezredmásodperc) alatt tele lehet írni a 2x16-os
kijelzõt. Rövidebb idõzítéseket használva nekem elõfordult, hogy némelyik kijelzõ idõnként elvesztett egy karaktert,
de lehet, hogy az említett (bontott) kijelzõ nem teljesítette az adatlapján szereplõ paramétereket.
Nézzük, hogy lehet szöveget kiírni a kijelzõre, ill. kurzort pozicionálni:
void lcdsh_str(char * s){ //string kiírása
for ( ; *s ; s++) lcdsh_wrbyte(*s); //a ciklus karakterenként küldi az LCD-nek a szöveget
}//end fv
void lcdsh_str(char *s, byte len){ //string kiírása, len karakteren (ha hosszabb levágja, ha rövidebb szóközzel kiegészíti)
for ( ; *s && len; s++,len--) {lcdsh_wrbyte(*s); }
for ( ;len; len--) lcdsh_wrbyte(32);
}//end fv
void lcdsh_Line1 (){ //kurzor az elsõ sor elejére
lcdsh_wrbyte(0x80,50);
}//end fv
void lcdsh_Line2 (){ //kurzor a második sor elejére
lcdsh_wrbyte(0xC0,50);
}//end fv
void lcdsh_Cursor (char oszlop, char sor){ //kurzor adoss sor/oszlop pozicióba
switch (sor) {case 2: oszlop += 0x40; break; case 3: oszlop += 0x14; break; case 4: oszlop += 0x54; break; };
lcdsh_wrbyte( oszlop|0x80,50 );// set RAM address
}//ed fv
//példák:
lcdsh_str("Hello Wolrd"); //kiírás a kurzor poziciójától kezdve
lcdsh_Line1();lcdsh_str("Elso sor",16); //szöveg kiírása az eslõ sor elejétõl, de végig kitölti a sort,
//így ami elõzõleg volt a kijelézõn az egyúttal törlõdik.
A programot az Arduino fejlesztõrendszerében írtam, tehát igen jó eséllyel használható bármilyen arduino
eszközön, nem csak kizárólag az ESP8266-on. Aki programozott már más mikrovezérlõt, annak nyilván nem gond ilyen
programot megírni, de a fenti kód is pillanatok alatt átírtható szinte bármilyen mikrovezérlõre. Remélem
sokaknak hasznára válik a fenti cikk, és lesz akinek talán sikerül öltletet adni. Esetleg valaki pont
ESP8266-hoz szeretne egy kijelzõt illeszteni.
u.i.
Köszönet Attila86-nak, hogy legyártotta kérésemre ezt a kis nyákot!
Köszönet Ottónak, hogy segitett az SMD 74HCT595 forrasztásában, mivel Õ ezt még szabadszemmel is látja...
Az elgépeléseket, hibákat kérem elnézni, mivel nem sok idõm volt erre a cikkre, így a tartalom volt az elsõdleges.
Aki valami nagyon zavaró hibára akad, azt mailban elküldheti nekem, és amint lesz rá idõm javítani fogom!
Úgy néz ki máris elérkezett a folytatás ideje:
LCD kijelzõ vezérlése 2 vezetéken
Mielõtt a lényegere térek jöjjön pár sor a miértrõl is. A 3 vezetékes megoldás szoftverével kisérleteztem ,
hogy meddig lehet lefaragni a késleltetési idõket, úgy, hogy még stabil maradjon a kijelzõ mûködése. A gyakorlatban arra jutottam,
hogy az szinte mindegy, hogy hol vannak berakva a késleltetések, anyi a lényeg, hogy kb 40...50µsec alatt lehet kiírni egy karaktert.
Ha ennél lassabban írjuk a kijelzõt akkor semmi gond, viszont ha gyorsabban akkor véletlenszerûen kihagy karaktereket. Ebbõl az is következik,
hogy pl. az órajel esetében nem szükséges L és H szinten ugyanannyi várakozást betenni a programba, hanem lehet asszimmetrikus is az órajel.
Ezt tovább gondolva viszont az órajel kitöltési tényezõje (avagy az L és H szint idõaránya) is hordozhat információt, ha ezt kihasználjuk.
A 3 vezetékes megoldásból a "data" vezetéket az alábbi kapcsolással megspórolhatjuk, úgy, hogy a léptetõregiszterbe írandó adatot,
az órajelbõl állítjuk elõ, egy 1µs idõállandójú R-C taggal.:
A fenti áramkört megvalósítva, a kijelzõt ugyanakkora sebességgel tudjuk kezelni mint a 3 vezetékes módszerrel, azaz a még mindíg
kijelzõ korlátozza a sebességet, ill. ettõl függ a kiírásra fordított idõ, tehát lényegében nem igényel több erõforrást ez a megoldás.
A kijelzõ RS (adat/parancs) kivezetését ami eddig az adat lábra volt kötve, átraktam az órajel lábra. A szoftver esetében
a lcdsh_wrbyte függvényt kell átírni, illetve az LCDSH_data I/O láb definiálását kell törölni a programból.
Tehát az alábbi programkóddal lehet elküldeni 1 bájtot a kijelzõnek:
void lcdsh_wrbyte(unsigned char data, int delay=0){ // 1 bájt adat vagy parancs küldése az LCD-nek, 2 I/O lábon
for (byte mask=128; mask ; mask >>=1){ // 8-szor fog lefutni
digitalWrite(LCDSH_clock, mask & data); // clock lábbeállítása az adatbitnek megfelelõen
delayMicroseconds(3); // 3µs várakozás, hogy a kondi feltöltödjön vagy kisüljön
noInterrupts(); // a felfutó él elõtt tiltani kell a megszakításokat
digitalWrite(LCDSH_clock, 0);digitalWrite(LCDSH_clock, 1); // felfutó él az órajelben a "kondiban tárolt" bit beírásához a léptetõregiszterbe
interrupts(); // a felfutó él után ujra lehetnek megszakítások
}//end for //ADATBITEK beáll
digitalWrite(LCDSH_clock, !delay); //RS beállítása
digitalWrite(LCDSH_en, 1); delayMicroseconds(1); digitalWrite(LCDSH_en, 0); //EN impulzus
if (delay) delayMicroseconds(delay-25); //ha volt megadva várakozás, akkor várakozik (az LCD parancs befejezõdésére)
}//end fv
A program nem igazán lett bonyolultabb, de a mûködése kicsit érdekesebb. A for ciklusban, ugyanúgy mint a korábban, egy mask
változó bitléptetésével választja ki az elküldendõ bitet. Elõször beállítjuk az órajel lábat az adatbitnek megfelelõen,
majd 3 µsec várakozás következik, hogy a 10k ellenálláson keresztül a 100pF kondinak legyen ideje feltöltõdni vagy kisülni.
Ezután egy felfutó élt (0 majd rögtön 1) írunk ki a clock lábra - ennek hatására "kondiban tárolt" bit beíródik a léptetõregiszterbe.
A felfutó él ideje alatt tiltani kell a megszakításokat, mert ha ennek az idejét megnyújtaná egy megszakítás, akkor a kondi
töltése megváltozhatna és hibás bitet írna ki a regiszterbe a program. A cilkusmag mindigh H szintû órajelre végzõdik,
így a ciklusmag következõ futásakor az adatbit beállítása nem okozhat fals L-H átmenetet (azaz feltfutó élt). Erre egyedül
a cilkus legelsõ futásakor van lehetõség, ami egy hibás bit kiírását eredményezhetné, ami azonban azért nem okoz problémát,
mert utána még 8 bitet kiléptetünk, így az esetleges hibás bit nem maradhat benne a léptetõregiszterben. A cilkus lefutása után,
tehát ha kiléptettük mind a 8 bitet, a clock lábon beállítjuk a kijelzõ RS lábát (vagy marad H szinten vagy L szint lesz).
Itt egyik esetben sem, lesz felfutó él, így a léptetõregiszter tartalma emiatt nem változik meg. Ezután az EN-re adott
impulzussal a korábbiakhoz hasonlóan a kijelzõ megkapja a küldött bájtot. Ezután ha volt delay, azaz parancsot küldtünk a
kijelzõnek, akkor várakozunk a parancs lefutási idejére. Ez utóbbiból le lehet vonni a bájt kiírási idejének egy részét
(-25µs). A korábban közölt függvényeket nem kell módosítani, ugyanúgy használhatók lesznek, de az inicializáló
függvénybõl ki kell venni az LCDSH_data azaz a data láb kezelését.
Ha nen használ megszakításokat az az eszköz amire a programot készítjük vagy átírjuk, akkor a megszakítás tiltás/engedélyezés
parancsot kihagyhatjuk a programból.
A 2 kivezetéses LCD kijelzõ illesztést kipróbáltam többféle LCD, + egy OLED kijelzõvel,
és teljesen megbízhatóan, stabilan mûködött.
Ahogy elnézem a programja végülis nem is lett bonyolultabb mint a 3 vezetékes verzió hasonló függvénye.
Ha valaki kipróbálja ezt a megoldást más arduinoval, esetleg teljesen más hardverrel, akkor szívesen fogadnám a tapasztalatait.
Készült egy újabb verzió a 2db I/O portot használó LCD illesztõ nyákból, amit nyákgyártó céggel legyártattam.
Annyi a változás a korábbuiakhoz képest, hogy az LCD kijelzõ háttérvilágítása is használható ezzel a kis nyákkal,
és igyekeztem minél kisebbre összezsúfolni. A kész nyák gyakorlatilag egy kis tüskesorral ráforrasztható az LCD kijelzõ hátuljára,
és ebbe beforrasztható a 4 vezeték (vagy tüskesor, 4db tüskével).
Íme a kapcsolási rajz és a nyákterv:
Kapcsolási rajz és nyákterv letöltése az EAGLE nyáktervezõ formátumában.
Skori
@2017.jan.
A fenti cikk nmegírása óta soksz6or használtam ezt a megoldást, így nyugodtam állíthatom, hogy a gyakorlatban is bevált.
Azonban néhány tapasztalat is összegyûlt ezzel kapcsolatban, amivel nem árt tisztában lenni. Arduino esetében
(pl. arduino nano) a digitalWrite() függvény lassan fut le, egyes tipusok esetében néhány µs
ideig is letarthat, ami gyakorlatilag lehetetlenné teszi az ezzel összemérhetõ idejû idõzítések normális használatát a programban.
Némi nyomozás után kiderült, hogy nem az arduino lassú, hanem ez a függvény a port irásán kívül mást is csinál (pl. ellenõrzi,
hogy van-e más fubnkciója a lábank). Emiatt szükség lehet az adott portkivezetés közvetlen írására a programban.
A port közvetlen írása és a digitalWrite() fügvénnyel való írása között 5...20x-os sebességkülönbség is lehet!!!
Arduino Due használata esetén pl. makrókat készítettem a clock és az enable lábak írásához, íme:
#define LCDSH_clock 51
#define LCDSH_en 53
#define SET_CLK PIOC -> PIO_SODR = 1<<12 /* == digitalWrite(51, 1); csak sokkal gyorsabb!!! */
#define CLEAR_CLK PIOC -> PIO_CODR = 1<<12
#define SET_EN PIOB -> PIO_SODR = 1<<14
#define CLEAR_EN PIOB -> PIO_CODR = 1<<14
Késõbb nagyon megtetszett a BluePill fantázianevû kis modul, ami fizikailag az arduino nano-hoz hasonlít,
csak éppen olcsóbb, és sokkal nagybb teljesítményû. A nano 8bites, 16MHz órajelû processzorához képest ugyanis ezen egy
STM32F103 processzor kapott helyet, ami 32 bites, és 72MHz órajellel mûködik. Az ST-nek van saját
fejlesztõkörnyezete, de arduinoval is programozható, ami sok esetben elõny, hiszen korábban megírt arduino programok is átvihetõk rá.
Továbbá az arduino használata könnyû, gyorsan készíthetõk vele ügyes programok. Természetesen a BluePill-el
szerettem volna 2x16-os és 4x20-as LCD kijelzõt vezérelni, és ennél hasonlóképpen problémába ütköztem a
digitalWrite() függvénnyel, így erre is ekészült a portlában módosításához egy-egy makró, íme:
#define LCDSH_clock PB8
#define LCDSH_en PB9
#define SET_CLK GPIOB->regs->BSRR = 1<<9
#define CLEAR_CLK GPIOB->regs->BRR = 1<<9
#define SET_EN GPIOB->regs->BSRR = 1<<8
#define CLEAR_EN GPIOB->regs->BRR = 1<<8
A fentiek alapján valószínûleg a többi arduino panel esetén is szükséges lehet a portok közvetlen írása. Nagy valószínûséggel
az ESP8266 (amivel elsõre próbálkoztam) az egyetlen ahol a digitalwrite() függvény sebessége elegendõ, ezért erre a problémára
csak utólag figyeltem fel.
Mivel már több különbözõ projektben is használtam ezt az LCD kijelzõ kezelési módszert, ezért jónak láttam a használt függvényeket
külön fájlokba rakni, hogy a fõprogramba egyszerûen #include "LCDsh.h" -val be lehessen szúrni.:
LCDsh.h:
#include <Arduino.h>
//
// LCD kijelzõ vezérlése 2 vezetékkel, 74HCT595 léptetõregiszteren keresztül
// SMT32-re optimalizált verzió
//
// vezérlõ lábak írás/olvasás definiálása, PB9 és PB8
#define LCDSH_clock PB8
#define LCDSH_en PB9
#define SET_CLK GPIOB->regs->BSRR = 1<<9
#define CLEAR_CLK GPIOB->regs->BRR = 1<<9
#define SET_EN GPIOB->regs->BSRR = 1<<8
#define CLEAR_EN GPIOB->regs->BRR = 1<<8
void lcdsh_wrbyte(unsigned char data, int16_t dly=0);
void lcdsh_Init();
void lcdsh_str(char * s); //c string kiírása
void lcdsh_str(char *s, uint8_t len); //c string kiírása, len karakteren (ha hosszabb levágja, ha rövidebb szóközzel kiegészíti)
void lcdsh_Line1(); //kurzor az elso sor elejére
void lcdsh_Line2(); //kurzor a 2. sor elejére
void lcdsh_Line3(); //kurzor a 3. sor elejére
void lcdsh_Line4(); //kurzor a 4. sor elejére
void lcdsh_Cursor (int8_t oszlop, int8_t sor); //kurzor adott sor (1,2,3,4)/oszlop (0 - 19) pozicióba
void lcdsh_num(int32_t szam, int8_t hossz, int8_t tizedesek=0); //egész szám kiírása, jobbra igazítva, az utosó számjegyek tizedesként (tizedesek nem kötelezõ)
void lcdsh_Ekezet(); //ékezetes karakterek betöltése
void lcdsh_str2(char *s, byte len); //ékezetes karaktereket is tartalmazó c string kiírása
void lcdsh_str3(char *s, byte len); //c string kiírása, ha len hossznál kisebb akkor középre igazítva
LCDsh.cpp (részlet):
#include "LCDsh.h"
// SMT32-re optimalizált verzió
void lcdsh_wrbyte(uint8_t data, int16_t dly){ // 1 bájt adat vagy parancs küldése az LCD-nek, 2 I/O lábon
for (uint8_t mask=128; mask ; mask >>=1){ // 8-szor fog lefutni
if (mask & data) SET_CLK; else CLEAR_CLK; // clock lábbeállítása az adatbitnek megfeleloen
delayMicroseconds(5); // néhány µs várakozás, hogy a kondi feltöltödjön vagy kisüljön
noInterrupts(); // a felfutó él elott tiltani kell a megszakításokat
CLEAR_CLK; SET_CLK ; // felfutó él az órajelben a "kondiban tárolt" bit beírásához a léptetoregiszterbe
interrupts(); // a felfutó él után ujra lehetnek megszakítások
}//end for // ADATBITEK beállítva
if (dly) CLEAR_CLK; else SET_CLK; //RS beállítása
SET_EN ; delayMicroseconds(2); CLEAR_EN; //EN impulzus: néhány µs
delayMicroseconds(28); //további késleltetés, hogy meglegyen a 43us/byte (a kijelzõ miatt kell)
if (dly > 50) delayMicroseconds(dly-50); //ha volt megadva várakozás, akkor várakozik (az LCD parancs befejezodésére)
}//end fv
void lcdsh_Init(){ //LDC kijelõ inicializálása
pinMode(LCDSH_clock, OUTPUT); //
pinMode(LCDSH_en, OUTPUT); //vezérlolábak beállítása: kimenet
lcdsh_wrbyte(B00111000,5000); //8bites mód, 2soros mód, 5msec várakozás
lcdsh_wrbyte(B00001000,39); //disp. off, 50usec
lcdsh_wrbyte(B00111000,39); lcdsh_wrbyte(B00111000,50); //8bites mód, 2sor , duplán kell kiadni a parancsot
lcdsh_wrbyte(B00001100,39); //disp. on, cursor off, blink off, 50usec
lcdsh_wrbyte(B00000001,1600); //clear, 2msec
lcdsh_wrbyte(B00000010,1600); //cursor home, 2msec
lcdsh_wrbyte(B00000110,39); //entry mod increase, no shift, 50usec
lcdsh_wrbyte(B10000000,39); //set DD ram address, 50usec
}//end fv
void lcdsh_str(char * s){ //string kiírása
for ( ; *s ; s++) lcdsh_wrbyte(*s); //a ciklus karakterenként küldi az LCD-nek a szöveget
}//end fv
void lcdsh_str(char *s, uint8_t len){ //string kiírása, len karakteren (ha hosszabb levágja, ha rövidebb szóközzel kiegészíti)
for ( ; *s && len; s++,len--) {lcdsh_wrbyte(*s); }
for ( ;len; len--) lcdsh_wrbyte(32);
}//end fv
Az LCDsh.h és az LCDsh.cpp fájlok letölthetõk itt: LCDsh.zip
A fájlokat elég bemásolni a projekt mappájába és az .ino fájlban, #include "LCDsh.h" sort betenni.
Skori
@2020.július.
Támogasd az oldalt!
A napokban megnéztem oszcilloszkóppal is az MCU lábain, az LCD kijelzõt meghajtó panelhez menõ jeleket. Íme:
Az oldal egyik kedves olvasója, János, megosztotta velem (és mint látható nem csak velem,
hanem a weboldal többi kedves olvasójával is) egy fejlesztését, amelyben mindössze egyetlen
adatvezeték használatával oldja meg az LCD kijelzõ és egy 16 gombból álló
billenytyûzet kezelését. Ehhez persze már egy felprogramozott mikrovezérlõre is szükség
van, ami a kijelzõ és a gombok kezelését megoldja, és amely 1wire protokollal kommunikál.
János levelébõl idézek néhány sort:
...
Elég sokszor, sok projekthez használok 2x16-os LCD-t. Gyakorta olyan formában is, hogy a kijelzõ akár méterekre van a vezérlõtõl.
Ezért fejlesztettem ki egy áramkört és egy programot, ami mindössze 3 vezetékkel (+5V, 0V, adat) kapcsolódik a kijelzõhöz,
valamint akár 16db nyomógombhoz. Azaz a kijelzõ vezérlõ egy 4x4-es mátrix beolvasását is elvégzi.
Diódákkal leválasztott nyomógombok esetén egyidejûleg 3 gomb lenyomását képes fogadni.
Fontos dolog volt még a zavarvédelem és a gyors adatáramlás.
Ezt úgy oldottam meg, hogy az 1-Wire kommunikációhoz 2 I/O lábat használok egy speciális áramköri kialakítással.
Így, mivel nem kell az I/O lábat adás és vétel között kapcsolgatni, rövidebb a program és gyorsabb a kommunikáció.
A kialakításának köszönhetõen pedig a szokásosnál jóval magasabb vonali áram miatt jobb a zavarvédettség.
A legrégebben megépített ilyen vezérlésem egy bádogosipari gépben mûködik több mint 4 éve.
Elküldöm neked a kapcsolási rajzot és a paneltervet is. Valamint a kijelzõvezérlõ programját.
...
ha arra érdemesnek tartod,megjelenítenéd az oldaladon.
...
Ehhez már csak annyit fûznék hozzá, hogy köszönjük! Jöjjön a kapcsolási rajz,
és a letölthetõ fájlok:
Tehát a nyákterv képként és az Eagle nyáktervezõ .brd formátumában,
a mikrovezérlõ programjának .asm forráskója, és a kapcsolási rajz,
egyben letölthetõ ITT: ZES.ZIP