Close

Arduino a displeje II.

Arduino s připojeným TFT shieldem

Ve druhém článku o displejích se dostaneme k LCD displejům. Popíšeme si znakové i grafické displeje a jak s nimi pracovat.

Úvod

Od jednodušších maticových displejů se dnes dostaneme ke (konstrukčně) složitějším displejům LCD. Ty v současné době nalezneme téměř všude. Stačí rozebrat například rozbitý fax a téměř jistě v něm nějaký nalezneme.

LCD displeje

Jak už jsem zmínil před chvílí, existuje celá řada LCD displejů. Všeobecně se užívají dva typy dělení. První z nich rozděluje LCD displeje do dvou skupin na znakové a grafické. Druhý z nich je dělí na barevné a monochromatické (jednobarevné). My si nyní jednotlivé skupiny představíme.

Znakové LCD

Tato skupina displejů je snazší na ovládání. Ovladač jim totiž zasílá pouze informace o tom, jaký znak mají kde zobrazit. Tyto znaky jsou předem definované – displej obsahuje základní „slovník“ znaků. Existuje celá řada velikostí displejů, které se však neudávají v počtech pixelů, ale v počtu řádků a míst pro znaky, kdy každé místo má na displeji přesně dané umístění. Můžeme se tedy běžně setkat s displeji 8×1 (jeden řádek s osmi znaky) až 40×4 (čtyři řádky po čtyřiceti znacích). Ovládání poté probíhá tak, že nastavíme kurzor na místo, na které chceme znak napsat a poté ho odešleme. Znakové displeje jsou většinou monochromatické. Můžeme se ale setkat s různými barvami podsvícení displeje.

 
 
Příklady znakových LCD displejů
LCD displeje

Pro lepší představu níže vidíme umístění míst pro znaky na displeji – každý obdélník odpovídá jednomu místu.

 
Buňky znakového LCD
Maximální kontrast

Pro práci se znakovými LCD displeji je Arduino vybaveno knihovnou, která je zahrnutá již v základním balíku IDE. Ta umožňuje ovládání všech znakových displejů, které jsou kompatibilní s řadičem Hitachi HD44780, což většina současných displejů je. Tyto displeje mají většinou šestnáct pinů. V tomto příkladu budeme pracovat s tímto 20×4 LCD displejem. Pokud se podíváme na jeho zadní stranu, nalezneme zde piny popsané 1 a 16. Pojďme si je nyní představit.

Číslo pinu Symbol Popis
1 VSS, GND GND napájení displeje
2 VDD, VCC +5V napájení displeje
3 V0 Pin pro nastavení kontrastu LCD (bude vysvětleno později)
4 – 6 RS, R/W, E Řízení řadiče
7 – 14 DB0 – DB7 Datové piny
15 LED+ Anoda podsvícení displeje
16 LED- Katoda displeje

Poznámka: Některé starší displeje nemusejí mít podsvícení – piny 15 a 16 u nich tedy nenajdeme.

Displej zapojíme podle schématu. Také můžeme přes 10 Ohm rezistor připojit napájení k podsvícení. Potenciometr zde slouží k nastavení kontrastu displeje. Rezistor i potenciometr jsou součástí naší sady.

 
Zapojení znakového LCD displeje a Arduina
Zapojení znakového LCD displeje
Zapojení grafického LCD a Arduina
Znakový LCD

Jak už jsem řekl dříve, obsahuje Arduino IDE pro komunikaci se znakovými LCD knihovnu. Ta má pro ovládání displeje několik funkcí. Použití některých z nich si ukážeme na příkladu.

