Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
Pouze tento týden sleva až 80 % na e-learning týkající se Java. Zároveň využij akce až 80 % zdarma při nákupu e-learningu. Více informací:

Lekce 19 - Arduino - Využití I2C sběrnice

V minulé lekci, { PREVIOUS}, jsme se naučili pracovat s SD kartou.

V tomto tutoriálu Arduina se zaměříme na popis a využití I²C sběrnice. Vysvětlíme si, jak tato sběrnice funguje. Dozvíme se také, jak s její pomocí dokážeme rozšířit schopnosti našeho Arduina.

I²C sběrnice v Arduinu

I²C neboli Two Wire Interface je sériová sběrnice od společnosti Philips, která využívá ke komunikaci dva vodiče – SDA (Synchronous Data) a SCL (Synchronous Clock). SDA vodič slouží k přenosu dat a na SCL vodič je přiveden hodinový signál. Obě linky je možno používat jako obousměrné. Výhodou je stálý hodinový signál, který vysílá pouze jedno zařízení. Nebude nám proto vadit jeho posun s časem od zapnutí. Další výhodou je možnost zapojení několika zařízení najednou. V tom se I²C zásadně liší od sběrnice UART, která umožňuje propojit pouze dvě zařízení (RX – recieve, TX – transmit). Výběr zařízení ke komunikaci se na I²C provádí vysláním signálu s adresou zařízení, s nímž chceme komunikovat. Nevýhodou je ztráta dat na vyšší vzdálenosti. I²C se proto obvykle využívá např. na deskách plošných spojů ke komunikaci mezi procesorem a např. AD/DA převodníky.

A v čem je kouzlo této sběrnice? Pomocí čtyř vodičů na ní můžeme připojit až 128 zařízení. Naše Arduino bude pracovat jako tzv. master. To je zařízení, které je na sběrnici pouze jedno. Ovládá veškerou komunikaci a generuje hodinový signál na pinu SCL. Zbylých 127 zařízení jsou tzv. slave. To jsou zařízení, které komunikují s masterem a čekají na jeho pokyny.

Co tedy můžeme na sběrnici připojit? Skoro všechno! Například LCD display, RTC modul (ten na čas), IO expander (přidá další digitální piny), jiné Arduino a spoustu dalšího.

Jen pozor - piny SCA s SDL jsou u Arduin UNO a NANO připojeny na analogové piny A5 a A4, tudíž je nemůžeme používat současně se sběrnicí.

Teorie také tvrdí, že na SDA a SCL bychom měli odporem připojit VCC. Ze zkušenosti vám můžeme říci, že na krátké vzdálenosti (zapojování pár zařízení v breadbordu atd.) to vůbec není potřebné. Případný rezistor budeme potřebovat pouze pokud nám na delších vzdálenostech něco nebude správně fungovat. Sběrnice je tedy určena pro krátké vzdálenosti, ale s odpory je možné na UTP kabelu dosáhnout i délky 10 metrů. Na druhém konci pak bude bez problému fungovat LCD i s expandery.

I²C adresa

Každé zařízení má svoji adresu. Dost často na modulu nalezneme tři piny označované jako A0, A1 a A2 (hardwarové adresovací linky). Přivedením napětí na jednotlivé piny dokážeme měnit adresu zařízení. Například tabulka adres pro IO expandery PCF8574 použité v tomto tutoriálu bude vypadat takto:

A0 A1 A2 Dec Hex
L L L 32 20
H L L 33 21
L H L 34 22
H H L 35 23
L L H 36 24
H L H 37 25
L H H 38 26
H H H 39 27

L - Low, pin bez napětí; H - High, 5 V na pinu

Adresu lze zapsat decimálně (32) nebo hexadecimálně (0x20). Rozdíl v tom podstatě není žádný, používají se oba typy zápisů. Při koupi součástek se dost často stává, že zařízení má jinou adresu, než uvádí prodejce. Adresu si tedy zjistíme pomocí I²C scanneru ze stránek Arduina. Sketch nahrajeme do desky, otevřeme Serial Monitor a Arduino vypíše všechna zařízení na sběrnici.

LCD displej

Když si koupíme LCD displej s I2C sběrnicí, bude k němu připájena destička se čtyřmi vývody - VCC, GND, SDA a SCL. Zapojení těchto vodičů je jednoduché - VCC připojíme na 5 V, GND na zem, SDA na SDA a SCL na SCL. Ještě budeme potřebovat knihovnu. Arduino IDE už v sobě má manažera knihoven, tudíž rozklikneme Sketch -> Include Library -> Manage Libraries, vyhledáme knihovnu LiquidCrystal_I2C a nainstalujeme ji. Klasický LCD displej se od displeje se sběrnicí I2C liší pouze zapojením a použitím jiné knihovny.

Program na obsluhu displeje

Pro jednoduché zobrazení hlášky Hello world! použijeme tento kód:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);

void setup()
{
  lcd.init();
  lcd.backlight();
  lcd.setCursor(3,0);
  lcd.print("Hello, world!");
  lcd.setCursor(2,1);
  lcd.print("ITnetwork.cz!");
}


void loop()
{
}

Zajímavý je řádek LiquidCrystal_I2C lcd(0x27, 20, 4). Zde vytvoříme objekt LCD displeje. V parametrech není použit seznam RS, E a DX pinů, nýbrž pouze I²C adresa, počet znaků na řádek a počet řádků. Tudíž displej je připojený na adrese 0x27, má čtyři řádky a 20 znaků. Poté náš LCD displej inicializujeme pomocí lcd.init(). Jelikož i piny pro podsvícení jsou připojené na I2C čip, musíme pomocí lcd.backlight() zapnout také podsvícení. Funkce print() a setCursor() jsou nám již dobře známé z lekce o LCD displeji.

