CircuitPython, Adafruit Feather RP2040 i I2C

Jak zapewne domyśliłeś się po tytule, artykuł ten omawia CircuitPython, Adafruit Feather RP2040 i protokół komunikacyjny I2C.

RP2040 posiada dwa kontrolery I2C - dobre na przykład, gdy chcesz uruchomić dwa urządzenia I2C z tym samym adresem I2C.

W mojej konfiguracji testowej, mam płytkę z mikrokontrolerem Adafruit Feather RP2040, i podłączyłem do niej dwa z naszych BME688 breakout boards - jeden z wykorzystaniem pinów SCL + SDA oraz jeden z wykorzystaniem A1 (dla SCL) + A0 (dla SDA).

Używam CircuitPython w wersji 7.0.0, które można pobrać stąd.

Ponadto, zainstalowałem wszystkie biblioteki Adafruit do katalogu lib w folderze Feather RP2040. (Feather RP2040 ma wystarczająco dużo miejsca na swojej pamięci Flash, aby to umożliwić)

Możesz pobrać te biblioteki w Adafruit CircuitPython Bundle tutaj. (Pobrałem adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

Uwaga: aby zainstalować te biblioteki po prostu skopiuj je do folderu lib na "dysku" CIRCUITPY, który jest zamontowany na twoim komputerze. Oczywiście będziesz musiał skopiować tylko biblioteki, nie przykłady i inne rzeczy.

Dużą zaletą korzystania z rzeczy Adafruit jest to, że dostajesz tonę przykładów, które obejmują wiele popularnych układów, i możesz bardzo łatwo zacząć od tego. Są tam takie rzeczy jak obsługa karty microSD za pomocą SPI, odczyt RTC, czy odczyt z czujnika BME680.

Testowanie I2C w CircuitPython

Mam następującą konfigurację, ponieważ chcę wysterować dwa urządzenia niezależnie od siebie (które w tym przypadku mają te same adresy):

Feather RP2040 i dwa BME688 breakout boards

Zauważ, że nasze płytki breakout BME688 zawierają już pullupy dla SDA i SCL. (Ty potrzebujesz pullupów na SDA i SCL).

Uwaga 2: Nasza płytka breakout BME688 ma możliwość zmiany adresu, więc ten scenariusz jest przeznaczony do celów demonstracyjnych.

Aby sekwencyjnie przejechać przez oba zestawy pinów (w celu wykrycia urządzeń), używam następującego kodu:

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

Uwaga i2c.deinit() jest kluczem do działania tego konkretnego przykładu! (ponieważ SCL / SDA i A0 / A1 mają zarówno ten sam sprzętowy peryferyjny I2C - patrz poniżej).

Powinno to dać następujące wyniki:

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

Tutaj, 119 jest dziesiętne dla heksadecymalnego 0x77 - który jest adresem naszej płytki BME688 w stanie domyślnym.

Obie tablice są widoczne, kolejno w poszczególnych skanach.

Problem w tym, że chcemy ich używać jednocześnie.

Uruchamianie dwóch magistral I2C jednocześnie na Adafruit Feather RP2040

CircuitPython obsługuje zarówno kontrolerów sprzętowych (SDA0/SCL0 i SDA1/SCL1). Nie musisz ustawiać żadnej konfiguracji (z którego kontrolera chcesz korzystać, czy jak muxować piny) - tym zajmuje się busio dla ciebie.

Należy jednak zwrócić uwagę, których pinów używamy, ponieważ będą one dostarczać tylko jeden z tych magistral w każdym przypadku, a jeśli zdarzy się, że wybierzesz sprzeczne piny, otrzymasz ValueError: Peryferia I2C w użyciu .

Jeśli chcesz użyć "konfliktowych" pinów, fo rexample SCL / SDA (które mają SCL1 i SDA1) i A0 / A1 (które również mają SCL1 i SDA1), będziesz musiał bitbangować jeden z portów:

Oto jak przeskanować tę konfigurację pinów bez wywoływania 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()

Używamy bitbangio aby wysterować drugorzędny I2C. W moim przypadku drugorzędny I2C jest używany do celów wewnętrznych (do ekspandera portów), i całkiem prawdopodobne, że może sobie poradzić z niższą prędkością interfejsu.

Uwaga: nie masz możliwości programowego wyboru peryferiów I2C, które są kierowane do pinów - jeśli potrzebujesz innych peryferiów, musisz użyć innych pinów.

Rozwiązywanie problemów z błędami I2C

ValueError: Peryferia I2C w użyciu

Jeśli używasz busio.I2C dla obu portów: Sprawdź, czy używasz już tego samego sprzętowego peryferium I2C - i czy musisz ponownie przypisać swoje piny.

Na przykład, zarówno SCL i SDA , jak i A0 i A1 współdzielą ten sam sprzętowy peryferyjny I2C (SCL1 / SDA1 - patrz rysunek wyprowadzeń Adafruit Feather RP2040 w tym artykule).

W przypadku, gdy chcesz użyć tej samej konfiguracji pinów, możesz użyć bitbangio do "stworzenia" dodatkowego portu I2C sterowanego programowo. Wadą tego jest mniejsza prędkość dla tego portu I2C sterowanego programowo i większe obciążenie procesora.

RuntimeError: Nie znaleziono podciągania na SDA lub SCL; sprawdź okablowanie

Jeśli wystąpi następujący błąd

RuntimeError: Nie znaleziono podciągania na SDA lub SCL; sprawdź okablowanie

to powinieneś umieścić rezystory podciągające pomiędzy 3V3 na płytce (pin 3.3V) a odpowiednio twoimi pinami SDA i SCL. Są one wymagane do normalnej pracy I2C (urządzenia ściągają piny I2C aby się komunikować, domyślnym stanem bezczynności na magistrali jest stan wysoki) - i nie są dołączone do Adafruit Feather RP2040. Są one zawarte w wielu urządzeniach peryferyjnych Adafruit, oraz w urządzeniach peryferyjnych innych firm (jak, ponownie, nasze własne BME688 breakout board).

Jeśli nie wiesz co to jest pullup: jest to w zasadzie rezystor pomiędzy danym pinem (np. SDA) a pinem zasilania 3.3 V. Nie musi być on strasznie dokładny. Powinieneś zacząć od rezystorów 10 kOhm, jeśli to nie zadziała, ewentualnie spróbuj rezystora 1 kOhm dla "sztywniejszego" podciągania.

TimeoutError: Zbyt długi odcinek zegara

Sprawdź, czy układ, z którym chcesz rozmawiać, jest prawidłowo zasilany.

Różne notatki

Referencje / Zasoby / Linki / Dalsza lektura

Chcesz dowiedzieć się więcej o I2C? Sprawdź nasz artykuł na ten temat tutaj.

Komentarzy: 3

  1. Rob styczeń 23, 2023 o 4:45 pm

    Dziękuję za pomocny artykuł! Mam dwie sugestie:

    1) Jedna z przyczyn błędu "no pull up found" (co mnie tu sprowadziło) może być również spowodowana przez głupi błąd: posiadanie luźnego przewodu! Zdarza mi się to zbyt często, ponieważ używam złączy Stemma, które są trochę finnistyczne.

    2) Myślę, że twój przykładowy kod jest trochę mylący, w tym sensie, że myślę, że mógłby/powinien zacząć od najprostszego przypadku, w którym używasz dwóch dostępnych kontrolerów I2C, np.

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

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

    Metody, których używasz po tym, są oczywiście nadal ważne i przydatne!

    /rob

    • raspi berry luty 4, 2023 o 11:47 am

      Dziękuję za Twój przemyślany komentarz, Rob! Luźne przewody są jak "czy to jest zasilane?" w świecie obsługi komputera 🙂 Lepiej sprawdzić niż spędzać godziny na debugowaniu

  2. Edward M Johnstone czerwiec 27, 2023 o 2:43 pm

    Dziękuję za ten artykuł.
    Migruję z Raspberry Pi Pico i szukałem czegoś takiego!
    Niesamowite!

Pozostaw komentarz