Fyzikální kabinet FyzKAB
TechHobby ESP32 + MicroPython ESP32: Čidla a senzory v MicroPythonu SPI OLED displej SSD1306 v MicroPythonu

SPI (I²C) OLED displej SSD1306 v MicroPythonu

V dnešním článku si ukážeme, jak v MicroPythonu připojit k modulu ESP32 OLED displej s řadičem SSD1306. OLED displej je poměrně vděčnou a efektní periférií, takže návodů na jeho připojení a použití se dá na internetu najít mnoho. Obvykle se jedná o návody pro displej se sběrnicí I²C, který je v tomto případě jistě dobrou volbou. My se tedy pokusíme být aspoň trochu originální a zaměříme se na připojení OLED displeje přes sběrnici SPI. I když, jak uvidíme, tak si vlastně ukážeme i to, jak v MicroPythonu řídit displej se sběrnicí I²C.

Upřímně řečeno… motivace, proč se dnes vrhneme na OLED displej se sběrnicí SPI, není jen chuť být originální oproti ostatním článkům, ale je ještě jedna. Na trhu se objevuje „obojetný“ OLED displeje, který nabízí použití jak s I²C, tak se SPI sběrnicí. To pochopitelně zní lákavě! Tak se pojďme podívat, co si to pak ve svém oblíbeném elektro e-shopu vlastně pořídíme.

I²C nebo SPI?

Než se pustíme samotného OLED displeje nabízejícího použití sběrnic I²C a SPI, zkusíme obě sběrnice alespoň trochu představit a porovnat.

I²C (Inter-Integrated Circuit)

Sériová sběrnice navržená pro komunikaci mezi čipy na krátké vzdálenosti. Vytvořena firmou Philips (dnes NXP). Funguje na principu adresování zařízení, tedy každé zařízení má svou jedinečnou adresu. Všechna zařízení jsou připojena na stejné dvě linky, jedna je datová, druhá hodinový signál. Jak bylo naznačeno, může být více slave zařízení (např. senzory, displeje, EEPROM) na jedné sběrnici. Komunikace je řízena masterem (např. mikrokontrolér). Napěťová komunikace může být na úrovni dané napájecím napětím, v našem „bastlířském“ světě to znamená zpravidla 3,3 V nebo 5 V.

Používá jen dva vodiče:
  • SDA – datová linka
  • SCL – hodinová linka
Výhody:
  • Jednoduché zapojení (pouze 2 vodiče i pro více zařízení).
  • Šetří piny na mikrokontroléru.
  • Skvělé pro pomalejší přenosy (např. čtení teploty, posílání textu).
Nevýhody:
  • Nižší přenosová rychlost.
  • Náchylnější na rušení (citlivé na delší vodiče).
  • Při větším počtu zařízení může být komunikace pomalejší.

SPI (Serial Peripheral Interface)

Rychlá sériová sběrnice vyvinutá firmou Motorola. Používá se pro připojení zařízení, kde je důležitá rychlost přenosu dat. Přímá komunikace mezi řadičem (master) a jednotlivými periferními zařízeními (slave). Komunikace na sběrnici probíhá v režimu „full-duplex“, tedy data mohou proudit oběma směry zároveň – a to nezávisle na sobě – je zde linka jak pro směr tam (MOSI), tak zpět (MISO). Sběrnice zpravidla využívá 4 vodiče pro obousměrnou komunikaci nebo jen tři pro komunikaci jedním směrem. Součástí sběrnice je signál CS pro výběr dané periférie. Na jedné sběrnici může být připojeno několik periférií, periférie jsou „adresovány“ pomocí pinu CS, kterým je pro danou periférii určeno, zda aktuální komunikace je pro ni platná. Z hlediska elektrické podoby i zde se většinou setkáváme s normou 3,3 V nebo 5 V.

Používá obvykle 4 vodiče:
  • MOSI – Master Out Slave In (komunikace z řadiče)
  • MISO – Master In Slave Out (komunikace do řadiče)
  • SCK – hodinová linka
  • CS/SS – Chip Select/Slave Select (výběr periférie)
Výhody:
  • Vysoká přenosová rychlost (až desítky MHz)
  • Stabilnější a spolehlivější na kratší i střední vzdálenosti.
  • Ideální pro rychlá zařízení (displeje, paměti, ADC/DAC).
Nevýhody:
  • Vyžaduje více vodičů – pro každé zařízení zvlášť CS (chip select) signál.
  • Méně vhodné pro připojení většího množství zařízení.
  • Nemá žádné adresování – každé zařízení potřebuje vlastní výběrový signál CS.

Tabulka srovnání:

  I²C SPI
