CircuitPython, Adafruit Feather RP2040, und I2C

Wie der Titel schon vermuten lässt, geht es in diesem Artikel um CircuitPython, den Adafruit Feather RP2040 und das I2C-Kommunikationsprotokoll.

Der RP2040 hat zwei I2C-Controller - gut, wenn man z.B. zwei I2C-Geräte mit der gleichen I2C-Adresse betreiben will.

In meinem Testaufbau habe ich ein Adafruit Feather RP2040 Mikrocontroller-Board und zwei unserer BME688-Breakout-Karten - eine mit den Stiften SCL + SDA und eine mit A1 (für SCL) + A0 (für SDA).

Ich verwende CircuitPython in der Version 7.0.0, die Sie hier herunterladen können.

Außerdem habe ich alle Bibliotheken von Adafruit in der lib Ordner auf dem Feather RP2040. (Der Feather RP2040 verfügt über genügend Speicherplatz auf seinem Flash, um dies zu ermöglichen)

Sie können diese Bibliotheken in der Adafruit CircuitPython Bundle hier. (Ich habe heruntergeladen adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

HinweisUm diese Bibliotheken zu installieren, kopieren Sie sie einfach in den Ordner "lib" auf dem "Laufwerk" von CIRCUITPY, das sich auf Ihrem Computer befindet. Sie müssen natürlich nur die Bibliotheken kopieren, nicht die Beispiele und andere Dinge.

Der große Vorteil bei der Verwendung von Adafruits Material ist, dass Sie eine Tonne von Beispielen, die viele beliebte Chips abdecken, und Sie können sehr leicht mit ihm beginnen. Es gibt Dinge wie das Ansteuern einer microSD-Karte über SPI, das Auslesen einer RTC und das Auslesen des BME680-Sensors.

I2C in CircuitPython testen

Ich habe die folgende Konfiguration, da ich zwei Geräte unabhängig voneinander ansteuern möchte (die in diesem Fall zufällig die gleichen Adressen haben):

Feather RP2040 und zwei BME688-Breakout-Karten

Beachten Sie, dass unsere BME688-Breakout-Boards bereits Pullups für SDA und SCL enthalten. (Sie benötigen Pullups an SDA und SCL).

Hinweis 2: Unser BME688-Breakout-Board hat die Möglichkeit, die Adresse zu ändern, daher ist dieses Szenario für Demonstrationszwecke gedacht.

Um beide Pinsätze sequentiell zu durchlaufen (um die Geräte zu erkennen), verwende ich den folgenden Code:

print("Scanning SCL / SDA")
i2c = busio.I2C(board.SCL, board.SDA)
# a scan
i2c.try_lock()
print(i2c.scan())
i2c.unlock()
i2c.deinit()

print("Scanning A0 / A1")
si2c = busio.I2C(board.A1, board.A0)
# a scan
si2c.try_lock()
print(si2c.scan())
si2c.unlock()

Anmerkung: Die i2c.deinit() ist der Schlüssel zum Funktionieren dieses speziellen Beispiels! (weil SCL / SDA und A0 / A1 beide die gleiche Hardware-I2C-Peripherie haben - siehe unten).

Dies sollte die folgende Ausgabe ergeben:

Scanning SCL / SDA
[119]
Scanning A0 / A1
[119]

Hier ist 119 dezimal für hex 0x77 - was die Adresse unseres BME688-Breakout-Boards im Standardzustand ist.

Beide Tafeln sind nacheinander in den einzelnen Scans zu sehen.

Das Problem ist, dass wir sie gleichzeitig nutzen wollen.

Zwei I2C-Busse gleichzeitig auf dem Adafruit Feather RP2040 betreiben

CircuitPython unterstützt beide Hardware-Controller (SDA0/SCL0 und SDA1/SCL1). Sie müssen keine Konfiguration vornehmen (welcher Controller verwendet werden soll oder wie die Pins gemischt werden sollen) - dies wird von busio für Sie.

Sie müssen jedoch darauf achten, welche Stifte Sie verwenden, da die Stifte nur eine eine dieser Busse in jedem Fall, und wenn Sie zufällig widersprüchliche Pins wählen, erhalten Sie WertFehler: I2C-Peripheriegerät in Gebrauch .

Wenn Sie "kollidierende" Pins verwenden wollen, z.B. SCL / SDA (die SCL1 und SDA1 haben) und A0 / A1 (die auch SCL1 und SDA1 haben), müssen Sie einen der Ports bitbangen:

So scannen Sie diese Pin-Konfiguration, ohne deinit() aufzurufen:

import board
import busio
import bitbangio
# https://circuitpython.readthedocs.io/en/latest/shared-bindings/bitbangio/index.html

print("Scanning SCL / SDA - main I2C")
i2c = busio.I2C(board.SCL, board.SDA)
# a scan
i2c.try_lock()
print(i2c.scan())
i2c.unlock()
# no need to call deinit here!
#i2c.deinit()

print("Scanning A0 / A1 - secondary I2C [bitbang!]")
si2c = bitbangio.I2C(board.A1, board.A0)
# a scan
si2c.try_lock()
print(si2c.scan())
si2c.unlock()

# we also do not need to call deinit here
#i2c.deinit()

Wir verwenden bitbangio zur Ansteuerung eines sekundären I2C. In meinem Fall wird das sekundäre I2C für einen internen Zweck verwendet (für einen Port-Expander) und kann wahrscheinlich mit einer niedrigeren Schnittstellengeschwindigkeit auskommen.

Hinweis: Sie können die I2C-Peripherie, die auf die Pins geroutet wird, nicht in der Software auswählen - wenn Sie eine andere Peripherie benötigen, müssen Sie andere Pins verwenden.

Fehlerbehebung bei I2C-Fehlern

WertFehler: I2C-Peripheriegerät in Gebrauch

Wenn Sie busio.I2C für beide Ports verwenden: Prüfen Sie, ob Sie bereits die gleiche Hardware-I2C-Peripherie verwenden - und ob Sie Ihre Pins neu zuordnen müssen.

Zum Beispiel teilen sich sowohl SCL und SDA als auch A0 und A1 die gleiche Hardware-I2C-Peripherie (SCL1 / SDA1 - siehe das Bild der Adafruit Feather RP2040 Pinbelegung in diesem Artikel).

Für den Fall, dass Sie die gleiche Pin-Konfiguration verwenden möchten, können Sie mit bitbangio einen zusätzlichen softwaregesteuerten I2C-Port "erstellen". Der Nachteil dabei ist eine geringere Geschwindigkeit für diesen Software-I2C-Port und eine höhere CPU-Last.

RuntimeError: Kein Pull-Up an SDA oder SCL gefunden; überprüfen Sie Ihre Verdrahtung

Wenn Sie die folgende Fehlermeldung erhalten

RuntimeError: Kein Pull-Up an SDA oder SCL gefunden; überprüfen Sie Ihre Verdrahtung

dann sollten Sie Pullup-Widerstände zwischen 3V3 auf dem Board (dem 3,3V-Pin) und Ihren SDA- bzw. SCL-Pins setzen. Diese sind für den normalen I2C-Betrieb erforderlich (die Geräte ziehen die I2C-Pins herunter, um zu kommunizieren, der Standard-/Idle-Zustand auf dem Bus ist hoch) - und sind nicht auf dem Adafruit Feather RP2040 enthalten. Sie sind bei vielen Adafruit-Peripheriegeräten und bei Peripheriegeräten anderer Firmen enthalten (wie z.B. auch bei unseren eigenen BME688-Breakout-Platine).

Falls Sie nicht wissen, was ein Pullup ist: Dies ist im Wesentlichen ein Widerstand zwischen dem betreffenden Pin (z. B. SDA) und dem 3,3-V-Versorgungspin. Er muss nicht furchtbar genau sein. Sie sollten mit 10 kOhm-Widerständen beginnen, wenn das nicht funktioniert, versuchen Sie es vielleicht mit einem 1 kOhm-Widerstand für einen "steiferen" Pullup.

ZeitüberschreitungFehler: Zeitspanne zu lang

Prüfen Sie, ob der Chip, mit dem Sie sprechen wollen, ordnungsgemäß mit Strom versorgt wird.

Diverse Notizen

Referenzen / Ressourcen / Links / Weiterführende Literatur

Möchten Sie mehr über I2C erfahren? Besuchen Sie unser Artikel dazu hier.

3 Kommentare

  1. Veröffentlich von Rob am Januar 23, 2023 um 4:45 pm

    Vielen Dank für diesen hilfreichen Artikel! Ich habe zwei Vorschläge:

    1) Eine Ursache für den "no pull up found"-Fehler (der mich hierher brachte) kann auch durch einen dummen Fehler verursacht werden: ein loses Kabel! Das passiert mir nur allzu oft, weil ich Stemma-Stecker verwende, die ein wenig empfindlich sind.

    2) Ich denke, dass Ihr Beispielcode ein wenig verwirrend ist, da ich denke, dass er mit dem einfachsten Fall beginnen könnte/sollte, in dem Sie die beiden verfügbaren I2C-Controller verwenden, wie z. B.

    i2c = busio.I2C(board.SCL, board.SDA)

    si2c = busio.I2C(Karte.A9, Karte.A6)

    Die Methoden, die Sie danach anwenden, sind natürlich weiterhin gültig und nützlich!

    /rob

    • Veröffentlich von raspi berry am Februar 4, 2023 um 11:47 am

      Vielen Dank für Ihren nachdenklichen Kommentar, Rob! Lose Drähte sind so etwas wie das "Ist es mit Strom versorgt?" der Computer-Support-Welt 🙂 Besser zu überprüfen als stundenlanges Debuggen

  2. Veröffentlich von Edward M Johnstone am Juni 27, 2023 um 2:43 pm

    Ich danke Ihnen für diesen Artikel.
    Ich bin von Raspberry Pi Pico migrieren und haben für etwas wie dieses gesucht!
    Fantastisch!

Hinterlassen Sie einen Kommentar