I2C - relativně jednoduše

Motivace

I2C patří k nejrozšířenějším seriovým sběrnicím. Díky tomu se vyrábí nepřeberné množství integrovaných obvodů, které po I2C komunikují. Mezi ně patří AD a DA převodníky, všechny druhy pamětí, čidla teploty, tlaku, světla a vlastně všech možných veličin. Připojíte k ní expandéry, drivery displejů a vůbec skoro všechno. A proto se jí podíváme na zoubek !

Úvod

První část návodu bude zaměřena na pravidla komunikace na I2C sběrnici. Nebude však vyčerpávající, zaměřím se na sitauci, kdy je na sběrnici pouze jeden master. Je to častější a většině z vás to bude postačovat. Oddělit teoretickou část se přímo nabízelo, protože na ni pak budu moct navázat návodem nejen na Atmely ale i na STM32.

I2C

I2C je synchronní sběrnice, má tedy clock, kterým se jednoznačně určuje kdy má přijímač číst stav datové linky. Díky tomu můžou datové rychlosti dosahovat celkem vysokých hodnot 100kbit/s, 400kbit/s, 1Mbits nebo až 3.4Mbit/s. Celá komunikace se odehrává po dvou vodičích: SDA - data a SCL - clock. Všechna zařízení tyto dva vodiče sdílejí a mohou k nim přistupovat. Díky tomu je možné aby mohl komunikovat kdokoli s kýmkoli. Obecně je ale taková komunikace náročnější na programování. Typicky se proto I2C využívá tak, že na ní je jeden Master, který veškerou komunikaci řídí a několik Slave. Master pak iniciuje komunikaci s kým chce a nemusí se starat o to že by mu ji někdo mohl narušit (nebo že by ji mohl narušit on někomu). Jestliže je na sběrnici více Masterů, existuje sada pravidel jak se musí chovat aby mohli na sběrnici pracovat společně a neovlivňovali se. Touto sestavou se ale nebudeme zabývat.

I2C - Fyzická vrstva

Samotný komunikační protokol není příliš náročný. Na každé z linek je připojen pull-up rezistor, díky tomu je linka v neutrálním stavu v log.1. Všechna zařízení po ní komunikují pomocí otevřeného kolektoru (open-colector nebo open-drain). To znamená, že mohou linku pevně stáhnout k zemi a udělat na ní log.0. Když chtějí na sběrnici log.1, odpojí se od ní a nechají pull-up rezistor aby sběrnici stáhl zpátky k Vcc. Díky tomu nemůžou vzniknout na sběrnici nebezpečné kolize (nikdo tam totiž nemůže "tvrdě" držet log.1). Komunikace vždy začíná vysíláním START sekvence a končí vysíláním STOP sekvence. START sekvence vypadá tak že se na SDA vygeneruje sestupná hrana zatímco je SCL držena v log.1. STOP sekvence pak vypadá opačně. SCL je zase držena v log.1 a na SDA se vygeneruje vzestupná hrana (viz obrázek a1). Z toho plyne, že krom těchto dvou případů nesmí nikdy SDA měnit svoji hodnotu když je SCL v log.1 (pokud by se to stalo znamenalo by to právě vysílání START nebo STOP sekvence). Přenos jednoho bitu zprávy pak vypadá tak, že zatímco je SCL v nule, změní vysílač hodnotu SDA na takovou jakou potřebuje. Poté nastaví SCL do log.1. S touto vzestupnou hranou pak přijímač čte hodnotu datové linky (vysílaného bitu). Vysílač pak vrátí SCL do nuly a celá akce se opakuje s dalším bitem zprávy. Celá zpráva se skládá z 9 bitů. Prvních osm je datových. 9.bit slouží jako potvrzovací (acknowledge). Vysílač odvysílá 8 datových bitů a ještě jednou pokračuje v generování clocku jako by chtěl odvysílat 9.bit, ale datovou linku (SDA) nechá volnou ! Přijímač pak v tomto jednom bitu může a nebo nemusí dát vysílači potvrzení. Přijímač buď v 9.bitu stáhne sběrnici k log.0, čímž vysílači zprávu potvrdí (ACK) a nebo nechá sběrnici na log.1 a vysílač z toho vyrozumí, že zpráva není potvrzená (NACK). Přirozeně celé časování je náročnější, ale tento pohled na věc by vám měl na dlouho vystačit :)