Počet vodičů 2 (SDA – data, SCL – hodiny) 4 (MISO, MOSI, SCLK, CS)
Rychlost Nižší (typ. do 400 kHz, high-speed až 3,4 MHz) Vyšší (typ. až 10+ MHz)
Topologie Více zařízení na jedné sběrnici (adresace) Obvykle 1:1, nebo více zapínané přes různé CS piny
Komunikace Half-duplex (data vždy jen v jednom směru najednou) Full-duplex (možnost současné obousměrné komunikace)
Složitost kódu Jednodušší, vestavěná podpora v knihovnách O něco složitější na řízení více zařízení
Potřeba pinů Méně (sdílí dvě linky pro všechna zařízení) Více (každé zařízení potřebuje vlastní CS pin)

OLED displej SPI/I²C

Současný trh s elektronickými součástkami je doslova zaplaven různými nepřebernými komponentami. Nejinak je tomu v oblasti OLED displejů. Co si budeme říkat, někteří prodejci dle nejnižší ceny berou své zboží od různých dodavatelů a někdy si ani nevšimnou, že již prodávají trochu něco jiného, než mají v popisu a na fotografiích svého e-shopu. Nákup v oblasti elektroniky tedy občas trochu připomíná loterii. Také by občas neškodilo, kdyby v daném e-shopu byl k dispozici návod nebo datový list. O nějaké ukázkové aplikaci ani nemluvě! Není tedy nic překvapivého, že až po nákupu zjistíme, že „přepnutí“ displeje z SPI na I²C není jen záležitostí nějakého přepnutí jumperu, ale vyžaduje to pájení SMD součástek. Koupíte-li OLED displej slibující možnost komunikace pod oběma systémy (I²C a SPI), vězte tedy, že se bez páječky a pinzety neobejdete! V případě běžně prodávaných těchto „obojetných“ displejů změna systému komunikace vyžaduje přepájení minimálně jednoho (nebo více) SMD rezistorů!

I když by vše mělo být bezproblematické, moc tuto cestu nedoporučujeme, a to zejména začínajícím elektro nadšencům. Na internetu lze na několika fórech najít příspěvky, kde se různí bastlíři potýkají s různými displeji. Někdy opravdu stačí jen přepájet jeden rezistor, jindy je těch rezistorů více, občas ještě sem tam nějaký ten zkratovací drátek. A pak ještě nastavit piny jako jsou CS a RST na správné úrovně, tu je to LOW, tamhle zase HIGH…

Někdy je to už opravdová divočina! Jestli nákup někdy připomíná loterii, tak toto je už spíše na úrovni dobrodružné skautské výpravy. 😊

Naše doporučení je tedy opravdu si dopředu zvolit typ sběrnice a podle toho pak displej koupit pro tu jednu určenou. Při nákupu těchto obojetných displejů je určitě také dobré ověřit si (jak asi?!), jaká sběrnice je vlastně tou výchozí nastavenou od výrobce. Jasno do toho ani nevnáší popis pinů displejů. Podívejme se na následující obrázky, které ukazují jednotlivé displeje (a nejsou to pochopitelně všechny!), na které můžeme v e-shopech s elektronikou narazit.

OLED-I2C

I²C displej (4 piny)

Tam, kde se nemůžeme zmýlit, je klasický I²C OLED displej se 4 piny. Kromě dvojice napájení VCC a GND jsou zde dva signálové vodiče SCL a SDA. Dokonce i popisky SCL a SDA odpovídají standardu dané sběrnice.

Takový displej lze připojit pouze sběrnicí I²C a není co řešit!

OLED-I2C SPI

SPI displej s popisem I²C (6 pinů)

Prvním „exotem“ z oblasti obojetných displejů je displej, který většinou primárně bývá zapojen pro sběrnici SPI, ale potiskem spíše odpovídá sběrnici I²C. Piny napájení jsou jasné, popisy pinů sběrnice SPI ale spíše odpovídají popisům sběrnice I²C! Zapojení SPI tedy bude následovné:

  • SCL – Serial Clock Line (v I²C komunikaci je to hodinová linka), ale v SPI to odpovídá hodinové lince SCK
  • SDA – Serial Data Line (v I²C je to datová linka), ale v SPI to odpovídá MOSI (Master Out Slave In)
  • RES – resetovací pin (není součástí ani SPI, ani I²C datového přenosu), je třeba nastavit na správnou úroveň nebo použít knihovnu, která s tímto pinem umí pracovat
  • DC – Data/Command (v SPI jde o pin, který určuje, zda jsou data interpretována jako příkaz nebo jako data pro displej). Tento pin je specifický pro OLED displeje.

Datová linka MISO (Master In Slave Out) zde není potřeba, tak není použita

OLED-I2C SPI

SPI displej s D0, D1 a CS (7 pinů)

Tento displej svým popis neodpovídá ani SPI, ani I²C. Navíc nám u tohoto OLED displeje přibyl ještě jeden pin. (piny napájení v přehledu neuvádíme)

  • D0 – pin odpovídá SCK (Serial Clock) v SPI (při přepájení na I²C asi SCL)
  • D1 – pin odpovídá MOSI (Master Out Slave In) v SPI (u I²C asi SDA)
  • RES – resetovací pin (viz dříve)
  • DC – Data/Command pin (viz dříve)
  • CS – Chip Select (pin sběrnice SPI, který určuje, zda konkrétní zařízení na sběrnici má být aktivní)