Funkce Popis
LiquidCrystal lcd() Vytvoří objekt s názvem lcd pro práci s displejem. Jako parametry se udávají piny, na které je připojen displej. Více informací o různých kombinacích parametrů nalezneme v dokumentaci
lcd.begin(s,ř) Zahájí práci s displejem. Parametry jsou: počet sloupců a počet řádků.
lcd.clear() Tato funkce smaže všechny zobrazené znaky na displeji a nastaví kurzor do levého horního rohu.
lcd.home() Nastaví kurzor do levého horního rohu.
lcd.setCursor(s,ř) Nastaví kurzor na danou pozici – sloupce, řádky.
lcd.write(znak) Vypíše na displej jeden znak. Pozice kurzoru se posune o jedno místo doprava (v základním nastavení).
lcd.print(data) Vypíše na displej řetězec, nebo číslo. Pozice kurzoru se posune o počet zobrazených znaků doprava (v základním nastavení).
lcd.cursor() Zobrazí na displeji pozici kurzoru podtržením znaku, na kterém je nastaven.
lcd.noCursor() Skryje zobrazený kurzor.
lcd.blink() Zobrazí blikající kurzor.
lcd.noBlink() Skryje blikající kurzor.
lcd.noDisplay() Skryje všechny zobrazené znaky, ale nesmaže je. Komunikace s displejem nadále probíhá. Můžeme zapisovat znaky, které si displej pamatuje, jen je nezobrazí.
lcd.display() Zobrazí vše, co bylo skryto funkcí .noDisplay() pokud mezitím došlo ke změně znaků na displeji, zobrazí se stav po změně.
lcd.scrollDisplayLeft() Posune všechny zobrazené znaky o jedno místo doleva.
lcd.scrollDisplayRight() Posune všechny znaky doprava.
lcd.leftToRight() Nastaví automatický posun kurzoru po vypsání znaku doprava (což je výchozí stav).
lcd.rightToLeft() Nastaví automatický posun kurzoru po vypsání znaku doleva.
lcd.createChar(cislo, data) Tato funkce přináší možnost vytvoření vlastního znaku. Parametr data obsahuje informace o znaku (bude vysvětleno v příkladu). Cislo nám říká, pod jaké číslo se uloží do „slovníku“ znaků. To může nabývat hodnot 0 až 15. Pod tímto číslem jej poté můžeme pomocí funkce .write() zobrazit.

V prvním příkladu si vypíšeme na disleji počet vteřin od začátku běhu programu.

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //nastavení pinů

void setup() {
    lcd.begin(20, 4); //inicializace displeje
}

void loop() {
    if(millis() % 1000 == 0){
        lcd.clear();
        lcd.setCursor(9,1);
        lcd.print(millis()/1000);
    } 
}	

Nyní si ukážeme, jak se používá funkce .createChar(). Pod hodnotu 1 si vytvoříme znak dvou trojúhelníků. Námi definovaný znak se funkci předá jako jednorozměrné pole čísel (pro jednoduchou editaci v binární soustavě). V některých programovacích prostředích se pro zápis čísla ve dvojkové soustavě používá syntaxe 0b1111 (což odpovídá desítkové hodnotě 15). Arduino IDE však tento zápis neumožňuje. Pro pohodlnější práci s nižšími binárními čísly však v prostředí nalezneme několik předdefinovaných konstant začínajících velkým písmenem B. Konkrétně se jedná o rozsah hodnot od B0 (= 0) až B11111111 (= 255). Konstanta B00000010 tedy odpovídá hodnotě 2. Více o číselných soustavách si můžete přečíst na Wikipedii.

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

byte znak[8] = {B11111,
                B01010,
                B00100,
                B00000,
                B00000,
                B00100,
                B01010,
                B11111};

void setup() {
    lcd.createChar(1, znak);
    lcd.begin(20, 4);  
    lcd.write(1);
}

void loop() {}

Ve třetím příkladu se na displej vypíše text, který mu pošleme po sériové lince. K určení pozice na displeji slouží pomocná proměnná i. Pokud je celý displej zaplněn, smaže se a kurzor se vrátí do levého horního rohu.

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

int i = 0;

void setup() {
    Serial.begin(9600);
    lcd.begin(20,4);
}

void loop() {
    while(Serial.available() > 0){
        lcd.setCursor(i%20, (i-i%20)/20);
        lcd.write(Serial.read());
        i++;
        if(i == 80){
            i = 0;
            lcd.clear();
        }
    }
}

