CircuitPython, Adafruit Feather RP2040 en I2C

Zoals je waarschijnlijk al aan de titel kon zien, gaat dit artikel over CircuitPython, de Adafruit Feather RP2040 en het I2C-communicatieprotocol.

De RP2040 heeft twee I2C controllers - goed wanneer u bijvoorbeeld twee I2C apparaten met hetzelfde I2C adres wilt laten werken.

In mijn testopstelling heb ik een Adafruit Feather RP2040 microcontroller bord, en heb ik twee van onze BME688 breakout borden - één met de pennen SCL + SDA en één met A1 (voor SCL) + A0 (voor SDA).

Ik gebruik CircuitPython in versie 7.0.0, die u hier kunt downloaden.

Verder heb ik alle bibliotheken van Adafruit geïnstalleerd in de lib map op de Feather RP2040. (De Feather RP2040 heeft voldoende ruimte op zijn Flash om dit mogelijk te maken)

U kunt deze bibliotheken downloaden in de Adafruit CircuitPython Bundel hier. (Ik heb gedownload adafruit-circuitpython-bundel-7.x-mpy-20211123.zip)

Noot: om deze bibliotheken te installeren kopieert u ze eenvoudigweg naar de map lib op de CIRCUITPY "drive" die op uw computer is aangekoppeld. U moet natuurlijk alleen de bibliotheken kopiëren, niet de voorbeelden en andere dingen.

Het grote voordeel van het gebruik van Adafruit's spullen is dat je een heleboel voorbeelden krijgt die veel populaire chips dekken, en je kunt er heel gemakkelijk mee beginnen. Er zijn dingen zoals het aansturen van een microSD kaart met SPI, het uitlezen van een RTC, en het uitlezen van de BME680 sensor.

I2C testen in CircuitPython

Ik heb de volgende opstelling, omdat ik twee apparaten onafhankelijk van elkaar wil aansturen (die in dit geval toevallig dezelfde adressen hebben):

Feather RP2040 en twee BME688 breakout borden

Merk op dat onze BME688 breakout boards al pullups bevatten voor SDA en SCL. (U heeft pullups nodig op SDA en SCL).

Noot 2: Ons BME688 breakout bord heeft de mogelijkheid om het adres te veranderen, dus dit scenario is bedoeld voor demonstratie doeleinden.

Om beide pinnenreeksen sequentieel te doorlopen (om de apparaten te ontdekken), gebruik ik de volgende 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()

Opmerking: de i2c.deinit() is de sleutel tot de werking van dit specifieke voorbeeld! (omdat SCL / SDA en A0 / A1 beide dezelfde hardware I2C periferie hebben - zie hieronder).

Dit zou het volgende moeten opleveren:

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

Hier, 119 is decimaal voor hex 0x77 - dat is het adres van onze BME688 breakout bord in de standaard toestand.

Beide borden zijn te zien, achtereenvolgens in de afzonderlijke scans.

Het probleem is dat we ze gelijktijdig willen gebruiken.

Twee I2C bussen gelijktijdig op de Adafruit Feather RP2040

CircuitPython ondersteunt beide hardware controllers (SDA0/SCL0 en SDA1/SCL1). U hoeft geen configuratie in te stellen (welke controller u wilt gebruiken, of hoe de pinnen gemuxed moeten worden) - dit wordt verzorgd door busio voor jou.

U moet wel opletten welke pinnen u gebruikt, want de pinnen leveren alleen een van deze bussen in elk geval, en als je toevallig conflicterende pinnen kiest, krijg je ValueError: I2C randapparaat in gebruik .

Als u "conflicterende" pinnen wilt gebruiken, bijvoorbeeld SCL / SDA (die SCL1 en SDA1 hebben) en A0 / A1 (die ook SCL1 en SDA1 hebben), dan moet u een van de poorten bitbangen:

Hier is hoe je deze pin-configuratie kan scannen zonder deinit() op te roepen:

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