Sedmipinový displej bychom měli použít (díky přítomnosti pinu CS), pokud potřebujeme k SPI sběrnici připojit několik SPI periférií zároveň.

Fajn, koupili jsme si displej, který je ve výchozím stavu určen pro SPI sběrnici. Přepajovat ho na I²C nechceme, tak co teď s ním? Přece ho nevyhodíme! Tak se pojďme podívat, jak tento displej připojit k modulu ESP32 s MicroPythonem.

Budeme tedy nadále používat některý ze SPI displejů se 6 nebo 7 piny.

Připojení SPI displeje SSD1306 k modulu ESP32

ESP32 disponuje čtyřmi hardwarovými SPI řadiči pro periferie, jsou to: SPI0, SPI1, SPI2 (označováno jako HSPI) a SPI3 (označováno jako VSPI). Sběrnice SPI0 a SPI1 se používají v modulu ESP32 interně, například ke komunikaci s vestavěnou pamětí flash, a neměli bychom je používat pro jiné úkoly. Zbývají nám tedy sběrnice HSPI (SPI2) a VSPI (SPI3).

Poznámka:
Aby situace kolem označování SPI nebyla (asi) příliš jednoduchá, tak MicroPython pro modul ESP32 označuje tyto sběrnice jinými čísly! Musíme si tedy zapamatovat, že SPI2 (alias HSPI) je v MicroPythonu dostupná pod označením SPI(1) a SPI3 (VSPI) najdeme pod označením SPI(2).

Pro komunikaci s perifériemi musíme tedy používat jen dostupné sběrnice SPI. Budeme je nadále pojmenovávat HSPI a VSPI a na nějaká čísla sběrnic si maximálně vzpomeneme až při zápisu kódu programu.

Většina desek ESP32 má pevně přiřazené výchozí piny hardwarových sběrnic SPI. Je tam sice určitá možnost volby, ale teď v tom nebudeme dělat zmatky! Pro začátek tedy budeme muset při připojení displeje dodržet jasně dané piny a jejich role (viz následující tabulka).

Mapování pinů SPI sběrnice pro většinu desek ESP32:

SPI MOSI MISO SCK CS
HSPI GPIO 13 GPIO 12 GPIO 14 GPIO 15
VSPI GPIO 23 GPIO 19 GPIO 18 GPIO 5

Trochu asi předbíháme, ale neodpustíme si informaci, že pokud bychom opravdu chtěli připojit SPI periférii k „libovolným“ pinům modulu ESP32, uvidíme dále, že hardwarové SPI není jedinou možností!

Pro připojení SPI displeje SSD1306 využijeme VSPI (v MicroPythonu označeném jako SPI(2)). Propojení pinů OLED displeje a modulu ESP32 znázorňuje následující obrázek. Na schématu máme použit 7 pinový displej, ale pokud použijeme 6 pinový displej, jen vynecháme propojení z pinu GPIO5. Pokud nás zarazilo, že jsme dle tabulky výše nepoužili GPIO19, který na sběrnici SPI umožňuje komunikaci od periférie do modulu ESP32, jistě nás napadne, že signál MISO na displeji asi není potřeba (zde displej nemá co hlásit).

schema: OLED-ESP32 SPI2

Zatímco propojení SPI linky a pinu SCK (GPIO18), MOSI (GPIO23) a CS (GPIO5) je zde nutné dodržet, tak linky (piny) RES (GPIO16) a DC (GPIO17) si můžeme zvolit. Aktuálně zvolená konfigurace sběrnice VSPI a propojení modulu ESP32 s displejem je shrnuto v následující tabulce.

Modul ESP32 – VSPI OLED SSD1306 – SPI
GND GND
3V3 Vcc
GPIO18 SCK (SCL)
GPIO23 MOSI (SDA)
GPIO16 RES
GPIO17 DC
GPIO5 CS
GPIO19 (nepoužito) ---

Řízení displeje SSD1306 přes sběrnici SPI MicroPythonem na modulu ESP32

Řízení OLED displeje s řadičem SSD1306 už asi není tak jednoduché, abychom jej řešili přímým přístupem na jednotlivé piny, využijeme tedy obslužnou knihovnu. Knihovna se jmenuje ssd1306 a můžeme ji najít na internetu.

Zde ale nastává první problém!

Na internetu lze nalézt hned několik knihoven stejného jména. Některé jsou určitými klony základní knihovny, jiné jsou samostatnými výtvory svých autorů. Některé jsou obecně pro micropython, jiné pro konkrétní modul např. ESP32, Raspberry… Všechny slibují totéž – ovládat OLED displej SSD1306. Ale ne se všemi nám program bude na modulu ESP32 fungovat. A to dokonce i při použití ukázkových pythonovských kódů uvedených u dané verze knihovny! To je velmi zvláštní… 😕

