Сканер шины I2C
Intro
I2C — это такая шина данных, широко используемая в микроэлектронике. С помощью I2C можно подключить к микроконтроллеру много разных полезных модулей, включая датчики, дисплеи, АЦП и даже другие микроконтроллеры.
Шина является адресной, так что каждое подключаемое устройство имеет свой адрес в диапазоне от 1 до 126 (0x01 — 0x7E). На одной шине не может быть два устройства с одинаковыми адресами.
В этой короткой статье мы победим одну из частых проблем, которые испытывают DIY-энтузиасты про покупке неведомого модуля с этим самым I2C интерфейсом. Проблема состоит в том, что производитель может изменить адрес устройства, и стандартные библиотеки перестают работать. Часто с подобной неприятностью можно столкнуться, например, при подключении ЖК дисплея с интерфейсом I2C.
Подтяжка I2C
Для правильной работы шины I2C необходимо, чтобы линии SDA и SCL были подтянуты к питанию с помощью резистора соответствующего номинала. Эту подтяжку нужно сделать всего один раз на всей линии I2C.
Важно
Для устройств с 5-Вольтовой логикой подтягивать следует резисторами 10кОм. Для логики с напряжением 3,3В подойдут резисторы 2,2кОм.
Arduino IDE
Суть программы для сканирования шины I2C сводится к следующему. Чтобы понять, какой адрес у модуля, подключенного в данный момент к шине, нужно просто «постучаться» по каждому из 126 возможных адресов. Если кто-то ответит, значит адрес рабочий!
В среде Arduino IDE за работу с шиной I2C отвечает библиотека Wire, которая имеет свои вариации для разных плат: Arduino, ESP32, RP2040.
Инициализация шины осуществляется вызовом функции Wire.begin. При использовании стандартных контактов I2C (например, для Arduino Uno R3: SDA-A4, SCL-A5) функция вызывается без аргументов. В противном случае, передаём туда два аргумента: SDA и SCL.
#include <Wire.h>
void setup() {
Wire.begin(); # при использовании стандартных контактов SDA и SCL
//Wire.begin(SDA_PIN, SCL_PIN); # при использовании нестандартных контактов SDA и SCL
Serial.begin(115200);
while (!Serial); // для некоторых устройств нужно дождаться Serial
Serial.println("\nI2C Scanner");
}
void loop() {
int nDevices = 0;
Serial.println("Scanning...");
for (byte address = 1; address < 127; ++address) {
Wire.beginTransmission(address);
byte error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address < 16) {
Serial.print("0");
}
Serial.print(address, HEX);
Serial.println(" !");
++nDevices;
} else if (error == 4) {
Serial.print("Unknown error at address 0x");
if (address < 16) {
Serial.print("0");
}
Serial.println(address, HEX);
}
}
if (nDevices == 0) {
Serial.println("No I2C devices found\n");
} else {
Serial.println("done\n");
}
delay(5000); // Wait 5 seconds for next scan
}
Загружаем программу на плату, подключаем тестируемое устройство к I2C шине и открываем монитор последовательного порта. Если все подключено правильно, то появится примерно такой отчет:
Примечание
Это значит, что мой дисплей имеет адрес 0x27 в шестнадцатеричной системе счисления. Теперь можно смело подключить библиотеку для работы с такими дисплеями и указать в ней данный адрес.
CircuitPython
Суть программы для сканирования шины I2C сводится к следующему. Чтобы понять, какой адрес у модуля, подключенного в данный момент к шине, нужно просто «постучаться» по каждому из 126 возможных адресов. Если кто-то ответит, значит адрес рабочий!
В CircuitPython за работу с шиной I2C отвечает библиотека busio. Однако, при использовании стандартных контактов достаточно получить объект шины из библиотеки board:
i2c = board.I2C()
Если же планируется использовать нестандартные контакты, следует использовать busio:
i2c = busio.I2C(board.48, board.47)
import time
import board
# при использовании стандартных контактов SDA и SCL
i2c = board.I2C()
# при использовании нестандартных контактов SDA и SCL
# import busio
# i2c = busio.I2C(board.48, board.47)
while not i2c.try_lock():
pass
try:
while True:
print(
"I2C addresses found:",
[hex(device_address) for device_address in i2c.scan()],
)
time.sleep(2)
finally: # unlock the i2c bus when ctrl-c'ing out of the loop
i2c.unlock()