CircuitPython, Adafruit Feather RP2040 e I2C

Come probabilmente avrete intuito dal titolo, questo articolo parla di CircuitPython, dell'Adafruit Feather RP2040 e del protocollo di comunicazione I2C.

L'RP2040 ha due controllori I2C - buono per esempio, quando vuoi far funzionare due dispositivi I2C con lo stesso indirizzo I2C.

Nella mia configurazione di prova, ho una scheda microcontrollore Adafruit Feather RP2040, e ho collegato due dei nostri Schede breakout BME688 - uno usando i pin SCL + SDA e uno usando A1 (per SCL) + A0 (per SDA).

Sto usando CircuitPython nella versione 7.0.0, che potete scaricare da qui.

Inoltre, ho installato tutte le librerie di Adafruit nella cartella lib sul Feather RP2040. (Il Feather RP2040 ha abbastanza spazio sulla sua Flash per permettere questo)

Potete scaricare queste librerie nella sezione Adafruit CircuitPython Bundle qui. (Ho scaricato adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

NotaPer installare queste librerie basta copiarle nella cartella lib del "drive" CIRCUITPY montato sul tuo computer. Naturalmente dovrai copiare solo le librerie, non gli esempi e le altre cose.

Il grande vantaggio nell'usare il materiale di Adafruit è che si ottiene una tonnellata di esempi che coprono molti chip popolari, e si può iniziare molto facilmente con esso. Ci sono cose come pilotare una scheda microSD usando SPI, leggere un RTC e leggere dal sensore BME680.

Testare I2C in CircuitPython

Ho la seguente configurazione, dato che voglio pilotare due dispositivi indipendentemente l'uno dall'altro (che in questo caso hanno gli stessi indirizzi):

Piuma RP2040 e due Schede breakout BME688

Notate che le nostre schede di breakout BME688 includono già i pullup per SDA e SCL. (Avete bisogno di pullup su SDA e SCL).

Nota 2: La nostra scheda di breakout BME688 ha l'opzione di cambiare l'indirizzo, quindi questo scenario è inteso a scopo dimostrativo.

Per guidare attraverso entrambi i set di pin in modo sequenziale (per scoprire i dispositivi), sto usando il seguente codice:

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

Nota: il i2c.deinit() è la chiave per far funzionare questo particolare esempio! (perché SCL / SDA e A0 / A1 hanno entrambi la stessa periferica hardware I2C - vedi sotto).

Questo dovrebbe produrre quanto segue:

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

Qui, 119 è decimale per hex 0x77 - che è l'indirizzo della nostra scheda di breakout BME688 nello stato predefinito.

Entrambe le schede si vedono, in sequenza, nelle singole scansioni.

Il problema è che vogliamo usarli simultaneamente.

Far funzionare due bus I2C simultaneamente sull'Adafruit Feather RP2040

CircuitPython supporta entrambi controllori hardware (SDA0/SCL0 e SDA1/SCL1). Non avete bisogno di impostare alcuna configurazione (quale controller volete usare, o come fare il mux dei pin) - questo è curato da busio per te.

Dovete fare attenzione, però, a quali perni usate, perché i perni forniranno solo uno di questi bus in ogni caso, e se vi capita di scegliere pin in conflitto, otterrete ValueError: periferica I2C in uso .

Se volete usare dei pin "in conflitto", per esempio SCL / SDA (che hanno SCL1 e SDA1) e A0 / A1 (che hanno anche SCL1 e SDA1), dovrete bitbangare una delle porte:

Ecco come scansionare questa configurazione di pin senza chiamare 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()

Stiamo usando bitbangio per pilotare un I2C secondario. Nel mio caso l'I2C secondario è usato per uno scopo interno (per un espansore di porte), e molto probabilmente può fare con una velocità di interfaccia inferiore.

Nota: non siete in grado di scegliere la periferica I2C che viene indirizzata ai pin nel software - se avete bisogno di una periferica diversa, dovete usare pin diversi.

Risoluzione dei problemi degli errori I2C

ValueError: periferica I2C in uso

Se state usando busio.I2C per entrambe le porte: Controllate se state già usando la stessa periferica hardware I2C - e se è necessario riassegnare i pin.

Per esempio, sia SCL e SDA, sia A0 e A1 condividono la stessa periferica hardware I2C (SCL1 / SDA1 - vedi la foto del pinout di Adafruit Feather RP2040 in questo articolo).

Nel caso in cui vogliate usare la stessa configurazione dei pin, potete usare bitbangio per "creare" un'ulteriore porta I2C controllata dal software. Lo svantaggio di questo è una minore velocità per questa porta I2C software, e un maggiore carico della CPU.

RuntimeError: Nessun pull up trovato su SDA o SCL; controlla il tuo cablaggio

Se ottieni il seguente errore

RuntimeError: Nessun pull up trovato su SDA o SCL; controlla il tuo cablaggio

allora dovresti mettere delle resistenze di pullup tra 3V3 sulla scheda (il pin 3.3V) e rispettivamente i tuoi pin SDA e SCL. Questi sono necessari per il normale funzionamento I2C (i dispositivi tirano giù i pin I2C per comunicare, lo stato predefinito / inattivo sul bus è alto) - e non sono inclusi nella Adafruit Feather RP2040. Sono inclusi in molte periferiche Adafruit, e in periferiche di altre aziende (come, di nuovo il nostro Scheda di breakout BME688).

Se non sai cos'è un pullup: questo è essenzialmente una resistenza tra il pin in questione (per esempio SDA) e il pin di alimentazione a 3,3 V. Non è necessario che sia terribilmente preciso. Dovresti iniziare con resistenze da 10 kOhm, se questo non funziona, prova eventualmente una resistenza da 1 kOhm per un pullup più "rigido".

TimeoutError: Tratto dell'orologio troppo lungo

Controlla se il chip con cui vuoi parlare è alimentato correttamente.

Note varie

Riferimenti / Risorse / Link / Ulteriori letture

Volete saperne di più su I2C? Date un'occhiata a il nostro articolo al riguardo qui.

3 commenti

  1. Rob in Gennaio 23, 2023 il 4:45 pm

    Grazie per l'utile articolo! Ho due suggerimenti:

    1) Una causa dell'errore "nessun pull up trovato" (che è quello che mi ha portato qui) può essere causata anche da un errore stupido: avere un filo allentato! Questo mi capita troppo spesso perché uso connettori Stemma che sono un po' troppo sottili.

    2) Penso che il vostro codice di esempio sia un po' confuso, in quanto credo che avrebbe potuto/dovuto iniziare con il caso più semplice in cui si utilizzano i due controllori I2C disponibili, come ad esempio

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

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

    I metodi utilizzati successivamente sono ovviamente ancora validi e utili!

    /rob

    • raspi berry in Febbraio 4, 2023 il 11:47 am

      Grazie per il tuo commento, Rob! I fili allentati sono come il "è alimentato?" del mondo dell'assistenza informatica 🙂 È meglio controllare che passare ore a fare il debug.

  2. Edward M Johnstone in Giugno 27, 2023 il 2:43 pm

    Grazie per questo articolo.
    Sto migrando da Raspberry Pi Pico e stavo cercando qualcosa di simile!
    Fantastico!

Lascia un commento