Uvedeme si zde tedy postup, jak získat knihovnu, se kterou nám ovládání OLED displeje funguje. I když, jak uvidíme, ta funkčnost bez zádrhele nebude!

Instalace knihovny ssd1306

Naštěstí prostředí Thonny disponuje správcem „balíků“ (což jsme už poznali v některém z předešlých článků), pomocí kterého stáhneme knihovnu určenou přímo pro náš modul. Ostatní knihovny, které lze na internetu najít a které občas slibují i větší paletu příkazů, zatím necháme pro odvážnější bastlíře.

Načtení knihovny ssd1306 v prostředí Thonny

I když jsme si zde postup instalace knihovnu pomocí prostředí Thonny ukazovali, myslím, že nyní připomenutí postupu nebude na škodu.

  1. Připojíme k USB portu počítače s nainstalovaným prostředím Thonny modul ESP32 s firmwarem MicroPythonu, do kterého budeme chtít nainstalovat kód modulu ssd1306.
  2. V prostředí Thonny v menu Nástroje vybereme volbu Spravovat balíky.
  3. Do vyhledávacího pole okna správce „balíků“ zadáme klíčové slovo ssd1306 a vybereme nabízený balík, který je modulem pro ovládání OLED displeje.
  4. Instalaci spustíme kliknutím na tlačítko Instalace. Do připojeného modulu ESP32 se začne z internetu stahovat kód modulu (knihovny) ssd1306 – viz následující obrázek.
stazeni knihovny v prostredi Thonny

Jakmile máme modul ssd1306 stažený do modulu ESP32, můžeme se pustit do programování. Jen si připomeňme, že se modul ssd1306 stáhl do připojeného zařízení (modulu ESP32), nikoliv do prostředí Thonny. Připojíme-li jiný modulu ESP32, musíme do něj knihovnu opět nainstalovat (postup opakovat).

Knihovna ssd1306

Knihovna ssd1306 slouží pro ovládání OLED displejů s řadičem ssd1036 a je vytvořena tak, aby fungovala pro displeje připojené jak sběrnicí SPI, tak I²C. Její použití je tedy univerzální! Pokud budeme připojovat OLED displej se sběrnicí I²C, zvolíme úplně stejný postup i knihovnu. Dále si tedy zkusíme ukázat, jak knihovnu nakonfigurovat pro sběrnici SPI i I²C. I když pak budeme pokračovat jen na sběrnici SPI!

Hardwarové SPI v modulu ESP32:

Jak jsme uvedli, modul ESP32 má v MicroPythonu dostupné dvě sběrnice SPI (HSPI a VSPI). Pro ovládání SPI periférií musíme využít piny, které jsou k jednotlivým SPI řadičům připojené (viz dřívější tabulka)! Pro ovládání hardwarového SPI se používá objekt SPI, který je součástí modulu machine.

Následující ukázka kódu ilustruje, jak lze nastavit komunikaci s OLED displejem SSD1306 pomocí VSPI.

from machine import Pin, SPI
import ssd1306

# hspi = SPI(1) # sck=14 (scl), mosi=13 (sda), miso=12 (unused)
vspi = SPI(2) # sck=18 (scl), mosi=23 (sda), miso=19 (unused)

dc = Pin(17) # data/command
rst = Pin(16) # reset
cs = Pin(5) # chip select, some modules do not have a pin for this

# display = ssd1306.SSD1306_SPI(128, 64, hspi, dc, rst, cs)
display = ssd1306.SSD1306_SPI(128, 64, vspi, dc, rst, cs)

Pro nás je zde především důležitá deklarace objektu display, který nám umožní ovládat připojený OLED displej. Metodou SSD1306_SPI určujeme komunikační sběrnici SPI a nastavujeme zde následující parametry (po řadě): rozlišení v x-ovém směru (zde 128), rozlišení v y-ovém směru (64), objekt komunikační sběrnice (vspi), pin data/command (cd), pin reset (rst) a pin chip select (cs). Piny jako je SCK, MOSI, MISO jsou deklarovány v rámci defaultního nastavení zvoleného řadiče SPI(2). Ostatní piny jsou zadány pomocí proměnných dc, rst a cs.

V ukázce je zakomentována i možnost zvolení HSPI, tj. SPI(1). V tom případě je třeba v metodě SSD1306_SPI zvolit za typ komunikační sběrnice proměnnou hspi a odkomentovat potřebnou deklaraci hspi.

Softwarové SPI v modulu ESP32:

