erste Schritte MCP23017 (Portexpander)

Hallo,

ich habe noch nie etwas mit Interrupts oder dem MCP23017 gemacht und wüsste gerne ob mein Plan grundsätzlich funktioniert.

Ich würde gerne drei MCP23017 mit Eingängen möglichst stromsparend verwenden und deswegen Interrupts benutzen.
Kann ich alle 6 Interruptleitungen der drei 23017-Chips mit einem Pullup parallel schalten und das auf einen Eingang am uC legen?
Eine fallende Flanke müsste dann einen Interrupt auslösen und der uC fragt dann nacheinander alle 23017 ab, um zu sehen, was sich getan hat.

Besitzt der 23017 einen Puffer, oder wird immer nur der bei der Abfrage aktuelle Zustand eines Eingangspins ausgegeben?

Was passiert, wenn etwas am Eingang des 23017 prellt und die Zustandsänderungen schneller stattfinden, als man sie auslesen kann? Reicht es, wenn man dann nur den letzten Interrupt bearbeitet?

Schonmal das Datenblatt durchgelesen? Da sollte alles drinstehen, es müssen entsprechende Register gesetzt werden. Ein zusammenlegen ALLER Interruptleitungen ist schwachsinn. Du kannst das IC so einstellen, dass immer zwei Ports (1 MCP) zusammen eine Interruptsleitung braucht.

erste Schritte MCP23017

Die ersten Schritte?

Der erste Schritt sollte das lesen des Datenblattes des Bausteins sein, welchen du verwenden möchtest.

Besitzt der 23017 einen Puffer, oder wird immer nur der bei der Abfrage aktuelle Zustand eines Eingangspins ausgegeben?

Falsche Frage!
Du willst wissen, welcher Pin den Interrupt ausgelöst hat.
Und ja, das Datenblatt sagt: Der Baustein merkt sich die Interruptquelle, und du kannst sie erfragen.

Kann ich alle 6 Interruptleitungen der drei 23017-Chips mit einem Pullup parallel schalten und das auf einen Eingang am uC legen?

Auch hierzu ist das Datenblatt sehr auskunftsfreudig!

  1. du kannst die INTa und INTb intern verodern, brauchst also nur 3, nicht 6 Pins
  2. die INT Pins können als open-drain konfiguriert werden. Sind also veroderbar

Was passiert, wenn etwas am Eingang des 23017 prellt und die Zustandsänderungen schneller stattfinden, als man sie auslesen kann? Reicht es, wenn man dann nur den letzten Interrupt bearbeitet?

Da ich keine Ahnung habe, was das für deine geheime Applikation bedeuten mag, überlasse ich dir das Problem

sschultewolter:
Schonmal das Datenblatt durchgelesen? Da sollte alles drinstehen, es müssen entsprechende Register gesetzt werden. Ein zusammenlegen ALLER Interruptleitungen ist schwachsinn. Du kannst das IC so einstellen, dass immer zwei Ports (1 MCP) zusammen eine Interruptsleitung braucht.

Ich hatte versucht das Datenblatt zu lesen, habe aber zu wenig verstanden. Über Google habe ich einige Beiträge in Foren gefunden, die mich weitergebracht haben, aber die hier gestellten Fragen, haben sie mir nicht beantwortet.

Was spricht dagegen, alle Interruptleitungen aller Chips zu verbinden? Ansonsten müsste ich mehr Leitungen verlegen und mehr IO-Ports am uC belegen.
Ich habe es mir so vorgestellt, dass bei einer Zustandsänderung an irgendeinem Eingangspin ein Interrupt ausgelöst wird und der uC dann nacheinander alle 23017 abfragt, wie der aktuelle Zustand ist.

Ich hatte vergessen zu erwähnen, wofür es gedacht ist. Es sollen Standard-Lichtschalter/Taster abgefragt werden. Es kommt dabei nicht auf hohe Geschwindigkeit an. Doppelklicks und kurze Klicks (50 ms ?) sollen aber erkannt werden können.

Was spricht dagegen, alle Interruptleitungen aller Chips zu verbinden?

Wie das geht, habe ich dir schon gesagt.

Und ob das Sinn macht?

Taster abfragen und Interrupts ist (fast) ein Widerspruch.
Denn:
Taster prellen.
Das gibt jeweils eine Flut von Interrupts.

Ich würde die einzelnen Ports auch per polling abfragen, da kannst du in deinem Code auch prima "entprellen" und weiter auswerten.

Ein Interrupt ist nur dann wichtig wenn du tatsächlich mehrere Taster gleichzeitig oder direkt hinter einander bedienen musst oder eine länger dauernde Funktion wird noch abgearbeitet und muss unterbrochen werden.

combie:
Taster abfragen und Interrupts ist (fast) ein Widerspruch.
Denn:
Taster prellen.
Das gibt jeweils eine Flut von Interrupts.

Kann man die Interrupts nicht sperren, sobald einer aufgetreten ist? Dann sollte nur die erste Schaltflanke ausgewertet werden und alle weiteren, die durch das Prellen verursacht werden, würden verworfen werden.
Wenn man sicher sein kann, dass keine keine Interrupts mehr durch das Prellen ausgelöst werden können, schaltet man die Interruptleitung wieder frei und pollt noch einmal, um etwaige Zustandsänderungen, die in der Zwischenzeit aufgetreten sein können, nicht zu verlieren.