Poznámka: Existují i znakové displeje se sériovým řadičem. Jeden z nich naleznete například v našem setu. Těm se ale dnes nebudeme věnovat.

Grafické monochromatické LCD

Další skupinou displejů jsou grafické displeje, z nichž jednodušší jsou ty jednobarevné. My se jimi budeme zabývat pouze zběžně. Více se zdržíme až u barevných displejů. V tomto článku si ukážeme použití displeje ATM12864D (128 x 64 pixelů) s Arduinem Mega. Tento displej obsahuje řadič KS0107B, k jehož ovládání je pro Arduino napsaná knihovna (i pro typ A a C). Tu stáhneme z archivu knihovny. Dalším důležitým dokumentem je datasheet displeje, ve kterém najdeme funkce jednotlivých pinů. Posledním dokumentem, který budeme potřebovat je dokumentace knihovny.

Displej s Arduinem propojíme podle následující tabulky. Pro jiné typy desek je zapojení popsáno v dokumentaci knihovny na straně 2.

Displej Arduino Mega   Displej Arduino Mega   Displej Arduino Mega
1 GND   8 23   15 33
2 +5V   9 24   16 34
3 nepřipojeno   10 25   17 RESET
4 36   11 26   18 nepřipojeno
5 35   12 27   19 nepřipojeno
6 37   13 28   20 GND
7 22   14 29

Piny 3, 18 a 19 nejsou připojeny k Arduinu. Zapojení pinů 3 a 18 s potenciometrem je naznačeno na následujícím obrázku. Pin 19 je připojen na +5V přes 220 ohm Rezistor.

Zapojení potenciometru
Zapojení potenciometru

Všechny funkce jsou přehledně popsány v již zmíněné dokumentaci. My si ukážeme, jak jednoduše na displej vykreslit graf hodnot naměřených na pinu A0.

#include <glcd.h>

int data[128];

void setup(){
    GLCD.Init(); //inicializace displeje
}

void loop(){
    data[0] = map(analogRead(A0), 0, 1023, 0, 63);

    for(int i = 127; i > 0; i--){
        bod(i,data[i]);
        data[i] = data[i-1];
    }
    
    delay(40);
    
    GLCD.ClearScreen();
}

void bod(int x, int y){
    y = 63 - y; //převrácení os
    GLCD.SetDot(x,y, BLACK); //nastavení bodu
    GLCD.SetDot(x+1,y, BLACK);
    GLCD.SetDot(x+1,y-1, BLACK);
    GLCD.SetDot(x,y-1, BLACK);
}
Graf naměřených hodnot z Arduina
Graf naměřených hodnot

Barevné grafické LCD

Nyní už se konečně dostáváme k barevným displejům. Představíme si dotykový displej s úhlopříčkou 2,8 palce a 320×240 pixely. Nebudeme se tedy zabývat pouze zobrazováním, ale i interakcí s dotykovou plochou. Ta nás bude zajímat jako první. Pro její funkci budeme potřebovat knihovnu Touch Screen Driver.

Arduino s připojeným TFT shieldem
Arduino a TFT Shield

Vytvoříme si program, který nám odešle souřadnice právě zmáčknutého bodu. Na začátek programu vložíme kód, který za nás určí typ Arduina (v tomto případě Leonardo) a nastaví piny použité pro dotykovou vrstvu. Část kódu společně s vložením knihoven, vytvořením objektu displeje a kalibrací dotykové plochy vypadá následovně.

#include <stdint.h>
#include <SeeedTouchScreen.h> 

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) // mega
#define YP A2 
#define XM A1 
#define YM 54 
#define XP 57 

#elif defined(__AVR_ATmega32U4__) //leonardo
#define YP A2 
#define XM A1
#define YM 18 
#define XP 21

#else
#define YP A2 
#define XM A1
#define YM 14 
#define XP 17 

#endif

TouchScreen ts = TouchScreen(XP, YP, XM, YM);