Když jsme se zmiňovali, že modul ESP32 umožňuje nastavit sběrnici SPI na „jakémkoliv“ pinu, tak jsme měli na mysli možnost tzv. softwarového SPI. V tom případě není využíván hardwarový řadič, ale je ovládání SPI sběrnice simulováno softwarově vnitřním programem. Toto řešení je takovým plánem B, pokud bychom již obsadili obě SPI sběrnice, nebo je nemohli využít z jiného důvodu. Pochopitelně softwarové řešení SPI komunikace má určité omezení, kupříkladu více zatěžuje procesor. To nás ale při našem jednoduchém projektu moc tížit nebude. Výhodou tohoto řešení je, že ho lze realizovat téměř na jakékoliv sadě pinů (pokud to pochopitelně samotný charakter pinu dovolí).

Pro používání softwarového SPI využijeme objekt SoftSPI, který je také součástí modulu machine. Na rozdíl od hardwarového SPI, kde jsou defaultně nastaveny výchozí piny a i určitý standard komunikace, zde musíme alespoň některá tyto parametry nastavit ručně. Jedná o následující parametry (některé naštěstí mají výchozí hodnoty, tak dále v kódu nejsou nastavovány):

  • baudrate – hodinová frekvence na pinu SCK.
  • polarity – může být 0, nebo 1 a je to úroveň, na které se nachází nečinná hodinová linka.
  • phase – může být 0, nebo 1, aby se data vzorkovala na první, resp. druhé hraně hodin.
  • bits – je šířka každého přenosu v bitech.
  • firstbit – určuje pořadí zasílaných bitů z odesílaného bytu (může být SPI.MSB, nebo SPI.LSB)
  • sck, mosi, miso jsou objekty pinů, které se použijí pro jednotlivé signály sběrnice.

from machine import Pin, SoftSPI
import ssd1306

spi = SoftSPI(baudrate=500000, polarity=1, phase=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19))

dc = Pin(17) # data/command
rst = Pin(16) # reset
cs = Pin(5) # chip select, some modules do not have a pin for this

display = ssd1306.SSD1306_SPI(128, 64, spi, dc, rst, cs)

Jakmile máme vytvořený objekt spi, který odpovídá objektu sběrnice SPI, je další použití úplně stejné, jako v případě hardwarového SPI. Objekt SPI sběrnice dosadíme jako určený parametr do metody SSD1306_SPI. Z hlediska programu je pak již jedno, zda se jedná o SPI hardwarové nebo softwarové, tyto záležitosti jsou plně v režii modulu ESP32.

I²C interface:

Když už jsme slíbili i ukázku pro použití I²C displeje, připojujeme tedy následující ukázku kódu pro sběrnici I²C:

from machine import Pin I2C
import ssd1306

# using default address 0x3C
i2c = I2C(sda=Pin(4), scl=Pin(5))

display = ssd1306.SSD1306_I2C(128, 64, i2c)

Vidíme, že základním komunikačním objektem pro I²C sběrnici bude objekt I2C, který importujeme z modulu machine. Knihovně ssd1306 pak potřebné informace předáme pomocí metody SSD1306_I2C, jejíž parametry jsou opět rozměry (rozlišení) použitého OLED zobrazovače a pochopitelně objekt komunikační sběrnice. V konfiguraci objektu pro I2C sběrnici vidíme, že byly zvoleny komunikační piny GPIO4 a GPIO5, kde zvolené parametry určují roli (SDA/SCL) těchto pinů. Tím máme vyřízenou komunikaci s displejem. Další práce s displejem je na úrovni knihovny ssd1306 a ta je na použiré sběrnici nezávislá. Výše uvedené nastavení je tedy vše, co by nám mělo pro komunikaci I²C stačit, vše ostatní bude stejné jako při kominikaci přes SPI.

Práce s OLED displejem

Jakmile má MicroPython definovaný objekt display (ať již na jakékoliv sběrnici), otevírá se nám paleta metod, které nám umožní zapisovat do vyrovnávací paměti displeje, a pak tyto data jednorázově zobrazit. To je třeba si uvědomit! Samotné provedení nějakého příkazu neovládá rovnou zobrazovací OLED jednotku, ale vytváří určitý datový obraz ve vyrovnávací paměti. Až metoda show() způsobí zobrazení!