HotSystems:
Ich würde die einzelnen Ports auch per polling abfragen, da kannst du in deinem Code auch prima "entprellen" und weiter auswerten.

Ein Interrupt ist nur dann wichtig wenn du tatsächlich mehrere Taster gleichzeitig oder direkt hinter einander bedienen musst oder eine länger dauernde Funktion wird noch abgearbeitet und muss unterbrochen werden.

Das wäre zumindest die leichtere Variante, aber da die Tasten vergleichsweise selten betätigt werden und auch kurze Klicks hintereinander abgefragt werden sollen, wäre der uC permanent beschäftigt. Mit der Interruptvariante könnte er die allermeiste Zeit im Stromsparmodus bleiben.

Bleiente:
Das wäre zumindest die leichtere Variante, aber da die Tasten vergleichsweise selten betätigt werden und auch kurze Klicks hintereinander abgefragt werden sollen, wäre der uC permanent beschäftigt. Mit der Interruptvariante könnte er die allermeiste Zeit im Stromsparmodus bleiben.

Der Stromsparmodus ist allerdings ein Argument, einen Interrupt zu nutzen.
Ich habe da allerdings noch nicht genügend Erfahrung mit und kann dir auch nichts über das Sperren sagen.

Kann man die Interrupts nicht sperren, sobald einer aufgetreten ist? Dann sollte nur die erste Schaltflanke ausgewertet werden und alle weiteren, die durch das Prellen verursacht werden, würden verworfen werden.

Das erste Problem ist dass Taster mehrere Millisekunden lang Prellen, was ein vielfaches deiner ISR Dauer ist.

Man könnte aber vielleicht schon in der ISR detachInterrupt() machen und erst wieder attachInterrupt() wenn eine gewisse Zeit vorbei ist. Solange man die Zeit irgendwie außerhalb der ISR zählt.

Du kannst die Interruptausgänge des MCP auch per Polling ausfragen und bei LOW das register abfragen um draufzukommen welcher Port den interrupt ausgelöst hat.

Keine Angst, Du kannst einen Schalter nicht innerhalb 50mS drücken und wieder loslassen. Arduino kontrolliert die Schalter in den 50mS bereits mehrfach.

Wie gesagt mußt Du den Interruptausgang als Open Collektor setzen da ansonsten paralellgeschaltete Ausgänge kaputtgehen. Du wirst das Datenblatt gut studieren müssen und verstehen welche Register/Bit abgefragt bzw gesetzt werden müssen.

Grüße Uwe

Serenifly:
Das erste Problem ist dass Taster mehrere Millisekunden lang Prellen, was ein vielfaches deiner ISR Dauer ist.

Man könnte aber vielleicht schon in der ISR detachInterrupt() machen und erst wieder attachInterrupt() wenn eine gewisse Zeit vorbei ist. Solange man die Zeit irgendwie außerhalb der ISR zählt.

Die Befehle zum aktivieren und deaktivieren der Interrupts kannte ich nicht, aber grundsätzlich war das mein Plan. Ich wollte die Interrupts nur zum Aufwecken des uCs nehmen, den Interrupteingang deaktivieren, zum Pollen übergehen, und wenn ich sicher sein kann, dass kein Tastenprellen mehr auftritt, würde ich den Interrupt wieder aktivieren und den uC in den Sleepmodus schicken.
Die ISR deaktiviert dann nur die Interrupts. Der Rest wird vom Hauptprogramm erledigt.

uwefed:
Du kannst die Interruptausgänge des MCP auch per Polling ausfragen und bei LOW das register abfragen um draufzukommen welcher Port den interrupt ausgelöst hat.

Wäre es denn ein nennenswerter Vorteil, wenn ich die Interruptausgänge pollen würde, statt direkt die IO-Register abzufragen?
Ich müsste dann doch trotzdem immer wieder den uC aus dem Sleepmodus holen, und das möchte ich vermeiden.

Keine Angst, Du kannst einen Schalter nicht innerhalb 50mS drücken und wieder loslassen. Arduino kontrolliert die Schalter in den 50mS bereits mehrfach.

Wie gesagt mußt Du den Interruptausgang als Open Collektor setzen da ansonsten paralellgeschaltete Ausgänge kaputtgehen. Du wirst das Datenblatt gut studieren müssen und verstehen welche Register/Bit abgefragt bzw gesetzt werden müssen.

Ja, Danke für den Hinweis. Open-Collektor war mir klar. Ich hatte mich bis gerade nur noch nicht weiter darum gekümmert, weil ich erst mal abklären wollte, ob meine Idee mit dem Interrupt grundsätzlich funktioniert.
Ich habe gerade im Datenblatt gesehen, dass das Kontrollregister IOCON dafür zuständig ist. Demnach muss Bit "ODR" auf 1 gesetzt werden. Nach einem Reset hat es eine 0.
Das ist für mich auch wichtig, weil ich die Bausteine über den uC resetten können möchte. Zu I2C habe ich gelesen, dass es sich auch mal weghängen kann und sich selbstständig nicht mehr fängt. Ich möchte also immer wieder mal die Ports abfragen und dadurch testen, ob I2C noch läuft und sinnvolle Werte ankommen. Wenn nicht, sollen die angeschlossenen Bausteine resettet und initialisiert werden.

Vielleicht sollte ich noch an jeden Interrupt-Ausgang 150 Ohm in Reihe hängen. Das sollte reichen um den Eingang auf GND zu ziehen und den Strom bei "Programmierfehlern" in Grenzen halten.