đź“— Einsatz des Port-Expanders PCA9555

Grundlagen

Gegenüber dem ESP8266 besitzt der ESP32 erheblich mehr GPIOs. Aber bei einem großen Projekt wie ESPuino können auch diese knapp werden. Hinzu kommen verschiedene ESP32-Limitierungen:

  • GPIO 0 und 2 dĂĽrfen nicht beliebig beschaltet werden, da sie fĂĽr den Boot/Flashvorgang besondere Aufgaben haben.
  • GPIO 34, 35, 36 und 39 sind streng genommen keine GPIOs sondern GPIs. D.h. man kann sie nur als Input-Kanäle verwenden und man hat bei ihnen auch keine Möglichkeit, interne Pullup-Widerstände zuzuschalten.
  • Im Falle eines ESP32-WROVERs hat man zwar PSRAM zur VerfĂĽgung, erkauft sich dies jedoch mit zwei fehlenden GPIOs: 16 und 17.

Nun ist das Thema bei Mikrocontrollern nicht neu und es gibt dafür eine Lösung: Port-Expander. Das sind ICs, die mit dem Bus-Protokoll I2C an den ESP32 angeschlossen werden und ihrerseits dann weitere Ein- und Ausgänge bereitstellen. Im Folgenden geht es ausschließlich um den Port-Expander PCA9555. Dieser stellt zwei Ports bereit, die jeweils aus acht Channels (Pins) bestehen. Diese können unabhängig voneinander als Eingänge oder Ausgänge konfiguriert werden. Hinweis: Ein solcher Port-Expander gehört bei ESPuino gewissermaßen zur Grundausstattung und wird z.B. bei ESPuino-mini 4Layer verwendet.

Eingänge

Der PCA9555 stellt zwei Ports (0 und 1) mit jeweils acht Pins (0 bis 7) zur VerfĂĽgung.
ESPuino-Interna: Per Default sind beide Ports mit allen Channels im „Input-Modus“, so dass man hier per I2C keine zusätzliche Register-Parametrierung vornehmen muss, sofern ein Ausgang nicht notwendig ist. Die Abfrage der Input-Kanäle erfolgt über die Register 0 und 1. Ein Port mit acht Channels wird immer durch 8 Bit repräsentiert.

Ausgänge

Aktuell sind beim ESPuino nur drei Ausgangstypen vorgesehen. Und zwar GPIO_PA_EN, GPIO_HP_EN und Power. D.h. man kann den Port-Expander nutzen, um die Verstärker für Lautsprecher und Kopfhörer zu aktivieren/deaktivieren. Und man kann ihn nutzen, um eine Mosfet-Schaltung anzusteuern.
ESPuino-Interna #1: Nachdem es beim Übergang in den Deepsleep Probleme gab, dass der ESP32 wieder aufgewacht ist, da es auf HP_DETECT durch die Spannungsabschaltung eine Signaländerung gibt (löst einen Interrupt aus), wird auch dieser Eingang kurz vor dem Shutdown als Ausgang umgeschaltet. Grund ist hier aber nur, dass ein Pin, der als Ausgang konfiguriert ist, keinen Interrupt werfen kann und somit der ESP32 fälschlicherweise nicht wieder aufwacht.
ESPuino-Interna #2: Soll ein Pin als Ausgang konfiguriert werden, so muss die Bitmaske im Register 6 bzw. 7 angepasst werden. D.h. per Default steht die Bitmaske eines Ports auf 255; jedes Bit, das gesetzt ist, ist ein Input-Kanal. Will man IO0_1 (siehe Bild) als Ausgang konfigurieren, so muss 253 gesetzt werden, weil das zugehörige Bit auf 0 gesetzt wird. Das Setzen der Ausgänge (high oder low) erfolgt im Anschluss über die Register 2 und 3.

Adresse

Über die Eingänge A0, A1 und A2 steuert man die I2C-Adresse des PCA9555. Beispiel: Legt man alle drei auf GND, so lautet sie 0x20.

Weitere Beschaltung

VDD: 3.3V
VSS: GND
PullUp-Widerstände (4.7 k) für SCL, SDA und INT (für INT reichen auch 10k).

Interrupt

Es gibt verschiedene Möglichkeiten, einen Port-Expander auszulesen. ESPuino tut dies entweder zyklisch oder über einen Interrupt. Der Interrupt hat den Vorteil, dass man hierüber den ESPuino auch aufwecken kann und es zeitsparender ist, als die Input-Register ständig zu pollen. Möchte man den ESPuino via Interrupt aufwecken, so muss WAKEUP_BUTTON auf den ESP32-GPIO konfiguriert werden, an dem der Interrupt-Pin des PCA9555 angeschlossen ist (PE_INTERRUPT_PIN). Hinweis: Das Feuern des Interrupts (und damit das Aufwecken) lässt sich nicht auf einzelne Eingänge des PCA9555 limitieren! D.h. jede Änderung an einem Eingang des PCA9555 resultiert in einem Interrupt, was im Gegenzug den ESP32 aufweckt.