Nejjednodušší zpráva se pak sestává ze START sekvence, odvysílání 8 bitů, potvrzovacího 9.bitu a STOP sekvence. Celou zprávu vidíte na obrázku a1. Na obrázku a2 vidíte START sekvenci. Žlutá linka - SDA - přechází z log.1 do log.0 zatímco je SCL (modrá linka) v log.1. V tomto okamžiku všechna zařízení na sběrnici zpozorní a začínají poslouchat. Pak následuje vysílání 8 bitů. V našem případě se vysílají data 0b11001000. To by jste měli docela dobře vidět. Na obrázku a3 je pak vidět 9.bit zprávy. Vysílač vygeneruje ještě jeden pulz na SCL a přijímač v našem případě nechal linku v log.1 a signalizuje tím, že zprávu nepotvrzuje. Čemuž se nelze divit, protože v tomhle případě jsem žádný přijímač na sběrnici nepřipojil. Za devátým bitem pak přichází STOP sekvence (vzestupná hrana na SDA zatímco SCL je v log.1). A to je konec komunikace. Potvrzovací bit mimo jiné slouží také k tomu aby vysílač poznal zda ho na sběrnici vůbec někdo poslouchá. Proto se potvrzení zprávy provádí pomocí log.0 - nějaké zařízení musí svoji aktivitou stáhnout sběrnici k nule. Kdežto log.1 se na sběrnici dostane když na ni nikdo "nesahá" pomocí pull-up rezistoru.

Obrázek a1 - jednoduchá komunikace na I2C, START + 8bit + 1bit + STOP
Obrázek a2 - detail na START sekvenci
Obrázek a3 - detail na potvrzovací 9.bit a na STOP sekvenci

Neodpustím si malou technickou poznámku. Kdo nemá čas na hlouposti, tak ji přeskočte. Všimněte si tvaru hran v signálu. Velice dobře je to vidět na SCL na obrázcích a2 a a3. Tvar vzestupné hrany odpovídá nabíjení RC článku (a měl by vám být povědomí). Celá sběrnice má nějakou kapacitu (je to prostě kondenzátor). Čím je linka delší a rozlehlejší tím je kapacita vyšší. A protože do kladných hodnot ji táhne pouze pull-up rezistor tak probíhá přechodový děj nabíjení kondenzátoru. Tento jev ale limituje komunikační rychlost. Čas potřebný k tomu aby sběrnice dosáhla log.1 je přibližně dán součinem pull-up rezistoru a kapacity sběrnice. Pokud ho chcete zkrátit musíte buď snížit kapacitu sběrnice, nebo zmenšit pull-up rezistory. Snížení pull-up rezistorů má ale za následek zvýšení odběru během komunikace. U rozlehlejších sběrnic se tedy hledá optimální poměr tří faktorů odběru, délky sběrnice, komunikační rychlosti. Všímavější z vás by mohla překvapit i sestupná hrana signálu. Je pomalá ! V tomto případě trvá přibližně 200ns. Mikrokontroléry mívají vzestupné a sestupné hrany na nezatíženém výstupu běžně v jednotkách ns. Čím to tedy je ? Je to záměr. Výstupy I2C sběrnice mívají omezenou rychlost přeběhu. Slouží to k limitování odrazů a zákmitů na sběrnici.

I2C Protokol

