Uprzedzę odpowiedź kolegi Macieja.
Wire.write(0x13); // read from port BTa instrukcja ustawia adres rejestru z którego będziemy czytać dane poźniej, w następnych krokach.
W tzw. pseudo-kodzie wygląda to tak:
- wyślij komendę I2C start, celuj w odbiornik o oadresie 0x20
- ustaw adres rejestru - musimy najpierw podać skąd chcemy pobrać dane
- wyślij I2C stop. W tym momencie wewnętrzny adres MCP23017 ustawiony jest na 0x13, czyli GPIOB (BANK=0).
Dalej startujemy transmisję jeszcze raz, ale tym razem będziemy odczytywać jeden bajt z MCP23017. Odczyt nastąpi z adresu ustawionego wcześniej.
Ustawienie BANK=0 przeznaczone jest w zasadzie do sekwencyjnego odczytywania/zapisywania portów A i B. Część kodu Macieja odpowiedzialną za odczyt z MCP23017 można uprościć i zoptymalizować do dwóch funkcji. Niestety nie mam teraz pod ręką układu, żeby go przetestować, ale powinno działać. Starałem się dokładnie komentować kod, myślę, że powinien być zrozumiały. Cała operacja czytania portów z MCP23017 zgrupowana jest w jedną funkcję, która jako argument pobiera adres I2C kości, a wyrzuca stan wszytkich przycisków w 16 bitowym słowie.
uint16_t buttonBank1, buttonBank2; //zmienne przechowujace stan przyciskow
void initMCP23017(uint8_t slaveAddr); //deklaracja funkcji inicjalizujacej
uint16_t readMCP23017(uint8_t slaveAddr); //deklaracja funkcji odczytywania stanu portow
/*
Funkcja inicjalizujaca uklad MCP23017
wszystkie piny jako input + pullup
argument: adres I2C
BANK0 = 0 - wykorzystamy sekwencyjny zapis,
adres jest automatycznie inkrementowany
po kazdej operacji zapisu bajtu
*/
void initMCP23017(uint8_t slaveAddr)
{
// IODIRA -> IODIRB
Wire.beginTransmission(slaveAddr);
Wire.write(0x00); //IODIRA
Wire.write(0xFF); //portA = wejscia, adres skacze do 0x01=IODIRB
Wire.write(0xFF); //portB = wejscia
Wire.endTransmission();
// Pull-ups
Wire.beginTransmission(slaveAddr);
Wire.write(0x0C); //GPPUA
Wire.write(0xFF); //podciagnij wejscia A do VCC,nowy adres = 0x0D=GPPUB
Wire.write(0xFF); //podciagnij wejscia B do VCC
Wire.endTransmission();
}
// Funkcja odczytujaca stany portow GPIOA i GPIOB
// stan zwracany jest w postaci 16bitowego slowa,
// gdzie jeden bit odpowiada jednemu przyciskowi
uint16_t readMCP23017(uint8_t slaveAddr)
{
uint8_t output[2];
uint8_t index = 0;
Wire.beginTransmission(slaveAddr); //I2C Start, wyslij adress
Wire.write(0x12); //ustaw wewnetrzny adres na GPIOA
Wire.endTransmission(); //I2C stop
Wire.beginTransmission(slaveAddr); //I2C Start, wyslij adres
Wire.requestFrom(slaveAddr,2,true); //grzecznie popros o 2 bajty
//po odczytaniu GPIOA wewnetrzny adres
//automatycznie zostanie zwiekszony o 1.
//czyli wyladuje na GPIOB
//"true" na koncu generuje I2C stop i zwalnia
//magistrale, Wire.endTrasmission nie jest potrzebny
while (Wire.available())
{
output[index] = Wire.read(); //wpisz GPIOA i B do tablicy
index++;
}
return uint16_t(output[0] | ((output[1])<<8)); //zgrupuj oba bajty w 16 bitowe slowo
}
Użycie funkcji wygląda następująco:
//inicjalizacja obu ukladow:
initMCP23017(0x20);
initMCP23017(0x27);
//potem gdzies w programie
buttonBank1 = readMCP23017(0x20);
buttonBank2 = readMCP23017(0x27);
W sumie właśnie odkryłem, że istnieje już
biblioteka do MCP23017 . Ech.. arduino

Może ten kod powyżej będzie miał chociaż wartość edukacyjną

Być może nadeszła odpowiednia chwila, żeby odkurzyć mój stary projekt kontrolera do Iłka i pomajstrować co nieco:

W kwestii enkoderów, pan Marek w dość przystępny sposób objaśnia ich działanie, pułapki i inne kruczki, które mogą się pojawić podczas ich obsługi:
http://mirekk36.blogspot.de/2016/02/enkoder-obrotowy-od-podstaw.htmlJeśli chodzi zaś o magistralę I2C to jest ona raczej "krótkiego zasięgu". Zbyt długie kable i spora liczba odbiorników zwiększa pojemności pasożytnicze na liniach, przez to mogą pojawić się przekłamania i błędy. Trzeba mieć to na uwadze projektując systemy modułowe.
Jeszcze takie pytanie: nie macie problemów z drganiami styków w przyciskach? Typu przycisk wyzwalany jest kilka razy itp.? Eliminacja drgań styków to pierwsza rzecz jaką dodaję, jesli urządzenie ma przyciski. Jest taki dość sprytny sposób na filtrowanie drgań kilku-kilkunastu wejść naraz. Wymaga jedynie przerwania Timera ustawionego na ok. 5-10 ms.
/Piter