View on GitHub

Arduino projekty pro děti

Zápisky z našich domácích Arduino projektů

← zpět na zápisky z Arduino projektů

OLED

Naše první pokusy s OLED displejem. Já vím, zkratka OLED už sama o sobě v sobě obsahuje slovo displej, ale stejně tomu tak všichni říkají :-) OLED mají nízkou spotřebu a dobrý zobrazovací kontrast (svítí jen to, co svítit musí). Tak jsme taky nějaké chtěli vyzkoušet.

Co je potřeba umět

Rozhodně se hodí vědět, jaký přesně displej vlastně držím v ruce. To usnadní jeho ovládání :-) Jinak nejsou potřeba asi žádné speciální znalosti.

Hardware

Jak to funguje

OLED displej, který jsme využili, má úhlopříčku 0,96” a rozlišení 128×64 pixelů. Připojili jsme ho přes rozhraní I2C. To komunikuje na 2 pinech SDA (data) a SCL/SCK (clock). Tyto má Arduino Uno schováno na analogových pinech A4 (SDA) a A5 (SCL/SCK) (zdroj).

K ovládání displeje se dále hodí knihovna u8g. Asi nejtěžším úkolem bylo zjistit, co přesně máme za displej a jakým konstruktorem knihovnu inicializovat. Nutno říci, že na displeji samotném jsme příliš vodítek nenašli.

Někdy můžete natrefit na displej, který je dvoubarevný. Nejde většinou o plnohodnotnou dvoubarevnost, ale jistá část displeje se zobrazuje vždy pevně jednou barvou a zbytek druhou. Barvu tak nenastavujete při vykreslování jako parametr, ale volíte ji umístěním na displeji.

Texty se na displeji vypisují metodou drawStr. Fonty se nastavují metodou setFont a je z čeho vybírat.

Následně děti projevily velký zájem zobrazovat na displej i něco jiného než jen texty různých velikostí. Postup je následující:

  1. Namalovat obrázek ve svém oblíbeném grafickém programu. Je dobré si nastavit velikost papíru na rozlišení displeje (např. 128×64), abychom se pak vešli. Pokud je displej jednobarevný či falešně dvoubarevný, je dobré kreslit pouze černou na bílem podkladu. Výsledný obrázek ořezat na minimální velikost.
  2. Pomocí programu pro převod bitmapy do kódu převést obrázek. Převod je vlastně pouhý převod černých pixelů na zápis v šestnáctkové soustavě (hexa) do zdrojového kódu tak, aby bylo možné ho nahrát do displeje.
  3. K převodu jsme využili program Image2Code napsaný v Javě (stačí pouze soubor Image2Code.jar, neinstaluje se, stačí spustit). Pokud Javu na počítači zatím nemáte, je potřeba doinstalovat. Pro Linux třeba takto:
    sudo apt-get install default-jre
    

    Program se pak spouští z příkazové řádky

    java -jar Image2Code.jar
    

Ale je samozřejmě možné využít i jakýkoli jiný program, např. LCD image converter, který běhá na Windows, ale je možné ho zkompilovat i pro Linux - a celkově vypadá vymazleně.

Výsledný hexa kód nakopírovat do zdrojového souboru pro Arduino.

const uint8_t veselySmajl[] PROGMEM = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,
...
...
...
};

Klíčové slovo PROGMEM zde zařídí, že celá bitmapa nebude uložena v operační paměti Arduina - té máme totiž obvykle velmi málo (Uno R3 = 2 KiB), ale protože se nebude v programu měnit (je konstantní), může být umístěna do tzv. flash paměti, kde leží i náš program (Uno R3 = 32 KiB). Více o rozdělení a druzích pamětí si lze přečíst zde, práce s PROGMEM je vysvětlena zde.