Je potřeba stanovit ještě několik pravidel. V prvé řadě je potřeba vyřešit otázku adresace. V druhé řadě pak směr toku dat po sběrnici. O tom od koho a komu budou data téct rozhoduje první byte odeslaný po START sekvenci. Komunikaci iniciuje Master odesláním START sekvence následované 8 mi bity a jedním potvrzovacím bitem. Prvních 7 bitů tvoří adresu a 8. bit rozhoduje o směru toku dat. Slave, jehož adresu Master poslal potvrdí v 9.bitu svou připravenost ke komunikaci. Pokud žádný slave takovou adresu nemá (nebo nemůže komunikovat), Master nedostane potvrzení (9.bit bude v log.1). V osmém bitu zprávy Master informuje slave zda po něm chce aby vysílal nebo přijímal. Vynulováním 8.bitu dává Master na vědomí, že bude vysílat (stále až do ukončení komunikace) a slave tak ví, že nemá zasahovat do datové linky a že má přijímat. Odesláním 8. bitu v log.1 dostává Slave informaci, že po zbytek komunikace bude vysílat data on a Master bude přijímat. Jestliže se Master rozhodl posílat data (8.bit v nule), pokračuje v komunikaci odesláním dalších 8 datových bitů, jejichž příjem slave opět v 9.bitu potvrdí nebo nepotvrdí. Takhle může master vysílat libovolné množství bytů za sebou. Samozřejmě pokud to slave umožní (většina zařízení to umožňuje). Kdykoli by chtěl slave naznačit Masterovi, že nedokáže jeho data zpracovávat, má možnost mu v 9.bitu za každým odeslaným bytem nedat potvrzení. Master by pak měl přestat vysílat. Jestliže se Master rozhodne že chce přijímat data (8.bit po START sekvenci v log.1), musí uvolnit datovou linku, ale dál generovat clock. Komunikace tak pokračuje další 9 bitovou sekvencí, kde prvních 8 bitů vysílá slave a v 9.bitu dává naopak Master potvrzení a tak stále dokola než přijme tolik bytů kolik potřebuje. Master potvrzuje každý přijatý byte, krom posledního. Poslední byte nepotvrdí a slave se tak dozví, že komunikace končí a má uvolnit SDA linku. Pak master odešle STOP sekvenci. Příklad takové komunikace je na obrázku a4 a a5, které si okomentujeme.

Obrázek a4 - Master odesílá 1 byte dat (0b00000101) pro slave se 7.bit adresou (0x60)
Obrázek a5 - Master přijímá 1 byte dat od slave se 7.bit adresou (0x60)

V obou případech budeme komunikovat se Slave obvodem jehož 7 bitová adresa je 0x60 (0b1100000). Konkrétně jde o DA převodník MCP4726. Na obrázku a4 je odeslání jednoho bytu dat do slave zařízení. Nejprve Master odvysílá adresu slave a v 8.bitu nastaví že chce posílat data. Slave mu dá v 9.bitu potvrzení. Master pak odvysílá 8 bitů dat (0b00000101) a opět dostane od slave potvrzení. Pak komunikaci ukončí STOP sekvencí. Na obrázku a5 je přijetí jednoho byte dat ze slave zařízení. Master opět začne komunikaci odesláním START sekvence následované adresou a v 8.bitu (log.1) dá najevo že chce číst. Slave mu dá opět potvrzení. Master uvolní SDA linku aby mohl slave odesílat data a generuje dále clock. Přijímá 8 bitů a v 9.bitu nedá Master potvrzení. Pak ukončí komunikaci STOP sekvencí. Když master přijímá může a nemusí dávat potvrzení, zda ho slave vyžaduje nebo ne je individuální a je jen na slave jak s potvrzením nebo nepotvrzením od Mastera naloží. Bývá ale pravidlem že Master dává potvrzení vždy, krom posledního bytu zprávy. Tedy poslední byte těsně před odvysíláním STOP (nebo nové START) sekvence.

I2C - Adresování