Funkcí definic se nemusíme zabývat. Hlavní je, že nám tato část kódu zajistí správný chod displeje. Dalším krokem je kalibrace dotykové plochy. Na displeji je použita rezistivní technologie dotykové membrány. Její princip by se dal zjednodušeně popsat tak, že tlak na pružnou membránu se projeví určitým elektrickým odporem měřeným na této membráně. V ideálním případě by se například při tlaku v bodě [10,20] naměřil odpor na membráně měřící odpor v ose x 10 ohm a u y 20 ohm. Naše plocha ale není takto ideální a musíme ji tedy nějakým způsobem zkalibrovat. Přibližné hodnoty maximální a minimální hodnoty odporu na osách vidíte v tabulce.

  minimum maximum
X 232 1780
Y 166 1826

V další části tedy musíme do kódu zahrnout údaje pro kalibraci.

int min_x = 232;
int max_x = 1780;
int min_y = 166;
int max_y = 1826;

int x, y;

void setup(){
    Serial.begin(9600);
}

V dalším kroku vytvoříme objekt pro bod, se kterým budeme dále pracovat. Jak se zachází s objekty jsme si vyzkoušeli už při programování v Processing, takže to pro nás nebude žádné překvapení. Bod má tři vlastnosti: x, y odpovídající odporu na souřadnicích a z, která uchovává informaci o tlaku (s ní se dá poté do programu zakomponovat i citlivost dotyku). Pro získání souřadnic x a y musíme porovnat naměřené hodnoty s údaji pro kalibraci. Poté je už můžeme po sériové lince vypsat.

void loop(){
    Point p = ts.getPoint();
    
    x = map(p.x, min_x, max_x, 0, 239);
    y = map(p.y, min_y, max_y, 0, 319);
    
    if(p.z > 10){
        Serial.print(x);
        Serial.print(':');
        Serial.println(y);
    }
    
    delay(500);
}	

Poznámka: Kalibrace dotykové plochy displeje se dá provést získáním naměřených hodnot pomocí funkce .getPoint a postupním přejížděním os z minima do maxima. Poté stačí najít maximální a minimální hodnotu pro obě osy a změnit kalibrační proměnné.

Už jsme zjistili, jak se dá z displeje zjistit souřadnice stlačeného bodu. Druhá (a důležitější) část je práce se samotnou zobrazovací plochou. I k tomuto účelu existuje pro náš displej knihovna, kterou stáhneme zde. Ta nám přináší funkce pro jednodušší práci s displejem. Níže vidíte některé z nich.

Funkce Popis
Tft.TFTinit() Inicializuje displej.
Tft.setPixel(x, y, barva); Vykreslí bod na daných souřadnicích.
Tft.drawCircle(x, y, r, barva) Nakreslí kruh dané barvy se středem v [x,y] o poloměru r. Více o barvách níže.
Tft.fillCircle(x, y, r, barva) Stejné jako předchozí funkce, pouze se zobrazí místo kruhu kružnice.
Tft.drawLine(x1, y1, x2, y2, barva) Nakreslí úsečku dané barvy z bodu [x1,y1] do [x2, y2]
Tft.drawVerticalLine(x, y, d, barva) Nakreslí vertikální úsečku dané barvy s počátkem v bodu [x, y] o délce d.
Tft.drawHorizontalLine(x, y, d, barva) Stejné jako u předchozí funkce, pouze bude výsledná úsečka horizontální.
Tft.drawNumber(cislo, x, y, v, barva) Vypíše číslo typu int dané barvy na souřadnicích x, y o velikosti v.
Tft.drawFloat(cislo, x, y, v, barva) Stejné jako u předchozí funkce. Zobrazí číslo typu float.
Tft.drawRectangle(x,y,delka,vyska,barva) Zobrazí okraje obdélníku dané barvy s levým horním rohem v souřadnicích x a y o délkách hran delka a vyska.
Tft.fillRectangle(x,y,delka,vyska,barva) Stejné jako předchozí funkce, pouze s tím rozdílem, že bude výsledný útvar vyplněný.
Tft.fillScreen(x_levo, x_pravo, y_dole, y_nahore, barva) Vyplní displej mezi souřadnicemi danou barvou.
Tft.drawTraingle(x1, y1, x2, y2, x3, y3, barva) Zobrazí trojúhelník dané barvy s vrcholy [x1,y1], [x2, y2], [x3, y3].
Tft.drawChar(znak, x, y, v, barva) Zobrazí znak dané barvy na souřadnicích [x,y] o velikosti v.
Tft.drawString(retezec, x, y, v, barva) Vypíše řetězce dané barvy na souřadnicích [x,y] velikosti v.
Tft.setDisplayDirect(smer) Nastaví směr textu. Smer může mít hodnoty: LEFT2RIGHT, RIGHT2LEFT, DOWN2UP a UP2DOWN.

