CircuitPython, Adafruit Feather RP2040, et I2C

Comme vous l'avez probablement deviné par le titre, cet article traite de CircuitPython, de l'Adafruit Feather RP2040 et du protocole de communication I2C.

Le RP2040 dispose de deux contrôleurs I2C - ce qui est intéressant, par exemple, lorsque vous souhaitez faire fonctionner deux périphériques I2C avec la même adresse I2C.

Dans ma configuration de test, j'ai une carte de microcontrôleur Adafruit Feather RP2040, et j'ai attaché deux de nos cartes à puce. Cartes d'extension BME688 - une utilisant les broches SCL + SDA et une utilisant A1 (pour SCL) + A0 (pour SDA).

J'utilise CircuitPython dans sa version 7.0.0, que vous pouvez télécharger ici.

De plus, j'ai installé toutes les librairies d'Adafruit dans le fichier lib sur le Feather RP2040. (Le Feather RP2040 dispose d'assez d'espace sur son Flash pour permettre cela)

Vous pouvez télécharger ces bibliothèques dans le Adafruit CircuitPython Bundle ici. (J'ai téléchargé adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

NotePour installer ces bibliothèques, il suffit de les copier dans le dossier lib du "lecteur" CIRCUITPY qui est monté sur votre ordinateur. Vous devrez bien sûr copier uniquement les bibliothèques, et non les exemples et autres.

Le gros avantage d'utiliser le matériel d'Adafruit est que vous obtenez une tonne d'exemples qui couvrent de nombreuses puces populaires, et vous pouvez très facilement commencer avec. Il y a des choses comme le pilotage d'une carte microSD en utilisant SPI, la lecture d'un RTC, et la lecture du capteur BME680.

Tester I2C dans CircuitPython

J'ai la configuration suivante, car je veux piloter deux appareils indépendamment l'un de l'autre (qui se trouvent avoir les mêmes adresses dans ce cas) :

Feather RP2040 et deux Cartes d'extension BME688

Notez que nos cartes breakout BME688 incluent déjà des Pullups pour SDA et SCL. (Vous avez besoin de pullups sur SDA et SCL).

Note 2 : Notre carte breakout BME688 a la possibilité de changer l'adresse, ce scénario est donc destiné à des fins de démonstration.

Afin de piloter les deux jeux de broches séquentiellement (pour découvrir les périphériques), j'utilise le code suivant :

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

Remarque : le i2c.deinit() est la clé pour que cet exemple particulier fonctionne ! (parce que SCL / SDA et A0 / A1 ont tous deux le même périphérique I2C matériel - voir ci-dessous).

Le résultat devrait être le suivant :

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

Ici, le 119 est décimal pour hex 0x77 - qui est l'adresse de notre carte breakout BME688 dans l'état par défaut.

Les deux tableaux sont vus, séquentiellement dans les scans individuels.

Le problème est que nous voulons les utiliser simultanément.

Faire fonctionner deux bus I2C simultanément sur le Adafruit Feather RP2040

CircuitPython supporte les deux contrôleurs matériels (SDA0/SCL0 et SDA1/SCL1). Vous n'avez pas besoin de définir de configuration (quel contrôleur vous voulez utiliser, ou comment muxer les broches) - ceci est pris en charge par busio pour vous.

Vous devez cependant faire attention aux broches que vous utilisez, car les broches ne fournissent que un de ces bus dans chaque cas, et si vous choisissez des broches contradictoires, vous obtiendrez ValueError : Périphérique I2C en cours d'utilisation .

Si vous voulez utiliser des broches "conflictuelles", par exemple SCL / SDA (qui ont SCL1 et SDA1) et A0 / A1 (qui ont aussi SCL1 et SDA1), vous devrez bitbanguer un des ports :

Voici comment analyser cette configuration de broches sans appeler 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()

Nous utilisons bitbangio pour piloter un I2C secondaire. Dans mon cas, l'I2C secondaire est utilisé à des fins internes (pour une extension de port), et peut très probablement se contenter d'une vitesse d'interface inférieure.

Remarque : vous ne pouvez pas choisir le périphérique I2C qui est acheminé vers les broches dans le logiciel - si vous avez besoin d'un périphérique différent, vous devez utiliser des broches différentes.

Dépannage des erreurs I2C

ValueError : Périphérique I2C en cours d'utilisation

Si vous utilisez busio.I2C pour les deux ports : Vérifiez si vous utilisez déjà le même périphérique I2C matériel - et si vous devez réassigner vos broches.

Par exemple, SCL et SDA, et A0 et A1 partagent le même périphérique I2C matériel (SCL1 / SDA1 - voir l'image du brochage du Adafruit Feather RP2040 dans cet article).

Si vous voulez utiliser la même configuration de broches, vous pouvez utiliser bitbangio pour "créer" un port I2C supplémentaire contrôlé par logiciel. L'inconvénient est une vitesse plus faible pour ce port I2C logiciel, et une charge CPU plus élevée.

RuntimeError : Aucun pull up trouvé sur SDA ou SCL ; vérifiez votre câblage.

Si vous obtenez l'erreur suivante

RuntimeError : Aucun pull up trouvé sur SDA ou SCL ; vérifiez votre câblage.

alors vous devez mettre des résistances pullup entre 3V3 sur la carte (la broche 3.3V) et respectivement vos broches SDA et SCL. Ces résistances sont nécessaires pour un fonctionnement I2C normal (les périphériques tirent vers le bas les broches I2C pour communiquer, l'état par défaut / de repos sur le bus est élevé) - et ne sont pas incluses sur le Adafruit Feather RP2040. Ils sont inclus sur de nombreux périphériques Adafruit, et sur les périphériques d'autres sociétés (comme, encore une fois notre propre Carte d'extension BME688).

Si vous ne savez pas ce qu'est un pullup : il s'agit essentiellement d'une résistance entre la broche en question (par exemple SDA) et la broche d'alimentation de 3,3 V. Elle n'a pas besoin d'être terriblement précise. Vous devriez commencer par des résistances de 10 kOhm, si cela ne fonctionne pas, essayez éventuellement une résistance de 1 kOhm pour un pullup plus "rigide".

TimeoutError : L'horloge s'étire trop longtemps

Vérifiez si la puce à laquelle vous voulez parler est correctement alimentée.

Notes diverses

Références / Ressources / Liens / Lectures complémentaires

Vous voulez en savoir plus sur l'I2C ? Consultez le site notre article à ce sujet ici.

3 commentaires

  1. Rob sur janvier 23, 2023 à 4:45 pm

    Merci pour cet article utile ! J'ai deux suggestions :

    1) Une des causes de l'erreur "no pull up found" (c'est ce qui m'a amené ici) peut aussi être causée par une erreur stupide : avoir un fil détaché ! Cela m'arrive trop souvent car j'utilise des connecteurs Stemma qui sont un peu fins.

    2) Je pense que votre exemple de code est un peu confus, dans la mesure où je pense qu'il aurait pu/devait commencer par le cas le plus simple où vous utilisez les deux contrôleurs I2C disponibles, par exemple

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

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

    Les méthodes que vous utilisez ensuite sont bien sûr toujours valables et utiles !

    /rob

    • raspi berry sur février 4, 2023 à 11:47 am

      Merci pour votre commentaire réfléchi, Rob ! Les fils lâches sont comme le "c'est alimenté ?" du monde de l'assistance informatique 🙂 Mieux vaut vérifier que de passer des heures à déboguer.

  2. Edward M Johnstone sur juin 27, 2023 à 2:43 pm

    Merci pour cet article.
    Je suis en train de migrer du Raspberry Pi Pico et je cherchais quelque chose comme ça !
    Génial !

Laissez un commentaire