Was kann alles am Port-Expander angeschlossen werden?

Alles, was ein Schalter ist:

  • Eingang: Taster
  • Eingang: Erkennung, ob Kopfhörer eingesteckt
  • Eingang: Taster des Drehencoders (nicht jedoch CLK und DT; dies unterstĂĽtzt die verwendete Lib nicht (Stand 02/2022)
  • Eingang: RFID: PN5180.IRQ
  • Ausgang: Aktivierung/Deaktivierung des Verstärkers fĂĽr den Lautsprecher (GPIO_PA_EN)
  • Ausgang: Aktivierung/Deaktivierung des Verstärkers fĂĽr den Kopfhörer (GPIO_HP_EN)
  • Ausgang: Ansteuerung der Mosfet-Schaltung (POWER)

Können am gleichen I2C-Bus weitere Slaves angeschlossen werden?

Aber natürlich. Dies ist in Form von RFID_READER_TYPE_MFRC522_I2C auch jetzt bereits möglich: Hierbei wird ein RC522 (dafür ist eine spezielle RC522-Platine notwendig, da die Standard-Platine nur SPI unterstützt!) per I2C angebunden. Technisch möglich wäre z.B. auch, dass man ein Display anschließt. Dies wird aktuell von ESPuino jedoch nicht unterstützt. In jedem Fall ist darauf zu achten, dass jeder Slave eine eindeutige Adresse besitzt.

Können am gleichen I2C-Bus mehrere PCA9555 angeschlossen werden?

Technisch ja, aber ESPuino unterstützt dies nicht. 16 zusätzlich Ein- und Ausgänge werden hoffentlich reichen, oder? :joy:

Werden andere Port-Expander-Typen als PCA9555 unterstĂĽtzt?

Nein. Das führt vom Testaufwand einfach zu weit…

Lassen sich Taster auf GPIOs und Port-Expander mischen?

Ja. Für GPIOs stehen weiterhin 0 bis 39 und für den Port-Expander 100 bis 115 bereit. D.h. man kann z.B. NEXT_BUTTON auf 14 legen, während man PREVIOUS_BUTTON auf 103 legt. Und so weiter…

Alle Buttons wecken den ESPuino auf. Kann man das auch einschränken, so dass z.B. nur ein einzelner Button zum Aufwecken benutzt werden kann?

Mit einem Workaround ist dies möglich: Aufwecken nur über Drehencoder.

Wie wird der Port-Expander in ESPuino konfiguriert?

Allgemein:
settings.h:

  1. PORT_EXPANDER_ENABLE aktivieren.
  2. expanderI2cAddress festgelegen. Dies ist die Adresse des PCA9555 am I2C-Bus (siehe weiter oben unter Adresse).

Spezifisch:
settings-<develboard>.h:

  1. ext_IIC_CLK und ext_IIC_DATA festlegen. Dies sind die GPIOs fĂĽr den I2C-Bus (SCL und SDA).
  2. Taster, die am PCA9555 hängen, können nun mit den Zahlen 100 bis 115 angesteuert werden. Hierbei ist: Port 0 / Eingang 0 => 100 … Port 0 / Eingang 7 => 107. Und: Port 1 / Eingang 0 => 108 … Port 1 / Eingang 7 => 115.
  3. (optional) PE_INTERRUPT_PIN festlegen. Dies ist der GPIO, ĂĽber den die Interrupts des PCA9555 empfangen werden. Ich empfehle eindringlich, diesen zu benutzen, da das Pollen auf dem Expander nicht fĂĽrchterlich schnell ist. Und nicht vergessen, dass ein externer PullUp-Widerstand notwendig ist, wenn der GPIO >=34 ist.

Löten:
Gibt es in verschiedenen SMD-Größen. Ich habe meine als TSSOP24 bestellt und zum Testen auf eine entsprechende Adapterplatine gelötet. Bisschen größer ist SOP24. Und wer gar nicht SMD-Löten mag, für den gibt es auch noch DIP24: PCA9555N Product Information|NXP und PCA9555N NXP 16bit I2C/SMBus I/O ports DIP24 NEW [3 pcs] #BP | eBay
Adapterplatinen: https://www.ebay.de/itm/10PCS-SOP24-SSOP24-TSSOP24-1mm-to-DIP24-2-54mm-PCB-Adapter-Converter-Plate/152761240660

2 „Gefällt mir“

Guten Morgen,

das heiĂźt, jeder Taster am Expander wird somit automatisch zum Einschalten des ESPuino genutzt?
Würde der ESPuino ggfs. dann auch beim Einstecken eines Kopfhörers aufwachen?

„Bin Port-Expander unwissend“…

Stefan

Ja, auch beim Kopfhörer würde er aufwachen.

Hi @biologist

ich habe heute mal den Port-Expander ausprobiert . Dazu habe ich aus meinem " Elektronikmuseum"
TTL PCF8574 aus den frühen 80ern , über Levelshifter , eingesetzt . Funktioniert , aber seltsamerweise reagiert die Software nur auf High-aktiven Tastendruck und ich verstehe noch nicht warum . Bei Low-aktiv passiert gar nichts . Zuerst hatte ich die Pullup-Widerstände vergessen , daran liegt es aber nicht . Ich kann mir auch nicht vorstellen das es am PCF8574 liegt , es werden ja Zustände übertragen , sonst ginge es ja mit High nicht . Mache ich etwas falsch , fehlt noch irgendwo ein Parameter ? Vielleicht liegt es auch am Steckboard , hatte aber keine Lust zum Löten .

Ansonsten wieder super umgesetzt und sehr benutzerfreundlich.

VG

Habe dir dazu ne Mail geschrieben mit meinem Testcode. Das kannst mal gegen deinen PCF laufen lassen und anpassen; dann muss man nicht jedes Mal den gesamten Code flashen.

Nachtrag: Es gibt jetzt auch die Möglichkeit, dass der PCA9555 Interrupt-gesteuert ausgelesen wird. Das geschieht via PE_INTERRUPT_PIN. Ich empfehle dringend, davon Gebrauch zu machen, da ich festgestellt habe, dass zyklisches Auslesen zu viel Zeit kostet und sich dies negativ auf die Audioqualität auswirkt.

Hinweis: PE_INTERRUPT_PIN muss natĂĽrlich ein GPIO sein. D.h. ist die konfigurierte Zahl nicht zwischen 0 und 39, dann wird sie ignoriert. Bitte bedenkt, dass ein externer PullUp fĂĽr GPIOs >=34 notwendig ist.

Zur Vollständigkeit sei erwähnt, dass der Port-Expander bisher nur auf folgendem PCB verwendet wird: Lolin D32 pro mit SD_MMC, PN5180, max. fünf Buttons und Port-Expander (SMD).
Ich hatte bisher keinerlei Probleme damit. Der PCB verwendet meine klassische (zweistufige) Mosfet-Schaltung, bei der die Peripherie spannungslos geschaltet wird im Deepsleep. Auf den Port-Expander trifft dies jedoch nicht zu. So kann er, auch wenn der ESP32 im Deepsleep ist, einen Interrupt werfen (beliebiger Tastendruck) und den ESP32 damit aufwecken.

Habe inzwischen mehrfach via AliExpress Port-Expander bestellt. Den Preisanstieg bei Halbleitern merkt man auch hier deutlich. Alles der gleiche Händler bei einer Einheit von 5 Stk; zzgl. Versand:

30.3.21: 1,44$
28.10.21: 2,80$ (+94%)
17.11.21: 2,80$ (unverändert)
03.01.22: 3,43$ (+138%)

Geliefert wurden teilweise Chips von NXP und teilweise noname. Einen Unterschied habe ich jedoch nicht feststellen können.

Hinweis: Vermutlich lässt sich ein Teil des Preisanstiegs auch damit erklären, dass inzwischen auch schon kleine Beträge besteuert werden. Aber das weiß ich im Detail nicht. Ist am Ende ja auch wurscht - es ist auf jeden Fall teurer geworden :slight_smile:.

Ich habe eine Frage zum Einsatz des Port-Expanders. Unter „was kann alles am Port-Expander angeschlossen werden?“ steht oben, dass man den PN5180.IRQ anschließen kann. Kann man auch den BUSY, NSS und RST Pin vom PN5180 am Expander anschließen?

Ich habe aufgrund der Nutzung des T-Display-S3 erheblichen Pin-Mangel. Vielen Dank, wenn mir jemand helfen kann und entschuldigt die womöglich dumme Frage, denn ich bin Anfänger.

Es hat sich leider gezeigt, dass die Nutzung von IRQ am Port-Expander relativ anfällig ist - da wacht der ESP32 gerne mal unnötig auf. Von daher bin ich wieder davon abgekommen, diesen dafür zu verwenden. Das nur als Hinweis.

Der Rest wird aktuell nicht unterstützt und ist auch nicht geplant. Grundsätzlich sollte das machbar sein (denke ich), aber dafür müsstest du Code der PN5180-Library und von ESPuino anpassen.

Zusätzlich kann es sein das Pins timing-kritisch sind, da könnte es also sein das sich (im besten Fall) die Zugriffe deutlich verlangsamt oder sogar gar nichts mehr geht…

Danke für die Rückmeldungen und die Ergänzungen zur Dokumentation.