Na chvíli se ještě pozastavíme u adresy. Protože je adresa 7 bitová, může existovat jen 127 unikátních adres (adresu 0 nepočítám). Z toho také plyne, že na sběrnici může být připojeno pouze 127 zařízení. Vzhledem k tomu, že adresu zařízení specifikuje výrobce už při výrobě, vyvstává tu jistá komplikace. Vyrábí se totiž daleko víc jak 127 druhů zařízení. Dejme tomu že si koupíte RTC obvod DS3231. Ten má pevně danou adresu (0x68). Nemůžete tedy dva takové čipy připojit k jedné sběrnici. Protože pak by jste měli dva slave obvody se stejnou adresou a při pokusu s nimi komunikovat by došlo ke kolizi dat (vysílali by oba slave zároveň). Nebo si připojíte tří-osý magnetometr HMC5883L, který má adresu 0x1E. Nic dalšího s touto adresou už pak na sběrnici připojit nemůžete. Prostě nesmíte připojit dvě zařízení se stejnou adresou na sběrnici (kromě dobře rozmyšlených vyjímek). Výrobci integrovaných obvodů to řeší tak, že jeden nebo více vývodů integrovaného obvodu slouží k nastavení adresy. Připojením těchto vývodů na Vcc nebo GND (někdy i nechání odpojených) změní adresu zařízení. Tím dostáváte jistý manévrovací prostor si adresy slave obvodů upravovat. Typicky jsou touto možností vybaveny obvody u nichž se počítá že jich může být na sběrnici více. Například EEPROM paměti, nebo expandéry. Můžete si pak koupit třeba 8 stejných EEPROM a každé z nich hardwarově nakonfigurovat jinou slave adresu. Malé AD a DA převodníky nemusí mít dost volných vývodů ke konfiguraci adresy. Výrobce proto vyrábí několik různých variant stejného převodníku pouze s odlišnou adresou. V takovém případě je potřeba rozmýšlet který z nich použijete už při nákupu. Další poznámku bych věnoval vyjádření adres. Výrobce vám přirozeně v datasheetu musí specifikovat jakou má zařízení adresu. Někteří výrobci adresu zapisují jako 7 bitovou a někteří jako 8 bitovou. Je potřeba v tom rozlišovat. Například výše zmíněný magnetometr má 7 bitovou adresu 0x1E což je v binárním vyjádření 0b00011110, 8 bitově lze pak tuto adresu vyjádřit jako 0b00111100 (0x3C). Jde o jednu a tu samou adresu ! V osmibitovém vyjádření musí být adresy zarovnány doleva. Jejich poslední bit musí být nulový (jak víme slouží jako příznak čtení / zápisu). Je proto potřeba si ověřit jak ji výrobce zapisuje. Špatná interpretace adresy z datasheetu bývá nejčastější důvod proč se s vámi slave nebaví... a někdy je pěkný opruz z datasheetu vyčíst který zápis má výrobce ve skutečnosti na mysli.

I2C - Odesílání více bytů

Na další ukázce komunikace využijeme dekódování z osciloskopu. Schválně se podívejte na obrázek a6 a zkuste si sami dekódovat adresu slave s nímž master komunikuje a co mu posílá. Měli by jste to dokázat. Pokud to nezvládnete, něco vám uniklo a měli by jste se vrátit a vyjasnit si to. Master opět START sekvencí zahájí komunikaci a chce vysílat data pro slave se 7 bitovou adresou 0x60. Slave dává potvrzení a master odešle dva byty dat. Po každém z nich dostane potvrzení od slave. Osciloskop barevně znázorňuje komunikaci. Svislou šedou čarou (s konci mířícími vpravo) označí START, fialovou barvou pak označuje byte s adresou. Písmenkem [w] značí že Master bude zapisovat. Zelená značka znamená, že slave dal potvrzení. V modrých rámečcích jsou pak data. Svislá šedá čára (s konci vlevo) značí STOP sekvenci. Tento druh komunikace kdy Master pouze vysílá se používá u zařízení jako jsou expandéry nebo DAC převodníky. Prostě tam kde není potřeba od slave číst data. Nejspíš vás znepokojují úzké jehly na SDA lince nacházející se těsně za 9.bitem každé zprávy. Ale nemusí vás moc rozčilovat, jednak jsou v místech kde je SCL v log.0, tedy tam kde SDA smí dělat cokoli a nikdo ji nečte a krom toho je jejich přítomnost normální. Master totiž musí v 9.bitu uvolnit SDA linku aby mohl slave vysílat potvrzení. Jakmile slave potvrzení odvysílá musí přirozeně zase přenechat SDA linku Masteru. Uvolní ji tedy a na chvíli linka nepatří nikomu. Slave už ji uvolnil a master ji ještě nezačal ovládat. Uvolněná linka je na krátkou dobu v log.1. Jakmile ji Master převezme, přivede na ni data jaká potřebuje (v tomto případě log.0) a v tom okamžiku krátká jehla skončí.

