Seite 1 von 2
I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 14:08
von Achim Klein
Hallo
versuche mit einem Raspberry Pi Pico einen MCP23017 zu betreiben. Das schalten von LEDs am Port funktioniert bereits super. Leider habe ich Probleme den Zustand von Tastern auszulesen. Verwende diesen Code dazu:
Code: Alles auswählen
from machine import Pin,I2C
import utime
# Initialisierung I2C, Bus 0, sda-0, scl-1, Adresse 0x22, Frequenz 400kHz
SCL_Pin = 1 # Angabe Pin SCL
SDA_Pin = 0 # Angabe Pin SDA
Bus = 0 # Angabe Bus Nr
MCP_Address = 0x22 # MCP23017 Angabe der Bus Adresse - 0x22, sonst ermitteln
i2c = I2C(Bus, scl = Pin(SCL_Pin), sda = Pin(SDA_Pin), freq = 400000)
MCP_GPIOA = 0x12 # Spiegelt den Wert am Anschluss A wider.
MCP_GPIOB = 0x13 # Spiegelt den Wert am Anschluss B wider.
MCP_OLATA = 0x14 # Schalte Ausgänge Port A.
MCP_OLATB = 0x15 # Schalte Ausgänge Port B.
MCP_IODIRA = 0x00 # Steuert die Richtung der Daten-E/A für Anschluss A.
MCP_IODIRB = 0x01 # Steuert die Richtung der Daten-E/A für Anschluss B.
MCP_IPOLA = 0x02 # Konfiguriert die Polarität der entsprechenden GPIO-Port-Bits für Port A.
MCP_IPOLB = 0x03 # Konfiguriert die Polarität der entsprechenden GPIO-Port-Bits für Port B.
MCP_GPINTENA = 0x04 # Steuert den Interrupt-on-change für jeden Pin von Anschluss A.
MCP_GPINTENB = 0x05 # Steuert den Interrupt-on-change für jeden Pin von Anschluss B.
MCP_DEFVALA = 0x06 # Steuert den Standard-Vergleichswert für Interrupt-on-Change für Anschluss A.
MCP_DEFVALB = 0x07 # Steuert den Standard-Vergleichswert für Interrupt-on-Change für Anschluss B.
MCP_INTCONA = 0x08 # Steuert, wie der zugehörige Pin-Wert für den Interrupt-on-change für Anschluss A verglichen wird.
MCP_INTCONB = 0x09 # Steuert, wie der zugehörige Pin-Wert für den Interrupt-on-change für Anschluss B verglichen wird.
MCP_IOCON = 0x0A # Steuert das Gerät
MCP_GPPUA = 0x0C # Schaltet Pull-up-Widerstände für Port A auf 5V
MCP_GPPUB = 0x0D # Schaltet Pull-up-Widerstände für Port B auf 5V
MCP_INTFA = 0x0E # Spiegelt den Unterbrechungszustand an den Pins von Anschluss A wider
MCP_INTFB = 0x0F # Spiegelt den Unterbrechungszustand an den Pins des Anschlusses B wider
MCP_INTCAPA = 0x10 # Erfasst den Wert von Anschluss A zum Zeitpunkt des Auftretens der Unterbrechung
MCP_INTCAPB = 0x11 # Erfasst den Wert des Anschlusses B zum Zeitpunkt des Auftretens der Unterbrechung
# Achtung Angabe der Pins in Hex
confA = [MCP_IODIRA, 0x00] # Steuert die Richtung der Daten am Port A, 0 Ausgang, 1 Eingang
confB = [MCP_IODIRB, 0xff] # Steuert die Richtung der Daten am Port B, 0 Ausgang, 1 Eingang
confC = [MCP_GPPUB, 0xff] # Schaltet Pull-up-Widerstände für Port B auf 5V
confD = [MCP_GPIOB, 0xff] # Spiegelt den Wert am Anschluss B wider
buff1 = [MCP_OLATA, 0x50] # Angabe Zahl, Pins an nach Tabelle
buff0 = [MCP_OLATA, 0x00] # Angabe 0, alle Pins aus
buff2 = [MCP_OLATA, 0x05] # Angabe Zahl, Pins an nach Tabelle
buff3 = [MCP_OLATA, 0x03] # Angabe Zahl, Pins an nach Tabelle
i2c.writeto(MCP_Address, bytearray(confA)) # Port A als Ausgang
i2c.writeto(MCP_Address, bytearray(confB)) # Port B als Eingang
i2c.writeto(MCP_Address, bytearray(confC)) # Schaltet Pull-up-Widerstände für Port B auf 5V
i2c.writeto(MCP_Address, bytearray(confD)) # Spiegelt den Wert am Anschluss B wider.
while True: # Beginn der Schleife
abt = i2c.readfrom(MCP_Address ,8)
print(abt) # Anzeige zur Kontrolle
print()
if (abt == 8):
i2c.writeto(MCP_Address, bytearray(buff2)) # schreibt an die Adresse buff 1 - 1 2
utime.sleep(0.5) # Pause
else:
i2c.writeto(MCP_Address, bytearray(buff3)) # schreibt an die Adresse buff 0 - 0 0
utime.sleep(0.5) # Pause
Im gesamten Code verwende ich INT nicht. Die Register habe ich hier zum Beginn des Programmes für eine bessere übersicht drin.
Das auslesen des gedrückten Taster und zur Kontrolle das schalten einer LED funktioniert nicht. Alle Pins des Ports A liegen auf 5V und haben beim betätigen der Taster GND. Taster schalten nach GND.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 16:02
von __deets__
Du hast schon so nette Konstanten. Aber benutzt die nicht. Wenn du das getan haettest, waere dir vielleicht aufgefallen, das du von Port A liest, obwohl du den zum Ausgang konfiguriert hast. Und nicht von Port B.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 17:27
von Achim Klein
Bist du dir sicher mit deiner Antwort? Habe das so eingestellt:
Code: Alles auswählen
confA = [MCP_IODIRA, 0x00] # Steuert die Richtung der Daten am Port A, 0 Ausgang, 1 Eingang
confB = [MCP_IODIRB, 0xff] # Steuert die Richtung der Daten am Port B, 0 Ausgang, 1 Eingang
confC = [MCP_GPPUB, 0xff] # Schaltet Pull-up-Widerstände für Port B auf 5V
confD = [MCP_GPIOB, 0xff] # Spiegelt den Wert am Anschluss B wider
Mit A setze ich Port A auf Ausgang, mit B setze ich B auf Eingang. Mit C und D schalte ich die 5V und spiegel das Ergebniss.
Code: Alles auswählen
i2c.writeto(MCP_Address, bytearray(confA)) # Port A als Ausgang
i2c.writeto(MCP_Address, bytearray(confB)) # Port B als Eingang
i2c.writeto(MCP_Address, bytearray(confC)) # Schaltet Pull-up-Widerstände für Port B auf 5V
i2c.writeto(MCP_Address, bytearray(confD)) # Spiegelt den Wert am Anschluss B wider.
Mit dieser Angabe rufe ich die Register einmalig auf.
Habe zur Kontrolle einiges da von ausgeschaltet, es wird alles so gemacht wie es angegeben ist.
Damit führe ich einen Vergleich aus. Die LEDs danach habe ich zur Kontrolle drin. Der Wechselblinker funktioniert.
Code: Alles auswählen
while True: # Beginn der Schleife
abt = i2c.readfrom(MCP_Address ,8)
print(abt) # Anzeige zur Kontrolle
print()
if (abt == 0):
i2c.writeto(MCP_Address, bytearray(buff2)) # schreibt an die Adresse buff 1 - 1 2
utime.sleep(0.5) # Pause
else:
i2c.writeto(MCP_Address, bytearray(buff3)) # schreibt an die Adresse buff 0 - 0 0
utime.sleep(0.5) # Pause
# Kontrolle mit LED
i2c.writeto(MCP_Address, bytearray(buff1)) # schreibt an die Adresse buff 1 - 1
utime.sleep(0.5) # Pause
i2c.writeto(MCP_Address, bytearray(buff0)) # schreibt an die Adresse buff 0 - 0
utime.sleep(0.5) # Pause
Es funktioniert das auslesen bzw der Vergleich des Tasters nicht.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 18:03
von __deets__
Ich weiss nicht, was es jetzt nochmal bringen soll, deinen Code zu paraphrasieren. Hast du mal probiert nachzuvollziehen, worueber ich rede?
Adresse 8. Laut deinem eigenen Code
bezieht sich das auf Port A. Den du als Ausgabeport setzt, siehe confA. Einlesen willst du aber von Port B, siehe confB. Und darauf bezog sich meine Aussage. Wenn du von B lesen willst, warum benutzt du ein Register von A?
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 18:07
von __deets__
Nachtrag: wenn ich das Datenblatt richtig verstehe (das ich wirklich nur grob ueberflogen habe), dann willst du Register MCP_GPIOB lesen. Aber bleibt bei meinem Argument. Du benutzt nicht das richtige Register.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 19:30
von Achim Klein
Irgendwie reden wir aneinander vorbei.
Code: Alles auswählen
MCP_IODIRA = 0x00 # Steuert die Richtung der Daten-E/A für Anschluss A.
MCP_IODIRB = 0x01 # Steuert die Richtung der Daten-E/A für Anschluss B.
Das ist das Register 0x00/01 mit Namen und steuert die Richtung der Daten
Code: Alles auswählen
MCP_INTCONA = 0x08 # Steuert, wie der zugehörige Pin-Wert für den Interrupt-on-change für Anschluss A verglichen wird.
MCP_INTCONB = 0x09 # Steuert, wie der zugehörige Pin-Wert für den Interrupt-on-change für Anschluss B
Name Register, Zur Interrupt Steuerung, verwende ich nicht
Code: Alles auswählen
confA = [MCP_IODIRA, 0x00] # Steuert die Richtung der Daten am Port A, 0 Ausgang, 1 Eingang
confB = [MCP_IODIRB, 0xff] # Steuert die Richtung der Daten am Port B, 0 Ausgang, 1 Eingang
confC = [MCP_GPPUB, 0xff] # Schaltet Pull-up-Widerstände für Port B auf 5V
confD = [MCP_GPIOB, 0xff] # Spiegelt den Wert am Anschluss B wider
Das MCP_IODORA (0x00) ist das Register für Port A das die Datenrichtung bestimmt. Mit der Hex werden die Pins der I/O festgelegt. Damit man unterschiedliche I/O, halt gemischter Betrieb. Damit ist confA das Register 0x00 mit dem Wert 0x00 und schaltet den Port A von A0 bis A7 auf Ausgang.
Mit MCP_confB schalte ich das Register B (0x01) mit dem Wert alle auf 1 (0xff für alle) auf Eingang.
Habe es mal so getestet. Die Daten stehen im bytearray data drin. An der 21 Stelle kann ich sehen wie das Pin schaltet (wackelt).
Code: Alles auswählen
i2c.writeto(MCP_Address, bytearray(confA)) # Port A als Ausgang
i2c.writeto(MCP_Address, bytearray(confB)) # Port B als Eingang
i2c.writeto(MCP_Address, bytearray(confC)) # Schaltet Pull-up-Widerstände für Port B auf 5V
i2c.writeto(MCP_Address, bytearray(confD)) # Spiegelt den Wert am Anschluss B wider.
Damit schalte ich z.B. die Busadresse 0x22, das Register 0x00 auf den Wert 0x00.
Im Slave 0x22 wird das Register 0x00 auf 0x00 geschaltet.
Habe es getestet, wenn ich dort 0xff eingebe schaltet der Ausgang nicht mehr.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 19:52
von __deets__
Und was hat das jetzt mit was zu tun? Du schreibst doch selbsts
Code: Alles auswählen
i2c.writeto(MCP_Address, bytearray(confA)) # Port A als Ausgang
i2c.writeto(MCP_Address, bytearray(confB)) # Port B als Eingang
Heißt das nun du willst von Port B lesen? Ja oder Nein? Und wo in deinem Code steht jetzt deiner Meinung nach die Abfrage der Bits von PortB?
Das readfrom /writeto ist wahrscheinlich auch eher falsch. Weil für die Register-Adressierung (lesend) eigentlich erst ein Write zum Register erfolgen muss. Und dann ein read des Wertes. Du ballerst aber einfach arrays raus, und liest welche ein, die dann aber so behandelt werden, als ob sie ein Registerwert wären.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 20:31
von Achim Klein
Leider scheint Python ein bischen anders zu sein.
Mit dem erst schreiben hast du vollkommen Recht. Bei Atmega hab ich das so genacht und es funktioniert alles. Kenn es auch so, das auf die Adresse 0x22 geschrieben wird und auf 0x22+1 gelesen wird. Wenn man im Netz sucht findet man Angaben und kurze Hinweise wie man das macht oder machen sollte. Zu diesem erst schreiben und dann lesen steht nichts. Den PCF8574 habe ich die Abfrage ohne Problem geschafft. Ist aber ein einfacher IC. Der MCP ist da schon etwas anderes. Das mit dem erst schreiben und dann lesen werde ich testen, Danke für deinen Hinweis.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 20:36
von __deets__
Es gibt dafür schon Methoden. Musst mal die Dokumentation von micropython lesen. Da gibts welche, die nehmen Geräteadresse und Register, und lesen oder schreiben das.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Dienstag 21. Februar 2023, 23:45
von __deets__
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Mittwoch 22. Februar 2023, 09:14
von Achim Klein
Morgen
genau diese meine ich, bin danach gegangen. Im Netz und den Fachbüchern findet man ja auch Beispiel zum Code. Meist aber nur wie man eine LED schaltet. Abfrage eines Taster habe ich bisher nicht gefunden.
Wenn ich das genauso mache wie beim AVR habe ich ein bischen Angst um den Pico. So einfach den Ausgang auf H schalten und den Taster auf GND - da könnte doch der Aush^gang sich das leben nehmen?
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Mittwoch 22. Februar 2023, 09:50
von __deets__
Wieso sollte das nicht gehen? Was am Pico ist anders als am AVR, und wieso hast du keine Angst um deine I2C Pins am Pico? Die werden doch genauso belastet? Ist I2C irgendwie magisch besser?
Ich finde jede Menge Beispiele zum einlesen von Daten, zb
https://tronixstuff.com/2011/08/26/ardu ... -tutorial/ etc PP. Wichtig auch hier die korrekte Ansprache der Register. I2C ist nicht trivial.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Mittwoch 22. Februar 2023, 10:52
von Achim Klein
Habe mir den Vergleich zum PCF8574 angesehen, ist doch unterschiedlich:
Code: Alles auswählen
if (PCF_Adresse_2.pin(1) == 0):
PCF_Adresse_1.pin(4, 1) # LED an 0
PCF_Adresse_1.pin(5, 0) # LED an 0
lcd.move_to(6,2) # Gehe zu Spalte/Zeile
lcd.putstr('gedr\365ckt ') # Angabe Text \365 Sonderzeichen für ü
else:
PCF_Adresse_1.pin(4, 0) # LED aus 1
PCF_Adresse_1.pin(5, 1) # LED an 0
lcd.move_to(6,2) # Gehe zu Spalte/Zeile
lcd.putstr('nicht gedr\365ckt') # Angabe Text \365 Sonderzeichen für ü
Das Programm verwendet die Lib pcf8574.py von causer. Damit ist es relativ leicht 3 verschiedne Slave am Bus zu betreiben. Es braucht ja auch keine Registereinstellungen. Werde als nächste mal die Einbindung der Lib vom mcp23017.py angehen. Da beide Libs vom gleichen Autor stammen müsste es gehen.
Habe mir deine Angabe zum MCP angesehen. Es wird c/c++ verwendet vom Arduino. Ist wieder etwas anders. Der Weg ist so ziemlich alles das selbe.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Mittwoch 22. Februar 2023, 11:09
von __deets__
Das der Code in C++ ist, ist doch irrelevant. Du siehst die genutzten I2C-Instruktionen, und dafuer sollte die Abstraktion doch hoffentlich reichen. Also musst du die dann nur abbilden in deinem Code.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Donnerstag 23. Februar 2023, 18:27
von Achim Klein
Habe nach der Doku von Python und der Hinweise im Netz so ziemlich alles ausprobiert was möglich. Siehe da, es funktioniert was:
Code: Alles auswählen
while True: # Beginn der Schleife
data=i2c.readfrom_mem(MCP_Address, MCP_GPIOB, 1)
print(data) # Anzeige zur Kontrolle am PC
if data == b'\xfe':
i2c.writeto(MCP_Address, bytearray(buff2)) # schreibt an die Adresse buff 1 - 1 ,2
utime.sleep(0.5) # Pause
else:
i2c.writeto(MCP_Address, bytearray(buff3)) # schreibt an die Adresse buff 0 - 0 ,3
utime.sleep(0.5) # Pause
Damit erfolgt die Auswertung des Tasters an B0. Der Rückgabe Wert von MCP_GPIO ist b'\xff. Wenn ich den Taster B0 betätige erscheint b'\xfe und die if wird ausgeführt.
Jetzt habe ich das Problem, was ist b'\xfe ???
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Donnerstag 23. Februar 2023, 18:36
von __deets__
Da du high pullst, 8 mal, hast du 0xff. Dann drueckst du deinen Taster, und du toggelst ein Bit, augenscheinlich Bit 0. Also hast du 0xfe, oder binaer 0b11111110. So wie es bei so Bitfummelei eben ist. Wenn du nur auf ein Bit testen willst, musst du eben die Verknuepfung entsprechend machen. Zb
Code: Alles auswählen
position = 0 # oder eben 1-7
if (data ^ 0xff) & (1 << position): # erst Bits invertieren, dann auf gesetztes Bit pruefen.
tuwas()
Aber da gibt' natuerlich auch beliebig viele andere Moeglichkeiten, zB
Code: Alles auswählen
if not (data & (1 << position)): # auf geloeschtes Bit pruefen
wobei ich so Negationen vermeide.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Donnerstag 23. Februar 2023, 19:03
von Achim Klein
Bist du dir sicher das der Code funktioniert. Habe ihn (hoffentlich) richtig eingegeben:
Code: Alles auswählen
data=i2c.readfrom_mem(MCP_Address, MCP_GPIOB, 1)
print(data) # Anzeige zur Kontrolle am PC
position = 0 # oder eben 1-7
if (data ^ 0xff) & (1 << position): # erst Bits invertieren, dann auf gesetztes Bit pruefen.
i2c.writeto(MCP_Address, bytearray(buff2)) # schreibt an die Adresse buff 1 - 1 ,2
utime.sleep(0.5) # Pause
else:
i2c.writeto(MCP_Address, bytearray(buff3)) # schreibt an die Adresse buff 0 - 0 ,3
utime.sleep(0.5) # Pause
damit bekomme ich diese Fehlermeldung:
Traceback (most recent call last):
File "<stdin>", line 102, in <module>
TypeError: unsupported types for __xor__: 'bytes', 'int'
Meine damit das ganze: b'\xff , nicht bloss xff'
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Donnerstag 23. Februar 2023, 19:10
von Sirius3
Natürlich mußt Du das Byte in eine Zahl umwandeln, um damit rechnen zu können:
Code: Alles auswählen
if (data[0] ^ 0xff) & 1:
i2c.writeto(MCP_Address, bytearray(buff2)) # schreibt an die Adresse buff 1 - 1 ,2
else:
i2c.writeto(MCP_Address, bytearray(buff3)) # schreibt an die Adresse buff 0 - 0 ,3
utime.sleep(0.5)
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Donnerstag 23. Februar 2023, 19:39
von Achim Klein
Als erstes die gute Nachricht, es funktioniert. In dieser Zeile liegt demnach die Auswertung des Rückgabewertes:
Der gewünschte Taster liegt in der "4". Bei einer 1 geht der Taster 1, bei einer 2 der Taster 2, bei einer 3 der Taster 1 und 2, bei einer 4 der Taster 3. usw Soweit klar.
Könntest du mir auch erklären was du da (genau) machst? Sorry ist mir noch zu hoch.
Re: I2C Bus mit MCP23017 am Pico
Verfasst: Donnerstag 23. Februar 2023, 19:47
von __deets__
^ ist der xor operator. Ein Byte xored mit FF invertiert alle Bits. Und damit kannst du dann mit & auf ein Bit Testen.