Displej umí pracovat s 216 (65536) různými barvami. Informace o barvě jednoho pixelu tedy zabere 16 bitů. Červené a modrá barva mají každá pět bitů, zelená má bitů šest, protože je na ní lidské oko více citlivé, tudíž rozezná více jejích rozdílů. Tento způsob míchání barev se nazývá 16-bit high color a více se o něm můžete dočíst na anglické Wikipedii. Červená a modrá barva tedy můžou mít 32 různých sytostí, na zelenou jich připadá 64. Knihovna obsahuje několik předdefinovaných konstant barev. Jsou to:

Barva Název konstanty HEX kód
červená RED 0xf800
zelená GREEN 0x07e0
modrá BLUE 0x001f
černá BLACK 0x0000
žlutá YELLOW 0xffe0
bílá WHITE 0xffff
azurová CYAN 0x07ff
purpurová BRIGHT_RED 0xf810
šedá GRAY1 0x8410
šedá GRAY2 0x4208

Pokud by nám nestačila výchozí paleta, můžeme si vytvořit i barvy vlastní. Jeden ze způsobů vidíte na příkladu. Jedná se o trošku pokročilejší programování, na které přijde řeč možná až časem. S čísly se zde pracuje na úrovni jednotlivých bitů. Důležité ale je to, že funkce barva má tři parametry – r, g a b pro jednotlivé barvy a vrací číslo barvy. Parametry r a b mohou nabývat hodnot od 0 do 31. Parametr g 0 až 63. Následující program zobrazí na displeji tři kružnice tří základních barev.

#include <stdint.h>
#include <TFTv2.h> //knihovna displeje
#include <SPI.h> //knihovna pro komunikaci s displejem

void setup(){
    Tft.TFTinit();
    
    Tft.drawCircle(120, 60, 20, barva(31,0,0));
    Tft.drawCircle(120, 160, 20, barva(0,63,0));
    Tft.drawCircle(120, 260, 20, barva(0,0,31)); 
}

void loop(){

}

uint16_t barva(int r, int g, int b){
    r = r % 32;
    g = g % 64;
    b = b % 32;    
    
    r = r << 11;
    g = g << 5;
    
    return r | g | b;
}

Jednotlivě jsme již vyřešili dotykovou plochu i displej. Nyní pojďme dát vše dohromady. Vytvoříme si aplikaci pro jednoduché malování černou barvou na bílé pozadí. Aplikace bude obsahovat také tlačítko reset.

#include <stdint.h>
#include <SeeedTouchScreen.h>
#include <TFTv2.h>
#include <SPI.h>

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) // mega
#define YP A2 
#define XM A1 
#define YM 54 
#define XP 57 

#elif defined(__AVR_ATmega32U4__) //leonardo
#define YP A2 
#define XM A1
#define YM 18 
#define XP 21

#else
#define YP A2 
#define XM A1
#define YM 14 
#define XP 17 

#endif

TouchScreen ts = TouchScreen(XP, YP, XM, YM);

int min_x = 232;
int max_x = 1780;
int min_y = 166;
int max_y = 1826;

int x, y;

