SKORI WEBLAPJA
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:
  1. 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.
     
  2. 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.
     
  3. 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!


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