Obrázek a6 - Master vysílá 2 byte dat pro slave se 7.bit adresou (0x60), slave všechny příjmy potvrdí

I2C - Běžná komunikace

Mohlo by se zdát že už umíme všechno co je potřeba, ale bohužel to tak není. Zatím jsme byli schopni pouze přijímat nebo pouze vysílat. to by u AD nebo DA převodníků asi i stačilo. Spolu s odeslanou adresou jsme se rozhodli zda budeme číst nebo zapisovat a zbytek komunikace už probíhal pouze jedním směrem. Typicky ale budete chtít nejprve něco poslat a pak něco přijmout. Třeba EEPROM paměti musíte nejprve nějak sdělit kterou její buňku vlastně chcete číst. Stejně tak RTC obvodu nejprve musíte sdělit kterou z mnoha informací (sekundy, minuty, hodiny, dny, měsíce, roky, alarm...) chcete číst. Jakou formou to udělat už záleží na konkrétním obvodu. Většina výrobců dodržuje jednoduché pravidlo. Nejprve cílovému zařízení pošlete zprávu s pokynem co po něm chcete (třeba co z něj chcete číst) a teprve pak si to z něj přečtete. Jenže jak jsme si již řekli směr toku dat se určuje spolu s adresou a je definitivní. Abychom mohli změnit směr (vysílání na příjímání) musíme znovu poslat adresu. A tu můžeme poslat jen po START sekvenci. Takže prostě musíme zahájit novou komunikaci. Teoreticky by jste mohli nejprve poslat jeden byte do slave zařízení. Tak jak už to umíme. Komunikaci normálně ukončit a hned potom zahájit novou komunikaci (tentokrát čtení 1 byte) a přečíst si odpověď. Tahle operace má ale riziko. Dejme tomu, že zde máme jednu EEPROM paměť a že je na sběrnici více masterů, kteří z ní něco chtějí vyčítat. Vy tedy pošlete paměti jeden byte s informací o tom kterou její buňku chcete číst. Ukončíte komunikaci (zápis) s úmyslem zahájit novou (čtení). Ale jakmile ji ukončíte jiný Master vám sebere sběrnici - má na to právo, protože jste ukončili komunikaci. A rozhodne se také mluvit s vaší pamětí ... a přirozeně si z ní chce přečíst jinou buňku. Takže jí pošle novou informaci o tom jakou buňku chce číst on a pak opět ukončí komunikaci. V tom okamžiku zabavíte sběrnici vy, protože pořád čekáte na to až si budete moct z paměti vyčíst vámi požadovaná data ... a vyčtete si je. Bohužel ale dostanete data, která jste nechtěli. Paměť totiž naposled dostala informaci o tom co má vysílat od jiného Masteru, ne od vás ... takže vám pošle data, která si vyžádal on. Zmatek. Aby k těmto problémům nemohlo docházet, má každý Master právo odvysílat tzv opakovaný START (Repeated Start). Po odeslání dat do slave master prostě nepošle STOP ale pošle znovu START (tak jak jsme si uváděli na začátku). A po odeslání START sekvence opět následuje adresa a specifikace zda Master bude vysílat nebo číst ... prostě celá komunikace začíná nanovo. Ale nikdo vám nemůže vzít slovo. A přesně takhle si to předvedeme.

Obrázek a7 - Master nejprve posílá 1 byte, kterým specifikuje co od Slave obvodu chce
pak restartuje komunikaci a čte 1 byte ze slave
Obrázek a8 - zoom na opakovaný start (Repeated Start)

