[015] Magistrala I2C
Zamiast całej rozprawki, którą można by z pewnością napisać na temat magistral, ogólne informacje sprowadzę do jednego zdania: magistrale to połączenia między różnymi blokami urządzenia bądź różnymi urządzeniami, którymi przepływają informacje, czyli dane.
No i tyle wystarczy, gdyż informacji na ten temat w sieci można znaleźć tak dużo, że musiałbym się tylko powtarzać. Magistrale mogą być bardzo różne, powstawały od początku czasów elektroniki i powstają nadal. Jedną z najczęściej stosowanych obecnie jest magistrala o nazwie I2C. Cóż to za dziwna nazwa? Nie ma się co dziwić, trzeba się przyzwyczaić, dziwnych nazw w elektronice spotyka się sporo. To jedna z wielu magistral szeregowych średniej szybkości, początkowo mająca w założeniach zastąpić połączenia równoległe między podzespołami, a więc uprościć przede wszystkim okablowanie wewnątrz urządzeń. Początki sięgają jeszcze lat osiemdziesiątych, a twórcą założeń był Philips. Obecnie standard przeszedł kilka modyfikacji i stanowi jedną z najbardziej popularnych magistral.
Specyfikację łatwo znaleźć w wielu miejscach, więc tylko ogólnie: magistrala korzysta z dwóch linii: danych i zegara, i może łączyć ze sobą wiele urządzeń, korzystając z puli 128 lub 1024 adresów. W danej chwili jedno z nich musi być nadrzędne, ale ten przywilej nie jest zastrzeżony na stałe do wybranego elementu.
Wśród peryferiów można znaleźć wiele układów różnych producentów: od zegarów, pamięci nieulotnych, przetworników analogowo-cyfrowych i na odwrót, po porty równoległe, które z racji powiększania ilości wejść bądź wyjść w systemie, zwie się ekspanderami. I takiemu układowi przyjrzymy się tutaj, a przy okazji poznamy zasady pracy z magistralą I2C w Arduino.
Na płytce edukacyjnej TME znajdują się dwa układy ekspanderów MCP23008 firmy Microchip. Ekspanderów – czyli układów oferujących dodatkowe piny, mogące być portami wejściowymi lub wyjściowymi. Układ może dużo, jak na dość prosty w założeniach zespół ośmiu uniwersalnych portów. Oprócz możliwości definiowania każdego z nich jako wejście lub wyjście, w przypadku wejść można aktywować wstępną polaryzację rezystorem o wartości 100 kΩ, a także korzystać z rozbudowanego systemu generowania przerwań. Na deser dostajemy możliwość sprzętowego wyboru jednego z ośmiu adresów, co umożliwia pracę ośmiu takich układów na jednej szynie I2C.
Układ pracuje przy napięciu od 1,8 do 5 woltów i pozwala obciążać wyjścia prądem 25 mA. Dość popularne stały się moduły wykorzystujące ten ekspander w formie interfejsu ograniczającego zużycie linii: jak na przykład szesnastoprzyciskowe klawiatury czy wyświetlacze LCD. W pierwszym przypadku oszczędność wynosi sześć portów, w drugim – cztery, przy czym magistrala nie jest wykorzystana na wyłączność.
Jak to połączyć? Nóżka pierwsza to zegar, druga – dane. Ze względu na wymagania biblioteki, o której zaraz opowiem, łączymy je kolejno z portami A5 i A4 Arduino.
Nóżki 3, 4 i 5 umożliwiają wybór adresu układu. Mają wpływ na najmłodsze bity rejestru adresu, w którym pozostałe bity ustawiono na stałe. Zatem adres może należeć do puli od 32 do 39. Na mojej płytce ma on wartość 36, co wynika z działania: 32 – bo tyle stanowi niezmienny adres bazowy + 1*4 – bo mamy wysoki stan na wejściu A2 + 0*2 + 0*1 – bo mamy niskie stany na wejściach A1 i A0. Drugi układ znajdujący się na płytce ma adres 32, ale o nim w tym artykule mówić nie będę.
Nóżka 6 to reset, aktywowany poziomem niskim. Nie chcąc go używać, należy go podłączyć do poziomu wysokiego. Nóżka 8 daje sygnał przerwania. Tym razem nie będziemy z niego korzystać.
Zasilanie należy podać na nóżki 18 – plus oraz 9 – minus. W końcu na nóżkach od 10 do 17 mamy porty, które możemy wykorzystać według własnego widzimisię.
Układ zawiera 11 rejestrów konfiguracyjnych i oczywiście współpracując z nim, można odwoływać się do nich wprost. Ale to zostawiam dociekliwym. Jak każde chyba peryferium, także to posiada kilka bibliotek, które znakomicie wszystko upraszczają i w oparciu o nie pokażę, jak można sterować tym ekspanderem. Na początek – coś bardzo prostego. Spróbujemy analizować wejście znajdujące się na nóżce 10 i w zależności od jego stanu sterować diodą świecącą wbudowaną w Arduino.
A tak przy okazji: dlaczego zaczynam od takich, wydawałoby się, głupot? Otóż jest zasada: przymierzając się do pierwszych kroków z dotychczas nieznaną materią, a taką jest tutaj magistrala I2C, jak i ten konkretny ekspander, należy zacząć od rzeczy najprostszej. Jeśli zadziała – wyeliminujemy wszelkie sprzętowe problemy na etapie porozumiewania się mikrokontrolera z tym urządzeniem, zarówno w zakresie magistrali jak i jego samego.
#include <Adafruit_MCP23008.h> // Dołącz bibliotekę sterującą układem MCP23008
Adafruit_MCP23008 ekspander; // Nadaj układowi nazwę "ekspander"
const byte LED = 13; // Adres portu sterującego diodą świecącą.
void setup() {
pinMode(LED, OUTPUT); // Port diody świecącej deklaruj jako wyjście.
ekspander.begin(36); // Inicjuj bibliotekę sterującą układem MCP23008 pod adresem 0x4
ekspander.pinMode(0, INPUT); // Zadeklaruj port zerowy ekspandera jako wejście.
ekspander.pullUp(0, HIGH); // Dołącz rezystor podciągający.
}
void loop() {
digitalWrite(LED, ekspander.digitalRead(0)); // Przekaż na port diody świecącej stan portu zerowego ekspandera.
}
Program jest bardzo prosty. Jak zwykle na początku dokładamy bibliotekę. Jest ich kilka, ta, którą wybrałem, jest dość prosta i przejrzysta. Specyfika wymaga nadania nazwy układowi, co też czynię tutaj:
Adafruit_MCP23008 ekspander; // Nadaj układowi nazwę "ekspander"
Do sprawdzenia działania wykorzystam arduinową diodę świecącą, jak już wiemy, umieszczoną na porcie 13. W związku z tym port ten należy zadeklarować jako wyjście. Teraz nastąpią deklaracje związane z ekspanderem. Zaczynamy od inicjacji układu, odnosząc się do niego bezpośrednio za pomocą adresu:
ekspander.begin(36); // Inicjuj bibliotekę sterującą układem MCP23008 pod adresem 0x4
Teraz trzeba zdefiniować zerowy port jako wejście.
ekspander.pinMode(0, INPUT); // Zadeklaruj port zerowy ekspandera jako wejście.
Na koniec trzeba jeszcze podłączyć do niego rezystor podciągający, chyba że polaryzację wymusimy z zewnątrz.
ekspander.pullUp(0, HIGH); // Dołącz rezystor podciągający.
W pętli głównej będzie tylko jedna instrukcja: stan zerowego portu będzie sterował diodą świecącą. Po wysłaniu programu do mikrokontrolera dioda będzie świecić albo nie, w zależności od tego, czy nóżka portu zerowego będzie zwierana do masy, czy będzie wisiała w powietrzu, a więc będzie zwierana do wysokiego poziomu dołączonym powyższą instrukcją rezystorem.
Jak mówiłem, program jest nudny, ale pozwala upewnić się, że komunikacja z ekspanderem pracuje i wszystko działa jak powinno. Oczywiście znajdziemy mnóstwo zastosowań takiej funkcjonalności układu, gdzie potrzebne są po prostu wejścia dwustanowe. Ciekawsze zastosowanie ekspandera omówię w kolejnym artykule.