Ještě potřebujeme vědět, jaké má obrázek rozměry. Šířku vydělíme osmi a zaokrouhlíme nahoru (= počet bajtů, které jsou na řádku), výšku pak necháme, jak je. Pro vykreslení slouží funkce drawBitmapP().

//sirka obrazku = 65 -> 9 bajtu na radek
// vyska obrazku = 57
drawBitmapP(5, 5, 9, 57, veselySmajl);

Možná někoho napadne, proč není potřeba následně ve volání drawBitmapP kopírovat data bitmapy z flash paměti do operační (pomocí nějaké z metod rozhraní pgmspace), když to návody u klíčového slova PROGMEM ukazují. Je to proto, že knihovna u8g již předpokládá, že bitmapu ve flash paměti máme.

Ukázka z kódu knihovny v těle drawBitmapP

u8g_Draw8Pixel(u8g, x, y, 0, u8g_pgm_read(bitmap));

Knihovna u8g využívá koncept redraw - je potřeba neustále obnovovat stav displeje opakovaným překreslováním.

void loop(void) {
  u8g.firstPage();  
  do {
    draw();
  } while(u8g.nextPage());

  // po nejake dobe prekresli displej
  delay(1000);
}

Schéma zapojení

oled.fzz

oled

Program s textem

oled_txt.ino

#include "U8glib.h"

// inicializace OLED
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);

void setup() {
  // nic
}

// vypise vysledek na displeji
void vypis() {
    u8g.setFont(u8g_font_fub25n);
    u8g.drawStr(40, 30, "BAF!");
    u8g.setFont(u8g_font_unifont);
    u8g.drawStr(45, 50, "strasidlo");
}

void loop() {
  u8g.firstPage();
  do {
    vypis();
  } while (u8g.nextPage());

  delay(1000);
}

Program s obrázkem

oled_pic.ino

#include "U8glib.h"

// inicializace OLED
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);