Na obrázku a7 vidíte celou komunikaci. Nejprve Master iniciuje komunikaci odesláním START sekvence. Pak pošle adresu slave zařízení (0x68) s příznakem zápisu. Dostane potvrzení, pošle data a opět dostane potvrzení. V tomto okamžiku slave obdržel 1 byte dat a díky tomu ví co po něm Master chce. Master pak vygeneruje znovu START, jehož detail je na obrázku a8. Nic zvláštního, sestupná hrana na SDA při SCL v log.1 - přesně to co jsme si na začátku určili že je START. Potom tak jak pravidla určují následuje opět adresa, tentokrát s příznakem čtení. Slave opět potvrzuje a Master přijímá 1 byte dat a protože to jsou poslední data co master chce přijmou, nedá v 9. bitu potvrzení. Pak už jen STOP sekvencí celou komunikaci ukončí. V tomto případě jsme komunikovali s RTC obvodem a chtěli jsme si od něj vědět kolik je minut. Obvod má minuty uloženy v paměti na adrese 0x01. My jsme mu proto poslali nejprve 0x01 aby věděl co po něm chceme a pak jsme zahájili čtení a on nám poslal 21. Toto je typický příklad jak bude vypadat komunikace s celou řadou zařízení. Některé slave obvody ale budou vyžadovat jiný styl komunikace. Třeba větší paměti budou vyžadovat aby jste jim nejprve poslali dva byty, protože 1 byte by na adresaci nestačil. Některá zařízení budou vyžadovat v prvním bytu nejen adresu toho co po nich chcete ale i nějaké další informace... některá zařízení zase naopak ani zápis nevyžadují a automaticky posílají to co chcete (ony sami ví co chcete). Můžeme si ještě udělat ukázku jak to vypadá když chcete vyčíst ze slave víc jak jeden byte zároveň (obrázek a9). Tady jsme RTC obvod přesvědčili, že po něm chceme aby nám poslal minuty i sekundy. Sekundy jsou uloženy v paměti 0x00, minuty pak o jedno dál (na 0x01). Začátek komunikace vypadá jako obvykle. Nejprve do RTC zapíšeme hodnotu 0x00, čímž mu dáváme vědět že nám má poslat sekundy. Vygenerujeme opakovaný START a zahájíme příjem. Po prvním přijatém bytu dá Master potvrzení a po druhém bytu nedá potvrzení (protože má v plánu komunikaci ukončit). RTC obvod nám v prvním bytu poslal to co jsme po něm chtěli (sekundy) a v druhém poslal to co má v paměti na další adrese (minuty). Kdybychom přijali více než dva byty, mohli bychom si přečíst obsah celé paměti RTC obvodu. Tento postup podporuje mnoho integrovaných obvodů, protože zrychluje komunikaci. Kyby vám slave posílal vždy jen jeden byte a o každý další by jste museli znovu žádat a znovu specifikovat který chcete, hodně by vás to zdržovalo. Přečtení jednoho byte tímto způsobem zabere na sběrnici čtyři bloky komunikace. Adresu, zapsání 1 byte, adresu , čtení 1 byte. Díky tzv "multibyte read" tedy možnosti číst více bytů po sobě se uspoří spousta provozu na sběrnici. Takže pokud budete moct tak toho využívejte.

Obrázek a9 - Multibyte read - Master vyčítá dva byty ze slave

I2C - clock stretching

Při pohledu na předchozí komunikace vás možná napadlo, že je slave v nehezkém časovém presu. Když má slave něco odesílat a vy mu generujete clock, musí se hodně snažit aby stihl data připravit včas. Slave má naštěstí ale v rukou možnost v určitém okamžiku komunikaci pozdržet. Po odvysílání 9.bitu zprávy, má slave právo držet SCL sběrnici v nule jak dlouho potřebuje. Master SCL uvolní a očekává, že na ní uvidí log.1. Čeká tak dlouho než slave sběrnici uvolní. Díky tomu může slave získat čas a připravit si odpověď pro mastera. Jakmile slave sběrnici uvolní, master pokračuje v generování clocku a slave vysílá nebo přijímá data. Když budete mikrokontrolér používat jako slave nemusíte mít strach, že program nestihne vykonat vše potřebné včas. Tato možnost má ale i svá úskalí. Pokud uděláte v programu na straně slave chybu, může slave držet clock v nule do nekonečna a blokovat tak celý provoz na sběrnici. Clock stretching můžete vidět na následujícím obrázku.

Obrázek a11 - Clock stretching. Slave po 9.bitu drží SCL přibližně 20us v nule a získává tím čas na reakci. Master čeká než se SCL vrátí do log.1, teprve pak pokračuje v generování clocku.

zdroje