I2C Port-Expander (Bsp. PCF8575)
| Anschlusstabelle I2C Port Expander | |
|---|---|
| GND | Masse (3 miteinander verbundene Anschlüsse auf dem Modul) |
| VDD | +5V Spannungsversorgung (3 miteinander verbundene Anschlüsse auf dem Modul) |
| P0 ... P7 | Ausgabe bzw. Einlesen der Pins durch 8 bit (Byte) |
| P10 ... P17 | Ausgabe bzw. Einlesen der Pins durch weitere 8 bit (Byte) |
| SCL | I2C Busleitung Serial Clock |
| SDA | I2C Busleitung Serial Data |
| INT | Interrupt-Ausgang. Wird bei einer Eingangs-Pegeländerung kurzzeitig LOW |
Adresse
Die Adresse hat bei einem I2C Teilnehmer 7 bit. Die Adresse startet mit '0100'. Die letzten 3 Digits können über
die Adresseingänge A0-A2 eingestellt werden. Bei dem hier verwendeten Modul sind diese Adresspins mit Lötbrücken
auf GND geschalten und somit '0'. Die Adresse ist also '0100000' = 32 = 0x20. Die Adresse lässt sich auch mit
einen I2C Scanner wie er bei der Arduino Bibliothek wire.h enthalten ist herausfinden.
Belastung
Durch einen internen PullUp Widerstand sind die Ports bei offenem Kollektor auf +5 geschaltet. Laut Datenplatt dürfen die PullUps nur mit 100µA belastet werden. Genug für digitale Signale. Eine LED direkt lässt sich jedoch nur schalten indem man den Minuspol durchschaltet. Den Ausgangstransistor also schließt und den Port auf GND legt. So können die Ports je bis zu 25mA belastet werden.
Interrupt-Ausgang INT
Verwendet man die Ports als Eingänge wird bei jedem Flankenwechsel egal welchen Ports der INT Ausgang für rund 1 Sekunde auf LOW gelegt. So kann der Mikrocontroller bei Bedarf ohne den Umweg über den I2C übermitteln, dass sich die Eingangssignale geändert haben. Dieser INT Ausgang wird dann idealerweise an einen Interrupt-Pin des Arduinos angeschlossen. Der INT Anschluss ist optional.
Ausgänge auf HIGH oder LOW setzten
Im Port-Expander sind zwei 8 bit Register für die Ausgänge P00 bis P07 und P10 bis P17 integriert. In diese Register wird geschrieben ob an den Ports HIGH oder LOW ausgeben werden sollen. Für ein LOW Pegel wird in das Byte an der entsprechende Stelle eine (0) gesetzt. Soll der Ausgang HIGH ausgeben wird eine (1) geschrieben.
Werden mehr als 2 Byte übertragen werden die Daten durchgeroutet. Die letzten beiden Byte sind dann in den Registern vorhanden.
#include "Wire.h"
#define adresse 32 // I2C Adresse des PortExpanders
void setup() {
Wire.begin();
Serial.begin(9600);
}
void loop() {
Wire.beginTransmission(adresse);
Wire.write(B00000000); // In/Out (P07 - P01)
Wire.write(B00000000); // In/Out (P17 - P10)
Wire.endTransmission(); // hier wird alles gesendet
while (1);
}
Eingänge auslesen ob HIGH oder LOW anliegt
Um die Ports als Eingänge auszulesen müssen diese zunächst auf HIGH gesetzt werden. Macht man dies nicht ist der Ausgangstransistor des Port-Expanders mit GND verbunden und das Ergebnis der Auslesens ist damit auf jeden Fall LOW. Hat man die Ports auf (1) gesetzt sind die Eingänge mit einem sehr hochohmigen PullUp Widerstand mit VCC verbunden. Mit diesem hochohmigen Ausgang kann es zu unklaren Verhältnissen kommen. Ist der Eingang jedoch mit GND verbunden wird eine (0) ausgegeben. Ist der Eingang mit VCC verbunden wird eine (1) zurückgegeben.
#include "Wire.h"
#define adresse 32 // I2C Adresse des PortExpanders
void setup() {
Wire.begin();
Serial.begin(9600);
}
void loop() {
Wire.requestFrom(adresse, 2); // 2 Bytes sollen empfangen werden
byte x = Wire.read(); // In/Out (P07 - P01)
byte y = Wire.read(); // In/Out (P17 - P10)
Serial.println(x, BIN); Serial.print(" ");
Serial.println(y, BIN);
delay(1000);
}
Funktionen zum Auslesen der Eingänge und Pegel-Setzten der Ausgänge
digitalReadPort(Pin)
Die Funktion soll ähnlich wie die Arduino Funktion digitalRead(Pin) funktionieren. Als
Rückgabewert wird true oder false zurückgegeben. Zunächst liest man beide Register des
Port-Expanders aus. Mit return wird dann das äquivalente bit des Eingangs zurückgegeben.
bool digitalReadPort(int pin) {
Wire.requestFrom(adresse, 2);
byte X = Wire.read(); // In/Out (P07 - P01)
byte Y = Wire.read(); // In/Out (P17 - P10)
if (pin >= 0 && pin <= 7) return bitRead(X, pin);
else if (pin >= 10 && pin <= 17) return bitRead(Y, pin - 10);
else Serial.println("Pin nicht vorhanden!");
}
digitalWritePort(Pin, Pegel)
Auch diese Funktion soll ähnlich wie Arduino Funktion digitalWrite(Pin,Pegel)
funktionieren. Zunächst wird unterschieden ob der Pegel auf HIGH oder LOW gelegt werden soll. Über die bit-
Funktionen bitSet() und bitClear() wird der entsprechende Port dann gesetzt und mit
Hilfe von bit-Vergleichen wird dann nur der relevante Eingang verändert. Infos zu den Bitoperationen gibt es unter
Software / Bitoperationen.
void digitalWritePort(int pin, bool pegel) {
if (pegel == HIGH) {
byte maskeL = B00000000;
byte maskeH = B00000000;
// Maskierung vorbereiten (an Pin-Position wird eine 1 geschrieben)
if (pin >= 0 && pin <= 7) bitSet(maskeL, pin);
else if (pin >= 10 && pin <= 17) bitSet(maskeH, pin - 10);
else Serial.println("Pin nicht vorhanden!");
portsL = portsL | maskeL;
portsH = portsH | maskeH;
} else {
byte maskeL = B11111111;
byte maskeH = B11111111;
// Maskierung vorbereiten (an Pin-Position wird eine 0 geschrieben)
if (pin >= 0 && pin <= 7) bitClear(maskeL, pin);
else if (pin >= 10 && pin <= 17) bitClear(maskeH, pin - 10);
else Serial.println("Pin nicht vorhanden!");
portsL = portsL & maskeL;
portsH = portsH & maskeH;
}
Wire.beginTransmission(adresse);
Wire.write(portsL); // In/Out (P07 - P01)
Wire.write(portsH); // In/Out (P17 - P10)
Wire.endTransmission();
}
Vollständiger Programmcode mit Funktionen
#include "Wire.h"
#define adresse 32 // I2C Adresse des PortExpanders
// Portstatus Speicher (Grundzustand alles über den PullUp Widerstand auf HIGH)
byte portsL = B11111111; // In/Out (P07 - P01)
byte portsH = B11111111; // In/Out (P17 - P10)
void setup() {
Wire.begin();
Serial.begin(9600);
}
void loop() {
for (int i = 3; i < 15; i++) {
digitalWritePort(i, LOW);
delay(50);
digitalWritePort(i, HIGH);
}
}
void digitalWritePort(int pin, bool pegel) {
if (pegel == HIGH) {
byte maskeL = B00000000;
byte maskeH = B00000000;
// Maskierung vorbereiten (an Pin-Position wird eine 1 geschrieben)
if (pin >= 0 && pin <= 7) bitSet(maskeL, pin);
else if (pin >= 10 && pin <= 17) bitSet(maskeH, pin - 10);
else Serial.println("Pin nicht vorhanden!");
portsL = portsL | maskeL;
portsH = portsH | maskeH;
} else {
byte maskeL = B11111111;
byte maskeH = B11111111;
// Maskierung vorbereiten (an Pin-Position wird eine 0 geschrieben)
if (pin >= 0 && pin <= 7) bitClear(maskeL, pin);
else if (pin >= 10 && pin <= 17) bitClear(maskeH, pin - 10);
else Serial.println("Pin nicht vorhanden!");
portsL = portsL & maskeL;
portsH = portsH & maskeH;
}
Wire.beginTransmission(adresse);
Wire.write(portsL); // In/Out (P07 - P01)
Wire.write(portsH); // In/Out (P17 - P10)
Wire.endTransmission();
}
bool digitalReadPort(int pin) {
Wire.requestFrom(adresse, 2);
byte X = Wire.read(); // In/Out (P07 - P01)
byte Y = Wire.read(); // In/Out (P17 - P10)
if (pin >= 0 && pin <= 7) return bitRead(X, pin);
else if (pin >= 10 && pin <= 17) return bitRead(Y, pin - 10);
else Serial.println("Pin nicht vorhanden!");
}