const uint8_t vesely[] PROGMEM = {
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x7f,0xff,0x80,0x0,0x0,0x0,
0x0,0x0,0x7,0xff,0xff,0xf8,0x0,0x0,0x0,
0x0,0x0,0x1f,0x80,0x0,0x7e,0x0,0x0,0x0,
0x0,0x0,0x7c,0x0,0x0,0xf,0x80,0x0,0x0,
0x0,0x1,0xe0,0x0,0x0,0x1,0xe0,0x0,0x0,
0x0,0x3,0x80,0x0,0x0,0x0,0x70,0x0,0x0,
0x0,0xf,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,
0x0,0x1c,0x0,0x0,0x0,0x0,0xe,0x0,0x0,
0x0,0x38,0x0,0x0,0x0,0x0,0x7,0x0,0x0,
0x0,0x70,0x0,0x0,0x0,0x0,0x3,0x80,0x0,
0x0,0xe0,0x0,0x0,0x0,0x0,0x1,0xc0,0x0,
0x1,0xc0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,
0x3,0x80,0x0,0x0,0x0,0x0,0x0,0x70,0x0,
0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,
0x6,0x0,0x7,0x80,0x0,0x3c,0x0,0x18,0x0,
0x6,0x0,0x1f,0xe0,0x0,0xff,0x0,0x18,0x0,
0xc,0x0,0x3f,0xf0,0x1,0xff,0x80,0xc,0x0,
0xc,0x0,0x70,0x38,0x3,0x81,0xc0,0xc,0x0,
0x18,0x0,0xe0,0x1c,0x7,0x0,0xe0,0x6,0x0,
0x18,0x0,0xe0,0x1c,0x7,0x0,0xe0,0x6,0x0,
0x18,0x0,0xe3,0x1c,0x7,0x18,0xe0,0x6,0x0,
0x30,0x0,0xe7,0x9c,0x7,0x3c,0xe0,0x3,0x0,
0x30,0x0,0xe7,0x9c,0x7,0x3c,0xe0,0x3,0x0,
0x30,0x0,0xe7,0x9c,0x7,0x3c,0xe0,0x3,0x0,
0x30,0x0,0xe3,0x1c,0x7,0x18,0xe0,0x3,0x0,
0x30,0x0,0xe0,0x1c,0x7,0x0,0xe0,0x3,0x0,
0x30,0x0,0xe0,0x1c,0x7,0x0,0xe0,0x3,0x0,
0x30,0x0,0x70,0x38,0x3,0x81,0xc0,0x3,0x0,
0x30,0x0,0x3f,0xf0,0x1,0xff,0x80,0x3,0x0,
0x30,0x10,0x1f,0xe0,0x0,0xff,0x0,0x3,0x0,
0x30,0x18,0x7,0x80,0x0,0x3c,0x0,0x3,0x0,
0x30,0x18,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
0x30,0x1c,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
0x30,0x1c,0x0,0x0,0x0,0x0,0x0,0x3,0x0,
0x18,0xe,0x0,0x0,0x0,0x0,0x0,0x6,0x0,
0x18,0xf,0x0,0x0,0x0,0x0,0x0,0x6,0x0,
0x18,0x7,0x80,0x0,0x0,0x0,0x0,0x6,0x0,
0xc,0x3,0xc0,0x0,0x0,0x0,0x70,0xc,0x0,
0xc,0x1,0xe0,0x0,0x0,0x0,0xf0,0xc,0x0,
0x6,0x0,0xf8,0x0,0x0,0x1,0xe0,0x18,0x0,
0x6,0x0,0x7f,0x0,0x0,0x3,0xc0,0x18,0x0,
0x3,0x0,0x1f,0x80,0x0,0x7,0x80,0x30,0x0,
0x3,0x80,0x7,0xe0,0x0,0x1f,0x0,0x70,0x0,
0x1,0xc0,0x1,0xfc,0x0,0xfe,0x0,0xe0,0x0,
0x0,0xe0,0x0,0x7f,0xff,0xf8,0x1,0xc0,0x0,
0x0,0x70,0x0,0x1f,0xff,0xe0,0x3,0x80,0x0,
0x0,0x38,0x0,0x0,0xfe,0x0,0x7,0x0,0x0,
0x0,0x1c,0x0,0x0,0x0,0x0,0xe,0x0,0x0,
0x0,0xf,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,
0x0,0x3,0x80,0x0,0x0,0x0,0x70,0x0,0x0,
0x0,0x1,0xe0,0x0,0x0,0x1,0xe0,0x0,0x0,
0x0,0x0,0x7c,0x0,0x0,0xf,0x80,0x0,0x0,
0x0,0x0,0x1f,0x80,0x0,0x7e,0x0,0x0,0x0,
0x0,0x0,0x7,0xff,0xff,0xf8,0x0,0x0,0x0,
0x0,0x0,0x0,0x7f,0xff,0x80,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
};

int sirka_displeje = 128;
int vyska_displeje = 64;

int sirka_obrazku = 65;
int vyska_obrazku = 57;

// vycentrovane souradnice pro obrazek
int x = (sirka_displeje - sirka_obrazku) / 2;
int y = (vyska_displeje - vyska_obrazku) / 2;

void setup() {
  // nic
}

// vypise vysledek na displeji
void vykresli() {
  //x = x souradnice, kde zacne kreslit obrazek (0;0 - levy horni roh)
  //y = y souradnice, kde zacne kreslit obrazek (0;0 - levy horni roh)
  //pocet bajtu na sirku obrazku
  //vyska obrazku
  //obrazek
  int pocet_bajtu_sirka_obrazku = (int)(sirka_obrazku / 8.0 + 0.5);
  u8g.drawBitmapP(x, y, pocet_bajtu_sirka_obrazku, vyska_obrazku, vesely);
}

void loop() {
  u8g.firstPage();
  do {
    vykresli();
  } while (u8g.nextPage());

  delay(1000);
}

Možná vylepšení

Poznatky