We gebruiken bitbangio om een secundaire I2C aan te sturen. In mijn geval wordt de secundaire I2C gebruikt voor een intern doel (voor een poortuitbreider), en kan deze waarschijnlijk met een lagere interfacesnelheid toe.

Opmerking: u kunt het I2C-periferie dat naar de pennen wordt geleid niet softwarematig kiezen - als u een ander periferie nodig hebt, moet u andere pennen gebruiken.

Oplossen van I2C fouten

ValueError: I2C randapparaat in gebruik

Als je busio.I2C gebruikt voor beide poorten: Controleer of u al dezelfde hardware I2C periferie gebruikt - en of u uw pinnen opnieuw moet toewijzen.

Bijvoorbeeld, zowel SCL en SDA , en A0 en A1 delen dezelfde hardware I2C periferie (SCL1 / SDA1 - zie de Adafruit Feather RP2040 pinout foto in dit artikel).

In het geval dat je toch dezelfde pin setup wilt gebruiken, kun je bitbangio gebruiken om een extra software-gestuurde I2C poort te "maken". Het nadeel hiervan is een lagere snelheid voor deze software-I2C poort, en een hogere CPU belasting.

RuntimeError: Geen pull up gevonden op SDA of SCL; controleer uw bedrading

Als u de volgende foutmelding krijgt

RuntimeError: Geen pull up gevonden op SDA of SCL; controleer uw bedrading

dan moet je pullup weerstanden plaatsen tussen 3V3 op het bord (de 3.3V pin) en respectievelijk je SDA en SCL pinnen. Deze zijn nodig voor een normale I2C werking (de apparaten trekken de I2C pinnen naar beneden om te communiceren, de standaard / idle toestand op de bus is hoog) - en zijn niet inbegrepen op de Adafruit Feather RP2040. Ze zijn wel aanwezig op veel randapparatuur van Adafruit, en op randapparatuur van andere bedrijven (zoals, alweer onze eigen BME688 breakout bord).

Als u niet weet wat een pullup is: dit is in wezen een weerstand tussen de pin in kwestie (b.v. SDA) en de 3,3 V voedingspin. Hij hoeft niet erg precies te zijn. U kunt het beste beginnen met weerstanden van 10 kOhm, als dat niet werkt, probeer dan eventueel een weerstand van 1 kOhm voor een "stijvere" pullup.

TimeoutError: Klok rek te lang

Controleer of de chip waarmee u wilt praten de juiste spanning heeft.

Diverse notities

Referenties / Hulpbronnen / Links / Verdere lectuur

Wil je meer weten over I2C? Kijk dan op ons artikel erover hier.

3 Opmerkingen

  1. Rob op januari 23, 2023 op 4:45 pm

    Bedankt voor het nuttige artikel! Ik heb twee suggesties:

    1) Een oorzaak van de "no pull up found" fout (wat mij hier bracht) kan ook worden veroorzaakt door een domme fout: het hebben van een losse draad! Dit overkomt mij maar al te vaak omdat ik Stemma connectoren gebruik die een beetje finniky zijn.

    2) Ik denk dat uw voorbeeldcode een beetje verwarrend is, omdat ik denk dat het had kunnen/moeten beginnen met het eenvoudigste geval waarin u de twee beschikbare I2C-controllers gebruikt, zoals

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

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

    De methoden die je daarna gebruikt zijn natuurlijk nog steeds geldig en nuttig!

    /rob

    • raspi berry op februari 4, 2023 op 11:47 am

      Bedankt voor je doordachte commentaar, Rob! Losse draden zijn als het "wordt het gevoed?" van de computer support wereld 🙂 Beter om te controleren dan uren te besteden aan debugging

  2. Edward M Johnstone op juni 27, 2023 op 2:43 pm

    Bedankt voor dit artikel.
    Ik migreer van Raspberry Pi Pico en was op zoek naar iets als dit!
    Geweldig!

Laat een reactie achter