15. díl - Arduino a I2C sběrnice

Hardware PC Arduino Arduino a I2C sběrnice

I²C je sériová sběrnice, původně od firmy Philips, používaná např. na základních deskách k připojení periferií, které nevyžadují vysokou rychlost přenosu dat. Občas se můžete setkat také s označením TWI, což je prakticky to samé, jen TWI používá Atmel(výrobce např. mikroprocesorů ATmega, pořivaných v Arduinu) a další společnosti, protože I²C je chráněná značka.

A v čem je kouzlo této sběrnice? Určitě jste na desce zahlédli piny označené SCL a SDA. Jejich zkratky znamenají Synchrous Clock a Synchronous Data. Takto můžeme pomocí dvou (čtyř (+napájení)) vodičů připojit až 128 zařízení. Naše Arduino bude pracovat jako tzv. master - zařízení, které je na sběrnici pouze jedno, ovládá komunikaci a generuje hodinový signál na pinu SCL. Zbylých 127 zařízení jsou tzv. slave - ti jsou řízeni masterem a bez jeho pokynu nic neudělají.

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), další Arduino a spoustu dalšího.

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

Teorie také tvrdí, že na SDA a SCL by jste měli odporem připojit VCC. Z vlastní zkušenosti vám ale řeknu, že na krátké vzdálenosti (zapojování pár zařízení v breadbordu atd.) tam vůbec nejsou potřeba, a když vám na delších vzdálenostech něco nefunguje, tak teprve teď je odpor potřeba. Sběrnice je určena pro krátké vzdálenosti, ale s odpory nemám nejmenší problém natáhnout sběrnici přes 10m UTP kabelu (a možná půjde i více) a na druhém konci vpohodě funguje LCD i s expandery.

I²C adresa

Každé zařízení má svoji adresu. Dost často na modulu naleznete 3 piny, označované jako A0, A1, A2. Přivedením napětí na jednotlivé piny pak lze 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, 5V na pinu

Adresu lze zapsat decimálně (32) nebo hexadecimálně (0x20). Rozdíl v tom podstatě není žádný, jen občas se někde dočtete jeden zápis, podruhé jiný. A jak zjistit adresu? Při koupi součástek z eBaye se mi dost často stává, že zařízení má jinou adresu, než uvádí prodejce. Lze použít I²C scanner ze stránek Arduina. Sketch nahrajte do desky, otevře Serial Monitor a Arduino vypíše všechna zařízení na sběrnici.

LCD display

Pokud si koupíte LCD display, bude k věmu připájena destička se 4 vývody - VCC, GND, SDA, SCL. Zapojení všech těchto zařízení je jednoduché - VCC na 5V, GND na zem, SDA na SDA, 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.

z 6. dílu víme, jak LCD funguje, a LCD přes I²C se ovládáním nijak moc neliší.

#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, jenže jako parametry není použit seznam RS, E, Dx pinů, ale pouze I²C adresa a počet znaků na řádek a řádků. Tudíž display je připojený na adrese 0x27, má 4 řádky a 20 znaků. Poté lcd inicializujeme (lcd.init()) a jelikož i piny pro podsvícení jsou připojené na I2C čip, musíme pomocí lcd.backligh() zapnout podsvícení. Funkce print() a setCursor() již známe.

I²C IO expander

Došly vám piny a potřebujete ještě něco připojit? Nevadí, stačí si koupit IO expander PCF8574. Na sběrnici jich můžeme připojit až 8, což je 8*8=64 pinů (jelikož skoro všechny jsou vyráběny se stejnou adresou, pokud budete mít štěstí, může se vám podařit sehnat i jiné rozsahy). Na expanderu máme 8 digitálních pinů, jenže práce s ním je trochu těžší na pochopení, ale pak už to je snadné. Pro následující vysvětlení předpokládám, že chápete princip dvojkové soustavy.

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 jak požadované signály na pinech převést na hodnoty a zase zpátky?

8 pinů na expanderu si označíme čísly 0-7, jak sou popsány na čipu. Tyto čísla pro nás budou mocniny dvojky (už chápete kam tím mířím?). Tudíž první pin (0) dostane číslo 1, druhý (1) dostane 2, třetí (2) dostane 4 a tak dále. Tudíž budeme mít něco takového:

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

A 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). Tudíž toto:

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 vznikne číslo v dvojkové soustavě - 10000100. To už jenom převedeme do desítkové soustavy a máte číslo, které můžeme odeslat na expander - tedy 132. Pro zápis na expander musíme mít naimportovanou knihovnu Wire.h a v setupu zavoláme 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. A ve třetím se provede samotné odeslání na expander. Funkce endTransmission() také vrací číslo podle úspěšnosti, pokud se data zapsat povedlo, vrátí 0, pokud zařízení neexistuje, vrátí 4. Jen nečekejte, že na čipu bude výstup čistých 5V, napětí může být nižší.