void setup(){
    Tft.TFTinit();
    
    Tft.fillScreen(10,230,10,230, WHITE);
    Tft.fillScreen(10,230,240,310, GRAY2);
    
    Tft.drawString("RESET", 45, 255, 5, BLACK);
}

void loop(){
    Point p = ts.getPoint();
    
    x = map(p.x, min_x, max_x, 0, 239);
    y = map(p.y, min_y, max_y, 0, 319);
    
    if(p.z > 10){
        if(x >= 10 && x <= 220 && y >= 250 && y <= 310){
            Tft.fillScreen(10,230,10,230, WHITE);
        }
        else if(x >= 10 && x <= 220 && y >= 10 && y <= 230 ){
            Tft.fillRectangle(x,y,2,2,BLACK);
        }
    }

    delay(1);
}
Použití TFT shieldu s Arduinem
Výsledný program

Zdroje obrázků

[Zapojení znakového LCD]

V případě jakýchkoliv dotazů či nejasností se na mě neváhejte obrátit v komentářích.

Zbyšek Voda

7 Comments on “Arduino a displeje II.

Inheritance123
30.11.2016 at 20:51

Ahoj, jsem do používání LCD displejů úplnej začátečník, ale s arduinem už nějakou dobu dělám, koupil jsem z Ebaye tenhle displej: http://www.ebay.com/itm/311523810931?_trksid=p2060353.m2749.l2649&ssPageName=STRK%3AMEBIDX%3AIT , jenže jsem nenašel navod přimo pro tenhle displej ale pouze pro nějakej podobnej a mám problém s tim kde k tomu vzít knihovny jestli budou fungovat z nějakejch jinejch displejů a taky si nejsem uplně jistej jestli jsou zde všechny piny ( teda asi s vyjimkou 1 nebo 2 na 5V) na 3,3 V kdyby se našla nějaká rada byl bych moc vdecnej, předem díky =)

Zbyšek Voda
15.12.2016 at 16:05

Dobrý den, přímo na EBay dole na stránce produktu je ZIP archiv, který by měl obsahovat i nějaké příklady. To vám nefunguje? 🙂

Zbyšek Voda
15.12.2016 at 16:08

Dobrý den, zkuste se podívat sem: http://www.buydisplay.com/default/7-inch-arduino-touch-screen-shield-ssd1963-library-for-mega-due
Dole na stránce je ZIP archiv s příklady. Displej by měl mít stejný řadič.

Jen pozor, abyste na piny displeje nepustil více než 3.6V.

Václav
9.8.2016 at 15:45

dobrý den jsem začínající začátečník a mám problém se znakovým LCD displejem, tedy spíše s jeho programování, při nahrátí jednoduchého programu se mi dolů do chybových hlášeních napíše něco v tomto smyslu: avrdude: žádný programátor není zadán na příkazovém řádku nebo config souboru zadejte programátor pomocí možnosti – c a opakujte akci a program se nechce nahrát, na display se píší různé znaky např.: otazníky a šipky aniž bych to chtěl co s tím mám dělat aby šel nahrát program který chci? předem děkuji za odpověď

Zbyšek Voda
12.8.2016 at 12:14

Dobrý den, opravdu to dělá ten znakový displej, nebo nejde nahrát do Arduina žádný program?
Zkuste například nahrát příklad Blink.

Karel
8.11.2015 at 20:50

Ahoj – měl bych dotázek – nikde jsem to nenašel… Dá se pomocí knihovny LiquidCrystal, která se využívá s LCD displeji zobrazovat tzv. invertovaný text ? (tzn. na tmavém pozadí světlý text) ? Díky za info, neobjevil jsem tuto funkcionalitu v manuálu k této knihovně… ?
s pozdravem Karel Jurkovič.

Kobo
7.1.2016 at 17:00

Ale urcite sa to da, funkcia pre vypisanie textu ma dva parametre: farbu textu a farbu pozadia. Tym padom si viem zvolit aj biele pismo a cierne pozadie.

Napsat komentář