CircuitPython, Adafruit Feather RP2040 και I2C

Όπως πιθανώς μαντέψατε από τον τίτλο, αυτό το άρθρο πραγματεύεται το CircuitPython, το Adafruit Feather RP2040 και το πρωτόκολλο επικοινωνίας I2C.

Το RP2040 διαθέτει δύο ελεγκτές I2C - καλό για παράδειγμα, όταν θέλετε να τρέξετε δύο συσκευές I2C με την ίδια διεύθυνση I2C.

Στη δοκιμαστική μου εγκατάσταση, έχω μια πλακέτα μικροελεγκτή Adafruit Feather RP2040 και έχω συνδέσει δύο από τα Πλακέτες διάσπασης BME688 - μία με χρήση των ακροδεκτών SCL + SDA και μία με χρήση των A1 (για SCL) + A0 (για SDA).

Χρησιμοποιώ το CircuitPython στην έκδοση 7.0.0, το οποίο μπορείτε να κατεβάσετε από εδώ.

Επιπλέον, έχω εγκαταστήσει όλες τις βιβλιοθήκες της Adafruit στο αρχείο lib στο φάκελο Feather RP2040. (Το Feather RP2040 έχει αρκετό χώρο στο Flash για να το επιτρέψει αυτό)

Μπορείτε να κατεβάσετε αυτές τις βιβλιοθήκες στο Adafruit CircuitPython Bundle εδώ. (Κατέβασα adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

Σημείωση: για να εγκαταστήσετε αυτές τις βιβλιοθήκες απλά αντιγράψτε τις στο φάκελο lib στο "δίσκο" του CIRCUITPY που είναι τοποθετημένος στον υπολογιστή σας. Φυσικά θα πρέπει να αντιγράψετε μόνο τις βιβλιοθήκες, όχι τα παραδείγματα και άλλα πράγματα.

Το μεγάλο πλεονέκτημα στη χρήση των υλικών της Adafruit είναι ότι λαμβάνετε έναν τόνο παραδειγμάτων που καλύπτουν πολλά δημοφιλή τσιπ και μπορείτε πολύ εύκολα να ξεκινήσετε με αυτά. Υπάρχουν πράγματα όπως η οδήγηση μιας κάρτας microSD με χρήση SPI, η ανάγνωση ενός RTC και η ανάγνωση από τον αισθητήρα BME680.

Δοκιμή I2C στο CircuitPython

Έχω την ακόλουθη ρύθμιση, καθώς θέλω να οδηγήσω δύο συσκευές ανεξάρτητα η μία από την άλλη (οι οποίες τυχαίνει να έχουν τις ίδιες διευθύνσεις σε αυτή την περίπτωση):

Feather RP2040 και δύο Πλακέτες διάσπασης BME688

Σημειώστε ότι οι πλακέτες BME688 περιλαμβάνουν ήδη Pullups για SDA και SCL. (Χρειάζεστε pullups για τα SDA και SCL).

Σημείωση 2: Η πλακέτα διάσπασης BME688 έχει τη δυνατότητα αλλαγής της διεύθυνσης, οπότε αυτό το σενάριο προορίζεται για σκοπούς επίδειξης.

Για να οδηγήσω διαδοχικά και τα δύο σύνολα ακροδεκτών (για να ανακαλύψω τις συσκευές), χρησιμοποιώ τον ακόλουθο κώδικα:

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

Σημείωση: το i2c.deinit() είναι το κλειδί για να δουλέψει αυτό το συγκεκριμένο παράδειγμα! (επειδή τα SCL / SDA και A0 / A1 έχουν και τα δύο το ίδιο περιφερειακό υλικό I2C - βλ. παρακάτω).

Αυτό θα πρέπει να βγάλει τα ακόλουθα:

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

Εδώ, το 119 είναι δεκαδικό για δεκαεξαδικό 0x77 - η οποία είναι η διεύθυνση της πλακέτας BME688 στην προεπιλεγμένη κατάσταση.

Και οι δύο πίνακες εμφανίζονται διαδοχικά στις μεμονωμένες σαρώσεις.

Το πρόβλημα είναι ότι θέλουμε να τα χρησιμοποιήσουμε ταυτόχρονα.

Εκτέλεση δύο διαύλων I2C ταυτόχρονα στο Adafruit Feather RP2040

Το CircuitPython υποστηρίζει και οι δύο ελεγκτές υλικού (SDA0/SCL0 και SDA1/SCL1). Δεν χρειάζεται να ρυθμίσετε καμία διαμόρφωση (ποιον ελεγκτή θέλετε να χρησιμοποιήσετε ή πώς να κάνετε mux τις ακίδες) - αυτό το αναλαμβάνει το busio για εσάς.

Θα πρέπει να προσέξετε, ωστόσο, ποιες ακίδες θα χρησιμοποιήσετε, καθώς οι ακίδες θα παρέχουν μόνο ένα από αυτούς τους διαύλους σε κάθε περίπτωση, και αν τύχει να επιλέξετε αντικρουόμενους ακροδέκτες, θα έχετε ValueError: I2C σε χρήση .

Αν θέλετε να χρησιμοποιήσετε "αντικρουόμενες" ακίδες, για παράδειγμα SCL / SDA (που έχουν SCL1 και SDA1) και A0 / A1 (που έχουν επίσης SCL1 και SDA1), θα πρέπει να κάνετε bitbang μία από τις θύρες:

Δείτε πώς μπορείτε να σαρώσετε αυτή τη διαμόρφωση ακροδεκτών χωρίς να καλέσετε την 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()

Χρησιμοποιούμε bitbangio για την οδήγηση ενός δευτερεύοντος I2C. Στην περίπτωσή μου, το δευτερεύον I2C χρησιμοποιείται για εσωτερικό σκοπό (για μια επέκταση θύρας) και είναι πολύ πιθανό να μπορεί να τα βγάλει πέρα με χαμηλότερη ταχύτητα διασύνδεσης.

Σημείωση: δεν έχετε τη δυνατότητα να επιλέξετε το περιφερειακό I2C το οποίο δρομολογείται στους ακροδέκτες στο λογισμικό - αν χρειάζεστε διαφορετικό περιφερειακό, πρέπει να χρησιμοποιήσετε διαφορετικούς ακροδέκτες.

Αντιμετώπιση σφαλμάτων I2C

ValueError: I2C σε χρήση

Εάν χρησιμοποιείτε busio.I2C και για τις δύο θύρες: Ελέγξτε αν χρησιμοποιείτε ήδη το ίδιο περιφερειακό I2C υλικού - και αν χρειάζεται να επαναπροσδιορίσετε τις ακίδες σας.

Για παράδειγμα, τόσο το SCL και το SDA , όσο και τα A0 και A1 μοιράζονται το ίδιο περιφερειακό υλικό I2C (SCL1 / SDA1 - δείτε την εικόνα pinout του Adafruit Feather RP2040 σε αυτό το άρθρο).

Σε περίπτωση που θέλετε να χρησιμοποιήσετε την ίδια ρύθμιση ακροδεκτών, μπορείτε να χρησιμοποιήσετε το bitbangio για να "δημιουργήσετε" μια πρόσθετη θύρα I2C ελεγχόμενη από το λογισμικό. Το μειονέκτημα αυτού είναι η χαμηλότερη ταχύτητα για αυτή τη θύρα I2C λογισμικού και το υψηλότερο φορτίο της CPU.

RuntimeError: Ελέγξτε την καλωδίωσή σας.

Εάν λάβετε το ακόλουθο σφάλμα

RuntimeError: Ελέγξτε την καλωδίωσή σας.

τότε θα πρέπει να τοποθετήσετε αντιστάσεις pullup μεταξύ του 3V3 στην πλακέτα (το pin 3.3V) και αντίστοιχα των pin SDA και SCL. Αυτά απαιτούνται για την κανονική λειτουργία του I2C (οι συσκευές κατεβάζουν τις ακίδες I2C για να επικοινωνήσουν, η προεπιλεγμένη / αδρανής κατάσταση στο δίαυλο είναι υψηλή) - και δεν περιλαμβάνονται στο Adafruit Feather RP2040. Περιλαμβάνονται σε πολλές περιφερειακές συσκευές της Adafruit, καθώς και σε περιφερειακές συσκευές άλλων εταιρειών (όπως, και πάλι η δική μας Πλακέτα διάσπασης BME688).

Αν δεν ξέρετε τι είναι το pullup: πρόκειται ουσιαστικά για μια αντίσταση μεταξύ του εν λόγω ακροδέκτη (π.χ. SDA) και του ακροδέκτη τροφοδοσίας 3,3 V. Δεν χρειάζεται να είναι τρομερά ακριβής. Θα πρέπει να ξεκινήσετε με αντιστάσεις 10 kOhm, αν αυτό δεν λειτουργεί, δοκιμάστε ενδεχομένως μια αντίσταση 1 kOhm για ένα πιο "σκληρό" pullup.

TimeoutError: Clock stretch too long

Ελέγξτε αν το τσιπ στο οποίο θέλετε να μιλήσετε τροφοδοτείται σωστά.

Διάφορες σημειώσεις

Αναφορές / Πηγές / Σύνδεσμοι / Περαιτέρω ανάγνωση

Θέλετε να μάθετε περισσότερα για το I2C; Ελέγξτε το το άρθρο μας σχετικά με αυτό εδώ.

3 Σχόλια

  1. Rob στις Ιανουάριος 23, 2023 στις 4:45 μμ

    Σας ευχαριστώ για το χρήσιμο άρθρο! Έχω δύο προτάσεις:

    1) Μια αιτία για το σφάλμα "δεν βρέθηκε pull up" (το οποίο είναι αυτό που με έφερε εδώ) μπορεί επίσης να προκληθεί από ένα χαζό λάθος: ένα χαλαρό καλώδιο! Αυτό μου συμβαίνει πολύ συχνά επειδή χρησιμοποιώ συνδετήρες Stemma οι οποίοι είναι λίγο φινιρισμένοι.

    2) Νομίζω ότι ο κώδικας του παραδείγματός σας είναι λίγο συγκεχυμένος, δεδομένου ότι νομίζω ότι θα μπορούσε/πρέπει να έχει ξεκινήσει με την απλούστερη περίπτωση όπου χρησιμοποιείτε τους δύο διαθέσιμους ελεγκτές I2C, όπως

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

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

    Οι μέθοδοι που χρησιμοποιείτε μετά από αυτό εξακολουθούν φυσικά να ισχύουν και να είναι χρήσιμες!

    /rob

    • raspi berry στις Φεβρουάριος 4, 2023 στις 11:47 πμ

      Σας ευχαριστώ για το προσεγμένο σχόλιό σας, Rob! Τα χαλαρά καλώδια είναι σαν το "έχει ρεύμα;" του κόσμου της υποστήριξης υπολογιστών 🙂 Καλύτερα να το ελέγξετε παρά να περάσετε ώρες με την αποσφαλμάτωση.

  2. Edward M Johnstone στις Ιούνιος 27, 2023 στις 2:43 μμ

    Σας ευχαριστώ για αυτό το άρθρο.
    Μεταβαίνω από το Raspberry Pi Pico και έψαχνα για κάτι τέτοιο!
    Φοβερό!

Αφήστε ένα σχόλιο