IO expander

Řekněme, že se dostaneme do situace, kdy potřebujeme připojit další zařízení a nemáme už volné piny. V takové chvíli můžeme sáhnout po tzv. IO expanderu, například po tomto PCF8574. Na sběrnici jich můžeme připojit až osm, což je 8*8=64 pinů. Na expanderu tedy máme osm digitálních pinů. Pro následující vysvětlení je třeba rozumět principu dvojkové soustavy.

IO expander jako výstup

Pokud expander chceme použít jako výstupní piny, zapíšeme na něj hodnotu v rozmezí 0-255. Pokud jako výstupní, budeme hodnotu číst. Nyní ovšem nastává otázka jak požadované signály na pinech převést na hodnoty a zase zpátky?

Osm pinů na expanderu si označíme čísly 0-7, jak jsou popsány na čipu. Tyto čísla pro nás budou mocniny dvojky. Tudíž první pin (0) dostane číslo 1, druhý (1) dostane 2, třetí (2) dostane 4 a tak dále. Ve výsledku získáme tabulku:

Pin 1 2 3 4 5 6 7 8
Číslo pinu 0 1 2 3 4 5 6 7
Mocnina dvojky 1 2 4 8 16 32 64 128

Dejme tomu, že budeme chtít mít na pinech číslo 0 a 5 napětí (logická 1) a na všech ostatních ne (logická 0). Naši tabulku tedy doplníme:

Pin 1 2 3 4 5 6 7 8
Číslo pinu 0 1 2 3 4 5 6 7
Mocnina dvojky 1 2 4 8 16 32 64 128
Výstup 1 0 0 0 0 1 0 0

Tím nám v posledním řádku vznikne číslo v dvojkové soustavě - 10000100. To už jenom převedeme do desítkové soustavy a máme číslo, které můžeme odeslat na expander - tedy 132. Pro zápis na expander musíme mít naimportovanou knihovnu Wire.h. V setupu z ní zavoláme funkci Wire.begin(). Poté můžeme na expander posílat data:

Wire.beginTransmission(33);
Wire.write(132);
Wire.endTransmission();

V prvním řádku otevřeme spojení k příslušnému expanderu na adrese 33 (také lze zapsat 0x21). V druhém řádku zapíšeme naši hodnotu 132. Ve třetím řádku se pak provede samotné odeslání na expander. Funkce endTransmission() také vrací číslo podle úspěšnosti. Pokud se data zapsat povedlo, vrátí nám 0, pokud zařízení neexistuje, vrátí 4.

Na expanderu nemusí být na výstupech čistých 5 V, napětí může být nižší.

IO expander jako vstup

Pokud budeme potřebovat použít piny jako vstupní, je princip fungování expanderu úplně stejný (jen obrácený). Číslo přečteme v desítkové soustavě. Pak jej musíme převést zpět na jedničky a nuly (úrovně napětí) pro jednotlivé piny. Jak tedy na to? Nejprve si pomocí funkce Wire.requestFrom() vyžádáme zařízení na sběrnici. Pokud je dostupné, přečteme z něj hodnotu pomocí Wire.read() a uložíme ji do proměnné. Arduino má funkci bitRead(), která vstupní číslo v desítkové soustavě převede na dvojkový zápis. Jako druhý parametr funkce vezme pozici čísla (tj. mocninu dvojky) a vrátí, zda je na ní 1 nebo 0. Funkci zavoláme postupně se všemi čísly od 0 do 7, vrácenou hodnotu převedeme na string a přidáme do proměnné. Tím získáme původní řetězec, se kterým jsme pracovali na začátku při zapisování:

Wire.requestFrom(33, 1);
if (Wire.available())
{
    byte readExpander = 255 - Wire.read();
    String readed;
    for (byte i = 0; i < 8; i++)
    {
        readed += String(bitRead(readExpander, i));
    }
    if (String(readed[0]) == "1")
    {
        pin1();
    }
    else if(String(readed[1]) == "1")
    {
        pin2();
    }
    else if(String(readed[2]) == "1")
    {
        pin3();
    }
    else if(String(readed[3]) == "1")
    {
        pin4();
    }
    else if(String(readed[4]) == "1")
    {
        pin5();
    }
    else if(String(readed[5]) == "1")
    {
        pin6();
    }
    else if(String(readed[6]) == "1")
    {
        pin7();
    }
    else if(String(readed[7]) == "1")
    {
        pin8();
    }
}

Samozřejmě můžeme vynechat ukládání do proměnné readed i smyčku a v podmínkách používat přímo if (bitRead(readExpander, 0) == 1) { pin1(); }


 

Předchozí článek
Arduino - Práce s SD kartou
Všechny články v sekci
Arduino - Hardware
Přeskočit článek
(nedoporučujeme)
Zapisovátko Morseovky s Arduinem
Článek pro vás napsal Adam Ježek
Avatar
Uživatelské hodnocení:
9 hlasů
Autor se převážně věnuje Arduinu a psaní tutoriálů z této oblasti, občas napíše příležitostně nějakou tu zprávičku. Většinu svého volného času momentálně věnuje Linuxu a/nebo Raspberry Pi. Také umí C#, HTML, CSS, PHP a Python.
Aktivity