Vyhledávání

#13 - Arduino a displeje II.

11.05.2014 20:00

Arduino a displeje II.

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 8x1 (jeden řádek s osmi znaky) až 40x4 (č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.

Různé velikosti znakových LCD displejů
Obr. 1: Různé velikosti znakových LCD displejů

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.

Rozmístění znaků na znakovém LCD displeji
Obr. 2: Rozmístění znaků na znakovém LCD displeji

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 naším 20x4 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.

Schéma zapojení LCD displeje
Obr. 3: Schéma zapojení LCD displeje
Zapojení LCD displeje
Obr. 4: Zapojení LCD displeje

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 u GLCD
Obr. 5: Zapojení potenciometru u GLCD

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);
}
Hotový program
Obr. 6: Výsledek

 


Barevné grafické LCD

Nyní už se konečně dostáváme k barevným displejům. Představíme si náš dotykový displej s úhlopříčkou 2,8 palce a 320x240 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 Leonardo s TFT displejem
Obr. 7: Arduino Leonardo s TFT displejem

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);
}
Malování
Obr. 8: Výsledek

 


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.

 

Kam pokračovat?


<-- #12 - Arduino a displeje I. || #14 - Arduino projekt: 2048 -->
Zpět

Diskusní téma: #13 - Arduino a displeje II.

Datum
Vložil
Titulek

Znakový displej 16x2

Dobrý deň. Mám znakový displej s 16 stlpcami a 2 riadkami. Keď chcem niečo na ňom vypisať, chová sa ako keby mal 20 stlpsov 4 riadky. Teda po 16. znaku nepiše na nasledujuci riadok, ale až po 20. (4 znaky sa nevypíšu). to iste sa deje s dvomi riadkami. Chova sa ako by mal 4 riadky. V príkaze lcd.begin(); som nastavil
počet riadkov a stlpcov, ale aj tak robí to isté. Vážne už neviem čo robiť. Popredu ďakujem za odpoveď.

Datum
Vložil
Titulek

Leonardo - 2.8" TFT Touch Shield V2.0 - Tftv2.h knihovna

Dobrý den, jsem trošku zoufalý. Měl jsem takovou představu, že si vytvořím pro můj projekt několik menších objektů třeba 60x60 pix., a budu si je postupně podle potřeby zobrazovat na LCD jako ikony pro dotykové tlačítka. Ale touto knihovnou jdou pouze nahrát obrázky 320x240 pix., né méně. Nebo jsem nepřišel na to jak.
Je vůbec nějaká možnost jak zobrazovat menší objekty na tento typ TFT LCD?
Pokud ano prosím o nějaký demo kód kterého se budu moc chytnout.
Příklad z této knihovny (tftbmp) funguje krásně, ale to je vše.
Předem děkuji za jakoukoli pomoc.
miloslav2007@gmail.com

© 2015 Všechna práva vyhrazena.

www.hwkitchen.com