Než se pustíme do ukázkového programu, podíváme se na základní metody modulu ssd1306:

  • show() – prokopíruje zadaná data zobrazení z vyrovnávací paměti na displej (je třeba volat vždy, pokud chceme, aby se nějaký příkaz projevil na OLED displeji)
  • invert(False/True) – zapíná/vypíná displeji inverzní režim (co bylo tmavé, svítí a naopak). Důležité je vědět, dvě věci:
    1. Co bylo na displeji namalováno, je okamžitě přemalováno inverzně (není třeba to tedy malovat znova nebo volat metodu show)
    2. Parametr určující barvu pixelů při inverzním režimu funguje obdobně jako při režimu normálním, tedy co má být vidět bude vidět (bude zhaslé na svítícím pozadí) a co má mít barvu pozadí, nebude vidět (svítí na svíticím pozadí)
  • rotate(False/True) – svisle převrací zobrazení na displeji. Používá se, pokud díky umístění displeje potřebujeme zobrazovat vzhůru nohama (obraz je převrácen tak, aby byl čitelný, ne jen vertikálně)
  • contrast(0–255) – nastavuje úroveň svitu OLED pixelů, podle dokumentace vstupní parametr umožňuje rozmezí hodnot 0–256, ale zkušenost ukazuje, že reálně použitelné hodnoty jsou asi jen 0 (málo), 64 (trochu) a 255 (maximum).
  • poweron() – zapnutí zobrazování displeje (po zapnutí napájení displeje není třeba tuto metodu používat pro nějaké „zapnutí“ zobrazování), spíše slouží jako rozsvícení po dočasném zhasnutí displeje.
  • poweroff() – vypnutí zobrazování displeje (dočasné zhasnutí displeje)
  • text(text, X, Y, col) – vypíše obsah řetězece text na souřadnice X (šířka) a Y (výška) barvou col – u monochromatického displeje barva 0 znamená barvu pozadí a 1 viditelnou barvy (i při inverzním režimu)
  • pixel(X, Y, col) – vykreslí se bod na souřadnicích X, Y barvou col
  • line(X1, Y1, X2, Y2, col) – zobrazí se úsečka mezi souřadnicemi X1, Y1 a X2, Y2 barvou col
  • fill(col) – vyplní displej zvolenou barvou col (1 znamená viditelnost, 0 barvu pozadí)
  • hline(X, Y, del, col) – vodorovná čára ze souřadnic X, Y o délce del (počet pixelů) a barvě col
  • vline(X, Y, del, col) – svislá čára ze souřadnic X, Y o délce del (počet pixelů) a barvě col
  • rect(X, Y, dX, dY, col) – vykreslí hranice obdélníku s levým horním rohem X, Y, délkami stran dX, dY a barvou col
  • fill_rect(X1, Y1, dX, dY, col) – zobrazí výplň obdélníku (parametry jako u rect) barvou col
  • poly(X,Y, pole, col) – vykreslí lomenou čáru dle souřadnic v poli pole na místo s levým horním rohem na souřadnicích X, Y barvou col – souřadnice lomené čáry jsou absolutní vzhledem k levému hornímu rohu, tzn. žádné záporné hodnoty (relativně vztažené k počátku!)
  • scroll(SX, SY) – posune obraz celého displeje ve směru SX, SY (kladné hodnoty znamenají vpravo/dolů, záporné vlevo/nahoru). Na původním místě odkud bylo něco odsunuto, ale nic tam nebylo přisunuto, zůstane část původního motivu.

Jak bylo naznačeno, lze na internetu najít i knihovny, které zvládají další příkazy, například příkaz pro elipsu a podobně. Ale osobní zkušenost s funkčností takových knihoven není nic moc.

Následující kód programu představuje ukázkový program, který by měl ukázat některé z výše uvedených metod. Předem upozorňujeme, že i když je zde propojení OLED displeje a modulu ESP32 takové, že odpovídá použití hardwarového VSPI, nakonec komunikaci řídíme pomocí softwarového SoftSPI. (důvody viz dále)

Kód programu:

from machine import Pin, SoftSPI
import ssd1306
import array
import time

# SPI(1) - sck=14 (scl), mosi=13 (sda), miso=12 (unused)
# SPI(2) - sck=18 (scl), mosi=23 (sda), miso=19 (unused)
spi = SoftSPI(baudrate=500000,
          polarity=1,
          phase=0,
          sck=Pin(18), mosi=Pin(23), miso=Pin(19)   # piny pro SPI(2)
         )

dc = Pin(17)     # data/command
rst = Pin(16)    # reset
cs = Pin(5)      # chip select

display = ssd1306.SSD1306_SPI(128, 64, spi, dc, rst, cs)
blik = Pin(2, Pin.OUT)

display.poweron()
display.contrast(255)
display.rotate(False)
display.invert(0)

display.fill(0)
display.text('FyzKAB 4ever!', 15, 1, 1)
display.text('FK', 57, 35, 1)
display.line(0, 10, 127, 10, 1)
display.rect(0, 0, 128, 64, 1)

display.show()

def efekt(barva):
     uhelnik = array.array('I', [0, 0, 80, 0, 80, 50, 0, 50, 0, 0])
     krokX = 8
     krokY = 5
     display.invert(not(barva))
     for i in range(0, 10):
         # zmena hodnot na indexech 0, 3, 8 o kroky
         display.poly(24,13, uhelnik, barva)

         display.show()
         uhelnik[0] += krokX
         uhelnik[3] += krokY
         uhelnik[4] -= krokX
         uhelnik[7] -= krokY
         uhelnik[8] += krokX

         time.sleep(0.2)

while True:
         blik.off()
         efekt(1)
         time.sleep(1)

         blik.on()
         efekt(0)
         time.sleep(1)

