Arduino Mikrocontroller Tutorials - Module

I2C Port-Expander (Bsp. PCF8575)

Bild I2C Port Expander 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!"); }