CircuitPython, Adafruit Feather RP2040 och I2C

Som du förmodligen gissat av titeln diskuterar den här artikeln CircuitPython, Adafruit Feather RP2040 och I2C-kommunikationsprotokollet.

RP2040 har två I2C-kontroller - bra till exempel när du vill köra två I2C-enheter med samma I2C-adress.

I min testuppställning har jag ett Adafruit Feather RP2040-mikrokontrollerkort och har kopplat två av våra BME688 breakout-kort - en med hjälp av stiften SCL + SDA och en med hjälp av A1 (för SCL) + A0 (för SDA).

Jag använder CircuitPython i version 7.0.0.0, som du kan ladda ner här.

Dessutom har jag installerat alla Adafruits bibliotek på lib på Feather RP2040. (Feather RP2040 har tillräckligt med utrymme på Flash för att möjliggöra detta).

Du kan ladda ner dessa bibliotek i Adafruit CircuitPython Bundle här. (Jag laddade ner adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

Obs: För att installera dessa bibliotek kopierar du dem helt enkelt till lib-mappen på CIRCUITPYs "enhet" som är monterad på din dator. Du måste naturligtvis bara kopiera biblioteken, inte exemplen och andra saker.

Den stora fördelen med att använda Adafruits produkter är att du får massor av exempel som täcker många populära chip, och du kan mycket enkelt börja med dem. Det finns saker som att driva ett microSD-kort med hjälp av SPI, läsa en RTC och läsa från BME680-sensorn.

Testning av I2C i CircuitPython

Jag har följande inställning, eftersom jag vill driva två enheter oberoende av varandra (som råkar ha samma adresser i det här fallet):

Feather RP2040 och två BME688 breakout-kort

Observera att våra BME688 breakoutkort redan innehåller Pullups för SDA och SCL. (Du behöver pullups på SDA och SCL).

Anmärkning 2: Vårt BME688-breakoutkort har möjlighet att ändra adressen, så detta scenario är avsett för demonstrationsändamål.

För att köra igenom båda stiftuppsättningarna sekventiellt (för att upptäcka enheterna) använder jag följande kod:

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()

Observera: den i2c.deinit() är nyckeln till att detta exempel fungerar! (eftersom SCL / SDA och A0 / A1 har båda samma I2C-periferi - se nedan).

Detta bör ge följande resultat:

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

Här är 119 decimal för hex 0x77 - vilket är adressen till vårt BME688 breakoutkort i standardtillstånd.

Båda brädorna syns i tur och ordning i de enskilda skanningarna.

Problemet är att vi vill använda dem samtidigt.

Kör två I2C-bussar samtidigt på Adafruit Feather RP2040

CircuitPython stöder båda hårdvarukontroller (SDA0/SCL0 och SDA1/SCL1). Du behöver inte ställa in någon konfiguration (vilken styrenhet du vill använda, eller hur du ska muxa stiftstiftet) - detta tas om hand av busio för dig.

Du måste dock vara uppmärksam på vilka stift du använder, eftersom stiften endast ger dig en av dessa bussar i varje enskilt fall, och om du råkar välja motstridiga stift får du en Värdefel: I2C-periferi i bruk .

Om du vill använda "motstridiga" stift, till exempel SCL/SDA (som har SCL1 och SDA1) och A0/A1 (som också har SCL1 och SDA1), måste du bitbanga en av portarna:

Så här skannar du den här stiftkonfigurationen utan att anropa deinit():

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()

Vi använder bitbangio för att driva en sekundär I2C. I mitt fall används den sekundära I2C för ett internt ändamål (för en port expander), och kan sannolikt klara sig med en lägre gränssnittshastighet.

Observera: Du kan inte välja vilken I2C-periferi som ska kopplas till stiften i programvaran - om du behöver en annan periferi måste du använda andra stiften.

Felsökning av I2C-fel

Värdefel: I2C-periferi i bruk

Om du använder busio.I2C för båda portarna: Kontrollera om du redan använder samma I2C-periferiutrustning - och om du behöver omfördela dina stift.

Till exempel delar både SCL och SDA och A0 och A1 samma I2C-periferi (SCL1/SDA1 - se bilden av Adafruit Feather RP2040-pinnutformningen i den här artikeln).

Om du vill använda samma stiftuppsättning kan du använda bitbangio för att "skapa" en extra mjukvarustyrd I2C-port. Nackdelen med detta är lägre hastighet för denna mjukvaru-I2C-port och en högre CPU-belastning.

RuntimeError: Kontrollera din kabeldragning: Ingen pull up hittades på SDA eller SCL.

Om du får följande fel

RuntimeError: Kontrollera din kabeldragning: Ingen pull up hittades på SDA eller SCL.

då bör du sätta pullupmotstånd mellan 3V3 på kortet (3,3V-stiftet) och dina SDA- respektive SCL-stift. Dessa krävs för normal I2C-drift (enheterna drar ner I2C-stiften för att kommunicera, standardläget/viloläget på bussen är högt) - och ingår inte i Adafruit Feather RP2040. De finns med på många Adafruit-periferier och på periferier från andra företag (som, återigen, vår egen BME688 breakoutkort).

Om du inte vet vad en pullup är: det är i princip ett motstånd mellan pinnen i fråga (t.ex. SDA) och 3,3 V-försörjningspinnen. Den behöver inte vara särskilt exakt. Du bör börja med ett motstånd på 10 kOhm, om det inte fungerar kan du eventuellt prova ett motstånd på 1 kOhm för en "styvare" pullup.

TimeoutFel: Klockan sträcker sig för länge

Kontrollera att chipet som du vill prata med är ordentligt strömförsörjt.

Diverse anteckningar

Referenser / resurser / länkar / ytterligare läsning

Vill du lära dig mer om I2C? Kolla in vår artikel om det här.

3 Kommentarer

  1. Rob den januari 23, 2023 kl 4:45 e m

    Tack för en nyttig artikel! Jag har två förslag:

    1) En av orsakerna till felet "no pull up found" (vilket är vad som förde mig hit) kan också orsakas av ett dumt misstag: att ha en lös tråd! Detta händer mig alltför ofta eftersom jag använder Stemma-kontakter som är lite finniga.

    2) Jag tycker att din exempelkod är lite förvirrande, eftersom jag tycker att den kunde/skulle ha börjat med det enklaste fallet där du använder de två tillgängliga I2C-kontrollerna, t.ex.

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

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

    De metoder du använder efter det är naturligtvis fortfarande giltiga och användbara!

    /rob

    • raspi berry den februari 4, 2023 kl 11:47 f m

      Tack för din tankeväckande kommentar, Rob! Lösa kablar är som "är den strömförsörjd?" i datorsupportvärlden 🙂 Bättre att kontrollera än att spendera timmar med att felsöka.

  2. Edward M Johnstone den juni 27, 2023 kl 2:43 e m

    Tack för denna artikel.
    Jag migrerar från Raspberry Pi Pico och har letat efter något liknande!
    Fantastiskt!

Lämna en kommentar