Kód programu je již trochu delší, stejně tak i seznam importovaných modulů (knihoven). Ale kromě použití polí, které si dále vysvětlíme, zde není nic, co bychom již v našem dosavadním „článkovém maratonu“ nepotkali.

První část, kde nastavujeme komunikační sběrnici, knihovnu ssd1306 a ostatní komunikačními piny jsme si již ukázali. V kódu se tedy můžeme přesunout až k deklaraci proměnné blik. Tato proměnná je objektem pro ovládání pinu GPIO2, na kterém je na našem vývojovém kitu modulu ESP32 připojena vestavěná LED. Tu budeme čistě jen pro efekt rozsvěcet při přepnutí displeje do inverzního režimu. To nám může posloužit jako takový ukazatel běhu programu, pokud by se na OLED displeji nic nezobrazovalo.

Následují úvodní nastavení displeje, jako je zapnutí poweron() (není třeba), nastavení kontrastu na maximum contrast(255), volba orientace rotate(False) a nastavení stavu inverze invert(0) (zde si všimněte, že místo deklarované hodnoty False/True používéme možnost použití hodnoty 0 a 1). Většina těchto nastavení je na začátku programu zbytečná a je zde v kódu uvedena jen proto, aby si s nimi mohli případní zájemci hrát.

display.poweron()
display.contrast(255)
display.rotate(False)
display.invert(0)

Následuje smazání displeje fill(0) a výpis textů, čáry a orámování obdélníkem:

display.fill(0)
display.text('FyzKAB 4ever!', 15, 1, 1)
display.text('FK', 57, 35, 1)
display.line(0, 10, 127, 10, 1)
display.rect(0, 0, 128, 64, 1)

Na displej se však zatím nic nezobrazí, to se provede až po provedení příkazu show():

display.show()

Následuje definice funkce efekt(), která slouží pro vykreslení efektu, který si pamětníci pamatují z televizního pořadu Vega. Jde o postupné vykreslování vepsaného čtyřúhelníku do obdélníku. Čtyřúhelník vykreslujeme pomocí metody poly(), abychom viděli, jak se vykresluje lomená čára.

Ve funkci efekt() je i nastavení inverzního zobrazení a barvy vykreslování tak, aby při první průběhu se obrazec vykreslil a při druhém smazal. S těmito různými parametry je funkce volána v hlavní nekonečné smyčce.

Práce s polem v MicroPythonu

Z hlediska našeho studia (Micro)Pythonu je nyní spíše zajímavější použití pole, než samotný grafický efekt na OLED displeji. Pojďme se tedy na chvíli podívat, jak se s polem v (Micro)Pythonu pracuje.

V (Micro)Pythonu se můžeme setkat s několika způsoby práce s poli (nebo poli podobnými strukturami). V některých případech je třeba importovat modul array, zatímco jindy můžeme používat standardní seznamy (lists), jako v jiných programovacích jazycích.

Seznamy jsou v Pythonu velmi flexibilní a uživatelsky přívětivé. Můžeme do nich ukládat různé typy dat (čísla, řetězce, objekty apod.), a je to nejběžnější způsob práce s „poli“ v běžném Pythonu.

Např.:
    seznam = [1, 2, 3, 4]
    seznam.append(5)

    print(seznam)  # Výstup: [1, 2, 3, 4, 5]

V MicroPythonu je situace trochu jiná, protože to je odlehčená verze Pythonu určená pro malé zařízení a mikrokontroléry (např. ESP32). V některých případech je tedy v MicroPythonu nutné používat modul array, aby se efektivněji spravovala paměť, protože běh na těchto zařízeních bývá omezený. Pokud tedy pracujeme s polem v MicroPythonu a potřebujeme optimalizovat využití paměti nebo rychlost, pomůže nám modul array. Na druhou stranu, pokud nepotřebujeme takovou optimalizaci a jednoduše potřebujeme seznamy, můžeme použít standardní seznamy.

Modul array se používá, jak jsme řekli, pro efektivní práci s polem, které obsahuje hodnoty stejného typu. Je to typické pro aplikace, kde potřebujeme vysoký výkon nebo chceme mít pole, které bude mít nižší paměťovou náročnost než seznamy.

Např.:
    import array

    # Vytvoření pole celých čísel (typ 'i' znamená integer)
    pole = array.array('i', [1, 2, 3, 4])
    pole.append(5)

    print(pole)  # Výstup: array('i', [1, 2, 3, 4, 5])

Pole v modulu array jsou v Pythonu tzv. typová, což znamená, že když deklarujeme pole, určíme jeho typ (například pouze celé číslo, pouze desetinné číslo apod.).

Deklarace takového pole pak vypadá následovně, například v našem kódu:

uhelnik = array.array('I', [0, 0, 80, 0, 80, 50, 0, 50, 0, 0])

