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 имеет достаточно места на флэш-памяти для этого)

Вы можете загрузить эти библиотеки в Adafruit CircuitPython Bundle здесь. (Я скачал adafruit-circuitpython-bundle-7.x-mpy-20211123.zip)

Примечание: для установки этих библиотек просто скопируйте их в папку lib на "диске" CIRCUITPY, который установлен на вашем компьютере. Конечно, вам придется скопировать только библиотеки, а не примеры и другие материалы.

Большим преимуществом использования материалов Adafruit является то, что вы получаете тонну примеров, которые охватывают многие популярные микросхемы, и вы можете очень легко начать с них. Есть такие вещи, как управление картой microSD с помощью SPI, чтение RTC и чтение с датчика BME680.

Тестирование I2C в CircuitPython

У меня следующая установка, поскольку я хочу управлять двумя устройствами независимо друг от друга (которые в данном случае имеют одинаковые адреса):

Feather RP2040 и два Разрывные платы BME688

Обратите внимание, что наши разрывные платы BME688 уже содержат подтяжки для SDA и SCL. (Вам нужны подтяжки на 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). Вам не нужно задавать конфигурацию (какой контроллер вы хотите использовать, или как объединить пины) - об этом позаботятся busio для вас.

Однако вам следует обратить внимание на то, какие контакты вы используете, поскольку контакты будут обеспечивать только один этих шин в каждом случае, и если вы случайно выберете конфликтующие контакты, вы получите ValueError: Используется периферийное устройство I2C .

Если вы хотите использовать "конфликтующие" контакты, например SCL / SDA (которые имеют SCL1 и SDA1) и A0 / A1 (которые также имеют SCL1 и SDA1), вам нужно будет сделать битбанг одного из портов:

Вот как просканировать эту конфигурацию выводов без вызова 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 - см. рисунок распиновки Adafruit Feather RP2040 в этой статье).

В случае если вы хотите использовать тот же набор выводов, вы можете использовать bitbangio для "создания" дополнительного программно-управляемого порта I2C. Недостатком этого является более низкая скорость для этого программного порта I2C, а также более высокая загрузка процессора.

RuntimeError: Не найдена подтяжка на SDA или SCL; проверьте проводку

Если вы получите следующую ошибку

RuntimeError: Не найдена подтяжка на SDA или SCL; проверьте проводку

тогда вам следует установить подтягивающие резисторы между 3V3 на плате (вывод 3.3V) и соответственно вашими выводами SDA и SCL. Они необходимы для нормальной работы I2C (устройства подтягивают контакты I2C для связи, по умолчанию / в состоянии простоя на шине высокий уровень) - и не включены в Adafruit Feather RP2040. Они включены во многие периферийные устройства Adafruit, а также в периферийные устройства других компаний (например, опять же, наши собственные Разрывная плата BME688).

Если вы не знаете, что такое подтягивание: это, по сути, резистор между соответствующим выводом (например, SDA) и выводом питания 3,3 В. Он не должен быть очень точным. Начинать следует с резисторов 10 кОм, если это не работает, возможно, попробуйте резистор 1 кОм для более "жесткой" подтяжки.

TimeoutError: Слишком большой промежуток времени

Проверьте, правильно ли подано питание на микросхему, с которой вы хотите поговорить.

Разные заметки

Ссылки / Ресурсы / Ссылки / Дальнейшее чтение

Хотите узнать больше об I2C? Посмотрите наша статья об этом здесь.

3 комментариев

  1. Rob Январь 23, 2023 в 4:45 пп

    Спасибо за полезную статью! У меня есть два предложения:

    1) Одна из причин ошибки "не найдено подтягивание" (именно это привело меня сюда) также может быть вызвана глупой ошибкой: ослаблением провода! Это случается со мной слишком часто, потому что я использую разъемы Stemma, которые немного хрупкие.

    2) Я думаю, что ваш пример кода немного запутан, в том смысле, что я думаю, что он мог/должен был начаться с самого простого случая, когда вы используете два доступных контроллера I2C, например

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

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

    Методы, которые вы используете после этого, конечно же, остаются актуальными и полезными!

    /rob

    • raspi berry Февраль 4, 2023 в 11:47 дп

      Спасибо за вдумчивый комментарий, Роб! Свободные провода - это как "есть ли питание?" в мире компьютерной поддержки 🙂 Лучше проверить, чем тратить часы на отладку.

  2. Edward M Johnstone Июнь 27, 2023 в 2:43 пп

    Спасибо за эту статью.
    Я перехожу с Raspberry Pi Pico и давно искал что-то подобное!
    Потрясающе!

Комментировать