A nyní pokud budeme potřebovat použit piny jako vstupní. Princip fungování je úplně stejný. Zase jedničky a nuly na pinech, zase číslo v desítkové soustavě, jenže tentokrát číslo přečteme, a jak ho převést zpět na jedničky a nuly pro jednotlivé piny? 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 a jako druhý parametr vezme pozici čísla (tj. mocninu dvojky) a vrátí, zda je zde 1 nebo 0. Pokud tuto 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é, můžeme získat 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 ifech používat přímo if(bitRead(re­adExpander, 0) == 1) { pin1(); }

V příštím díle se podíváme na komunikaci mezi jednotlivými deskami Arduino.


 

  Aktivity (1)

Článek pro vás napsal Adam Ježek
Avatar
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.

Jak se ti líbí článek?
Celkem (4 hlasů) :
55555


 


Miniatura
Všechny články v sekci
Arduino

 

 

Komentáře

Avatar
Jirka Vavřík:

Nebylo by možná lepší do toho IO expaneru posílat číslo v binární podobě?

Wire.write(B10000100);

Vidíš tak přímo logické hodnoty, které tam posíláš... ;-)

Odpovědět 13. února 21:14
Inteligentní nemá čas si pamatovat, inteligentní musí vymýšlet.
Avatar
Odpovídá na Jirka Vavřík
Michal Žůrek (misaz):

podle mě je to také lepší, ale z nějakého (mě neznámého) důvodu se to moc nedělá.

Odpovědět 13. února 21:20
Nesnáším {}, proto se jim vyhýbám.
Avatar
Drahomír Hanák
Tým ITnetwork
Avatar
Odpovídá na Michal Žůrek (misaz)
Drahomír Hanák:

Nedělá se to, protože je to dost nepraktické pro větší data. Už třeba jen 32 bitové číslo by bylo dost dlouhé, ale i třeba jen 1 byte je otrava zapisovat po bitech. Stejné číslo (32 bitů) můžeš zapsat v šestnáctkové soustavě jen 8 znaky. Navíc je jednoduché z číslic zjistit, na kterých pozicích jsou 1 a 0 ve dvojkovém zápisu čísla. Každá cifra v šestnáctkové soustavě totiž reprezentuje 4 cifry ve dvojkové zápisu, a proto je takový zápis mnohem kompaktnější. Navíc snad každý programovací jazyk podporuje zápis čísel v šestnáctkové soustavě. Céčkovité jazyky mají 0xFFFFFFFF (kde 0x je prefix a pak následuje zápis čísla), v Pascalu je to třeba $FFFFFFFF. Ale asi je to pořád lepší, než zapisovat hodnoty v desítkové soustavě (pokud teda nechci všechny bity interpretovat jako 1 číslo)

 
Odpovědět  +1 13. února 21:55
Avatar
Odpovídá na Drahomír Hanák
Michal Žůrek (misaz):

to mě taky napadlo, ale myslím si že minimálně v tutoriálech nebo obecně článcích na internetu by bylo dobré to vypsat celé.

Odpovědět 13. února 23:11
Nesnáším {}, proto se jim vyhýbám.
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na Drahomír Hanák
ostrozan:

On ale má na mysli posílání jednoho jediného bajtu na expander, jinak máš samozřejmě pravdu.

 
Odpovědět 14. února 0:16
Avatar
ostrozan
Redaktor
Avatar
ostrozan:

jen bych upřesni/doplnil/u­pravil toto tvrzení:

Na sběrnici jich můžeme připojit až 8, což je 8*8=64 pinů (jelikož skoro všechny jsou vyráběny se stejnou adresou, pokud budete mít štěstí, může se vám podařit sehnat i jiné rozsahy).

I2C součástky mají 7,8, nebo 10ti bitovou slave adresu - ale držme se PCF8574

ten ji má 7mi bitovou

adresa se skládá ze tří částí :
bit 7 : 4 - pevná adresa určující typ součástky
bit 3 : 1 - volitelná část adresy realizovaná vývody A0,A1,A2 na samotné součástce
bit 0 - R/W - určuje, jestli se na té adrese bude provádět zápis nebo čtení

konkrétně adresa typu PCF8574 je 0 1 0 0 A2 A1 A0 R/W

počty adresovacích pinů se ale typ od typu liší - a je to dáno použitím - většinou jsou tři - A0 - A2 a to dokonce i tam kde zaberou skoro polovinu všech vývodů - paměti EEPROM,FLASH

naproti tomu RTC (hodiny) PCF8563 nemají adresovací pin žádný - na co taky že?

jen pro doplnění - slave adresa PCF8563 je 1 0 1 0 0 0 1 R/W

od tohohle všeho vás odstiňuje milosrdné arduino, ale když už tady Adam ten I2C bus tak rozpitval, tak ať je to kompletní :-)

 
Odpovědět  +1 14. února 13:03
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.