Metoda array objektu array má v tomto případě dva argumenty. První parametr je typ pole (zadáno klíčovým znakem, zde 'I'), druhým parametrem je uspořádaná n-tice jednotlivých prvků pole (zde postupně x-ové a y-ové souřadnice lomené čáry).

Klíčový znak, který udává typ pole, může být následující (viz tabulka):

znak typ počet bytů Poznámka
b signed char 1 -127 až 127
B unsigned char 1 0 až 255
h signed short 2 -32 768 až 32 768
H unsigned short 2 0 až 65 535
i
l
signed int
signed long
4 -2 147 483 648 až 2 147 483 647
I
L
unsigned int
unsigned long
4 0 až 4 294 967 295
q signed long long 8 -9 223 372 036 854 775 807 až 9 223 372 036 854 775 807
Q unsigned long long 8 0 až 18 446 744 073 709 551 615
f float 4 reálné číslo (Pokud je podporováno)
d double 8
u Unicode character 2 speciálně pro ukládání znaků
Ještě k tomu připomeňme:
Pole nemusíme zadávat jen výčtem konkrétních prvků, ale můžeme také pole inicializovat s počátečními hodnotami, které jsou výchozí.

Např. pole o 10 prvcích s výchozí hodnotou 0:

pole = array.array('B', [0] * 10)

Pole lze zadat i jako prázdné a pak prvky přidávat:

pole = array.array('B')
for _ in range(10):
    pole.append(0)  # nebo nějaké jiné výchozí hodnoty

To je k rychlému seznámení s polem a modulem array zatím asi vše.

Vraťme se k našemu OLED displeji!

Spustíme kód a měli bychom na displeji získat jednoduchý grafický efekt. Zvídavý čtenář si určitě zkusí zaexperimentovat se změnou různých parametrů a efektů.

SPI OLED displej-modul ESP32

Problém s hardwarovým SPI

Jestliže vše funguje, můžeme se konečně podívat na problém, který jsme zde již dříve tak trochu avizovali. Zároveň tím vysvětlíme, proč výše uvedený program využívá SoftSPI, nikoliv hardwarové SPI.

Problém je, i když je jak zapojení pinů, tak i výchozí nastavení připravené pro hardwarové SPI (konkrétně VSPI), tak prostě program s hardwarovým SPI nefunguje! Respektive, program žádnou chybu nehlásí a běží. Běh nám dokazuje interní LED na pinu GPIO2, která se pravidelně rozsvěcí, což signalizuje běh hlavní smyčky. Ale displej je celou dobu stále jen zhasnutý. 😒

Podle kontroly parametrů hardwarového řadiče SPI by měly být nastaveny stejné parametry jako jsou při použití SoftSPI, takže by vše mělo fungovat stejně. Ale prostě ne! Dokonce pokud tyto hodnoty (pro jistotu) nastavíme znova pomocí metody SPI.ini(), displej pod taktovkou hardwarového SPI stále nefunguje. Nepomohlo ani přepojení displeje na druhou hardwarovou sběrnici SPI (HSPI), ani různé experimenty s parametry komunikace.

V tuto chvíli opravdu netušíme, kde je problém. Je pravda, že použitý modul ESP32 WROOM-32 je již takovým veteránem různých našich pokusů a experimentů… Ale že by zrovna tento kus měl jen poškozený řadič hardwarového SPI, se zdá být jako dosti nepravděpodobné.

Pochopitelně nás situace nenechává klidnými a budeme dále hledat problém. Pokud by někdo věděl, kde a co děláme chybně, dejte nám, prosím, vědět. Za každou užitečnou radu budeme vděční!

Dnešní článek tedy skončil poněkud netradičně, ale i takové věci se ve světě bastlířů dějí! Je to svět pokusů, omylů a žádných pořádných manuálů… To je také důvod, proč tyto články píšeme, aby tam, kde můžeme poradit, jsme někomu pomohli.

A nyní se zkrátka karta obrátila! 😉

Závěr

V dnešním článku i přes jeho určitý rozpačitý závěr jsme si ukázali, jak lze ovládat OLED displej s řadičem SSD1306 pomocí sběrnice SPI – i když (zatím?) jen přes softwarově emulované SoftSPI. Seznámili jsme se s knihovnou ssd1306, která je ale použitelná i pro I²C displej, takže se nemusíme omezovat jen na zde avizované SPI displeje. Ovládnutím jakéhokoliv displeje rázem naše budoucí projekty získávají na atraktivitě a možnosti poměrně hezkého výstupu a smysluplné interakce s uživatelem.

A na co se podíváme příště?

Zatím jsme stále načítali nějaká čidla, dnes zobrazovali texty a obrázky, ale stále se nám na stole nic nepohnulo! Tak proč se nyní nezkusit vydat třeba tímto směrem?

UPOZORNĚNÍ:
Nesouhlasíme s vyřazením Newtonových zákonů, Ohmova zákona a zákona zachování energie z učiva fyziky základních škol v České republice!