ELECTRONICS

I2C, SPI и UART: три способа, которыми модули говорят с Arduino

Кнопка и светодиод обходятся одним проводом: на нём либо есть напряжение, либо нет. Дисплей, гироскоп или модуль часов должны передать числа — а числа одним проводом не передашь. Для этого придуманы интерфейсы: договорённости о том, как по двум-четырём проводам гонять данные. В мире Arduino их три, и у каждого своя ниша.

Коротко: чем они отличаются

I2CSPIUART
Проводов2 сигнальных4 сигнальных2 сигнальных
Устройств на шинеДо 127, по адресамСколько угодно, по проводу выбора на каждоеДва: точка-точка
СкоростьНизкая, 100–400 кбит/сВысокая, мегабитыСредняя, 9600–115200 бит/с
Типичные модулиOLED, часы, гироскоп, расширителиTFT-дисплей, SD-карта, радио nRF24, RFIDBluetooth, GPS, GSM, второй Arduino

I2C: два провода на всех

Линия данных SDA и линия тактов SCL, плюс питание и земля. На одну пару проводов вешается сколько угодно модулей — дисплей, датчик, часы, — и все они различаются адресами. Плата говорит: «адрес 0x3C, прими картинку», и отвечает только дисплей.

Это делает I2C любимым интерфейсом для модулей: четыре провода, и дисплей работает. На UNO и Nano шина живёт на A4 и A5, и пока модуль подключён, эти выводы для другого недоступны. Библиотека — встроенная Wire, но напрямую с ней почти не работают: у каждого модуля своя библиотека поверх.

Если модуль молчит, первым делом узнают его адрес. Скетч ниже перебирает все адреса и печатает, кто отозвался. Пусто — значит, дело в проводах или питании; адрес есть, но не тот, что в примере, — поправьте его в коде.

// Сканер I2C: печатает адреса всех модулей на шине
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
  Serial.println("Ищу устройства на I2C...");
  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      Serial.print("Найден адрес 0x");
      Serial.println(addr, HEX);
    }
  }
  Serial.println("Готово");
}

void loop() {}

SPI: быстро, но проводов больше

Четыре линии: MOSI — от платы к модулю, MISO — обратно, SCK — такты, и SS (он же CS) — выбор устройства. Адресов нет: чтобы обратиться к модулю, плата притягивает его провод выбора к земле. Два модуля — два провода выбора, остальные три линии общие.

Зато скорость в десятки раз выше, чем у I2C. Поэтому всё, что гонит много данных, сидит на SPI: цветные дисплеи, карты памяти, радиомодули. На UNO это выводы 10–13, и вывод 13 совпадает со встроенным светодиодом — он будет мерцать при передаче, это нормально.

Модули SPI чаще всего рассчитаны на 3,3 В: TFT-дисплеи, nRF24L01, RFID RC522. С UNO их питают от вывода 3.3V, а вот сигнальные линии с 5-вольтовых выводов формально им великоваты — обычно терпят, но преобразователь уровней надёжнее.

UART: последовательный порт

Самый старый и самый простой: провод туда, провод обратно, общая земля. Никаких адресов и тактов — обе стороны заранее договариваются о скорости, и если она не совпала, в монитор порта сыплется мусор. Отсюда цифра 9600 в Serial.begin: она должна быть одинаковой с обеих сторон.

По UART плата общается с компьютером через USB — на UNO это выводы 0 и 1, и занимать их модулем нельзя: загрузка скетча перестанет работать. Для Bluetooth или GPS берут любые два других вывода с библиотекой SoftwareSerial, а на Mega и ESP32 есть настоящие дополнительные порты.

Правило, которое путают все: TX платы идёт в RX модуля, и наоборот. Что один передаёт — другой принимает. Соединить одинаковые надписи — значит соединить два выхода, и связи не будет.

Где какой интерфейс на каждой плате

ПлатаI2CSPIUART
Arduino UNOA4 (SDA), A5 (SCL)11 MOSI, 12 MISO, 13 SCK, 10 SS0 RX, 1 TX
Arduino NanoA4, A511, 12, 13, 100, 1
Arduino Mega20 (SDA), 21 (SCL)51 MOSI, 50 MISO, 52 SCK, 53 SS0/1, 19/18, 17/16, 15/14
ESP3221 (SDA), 22 (SCL)23 MOSI, 19 MISO, 18 SCK, 5 SS3 RX, 1 TX и ещё два
ESP8266D2 (SDA), D1 (SCL)D7 MOSI, D6 MISO, D5 SCK, D8 SSRX, TX

На ESP32 это выводы по умолчанию — там любой интерфейс можно перевести на другие выводы в коде. На Arduino выводы жёсткие.

На чём попадаются

Два I2C-модуля с одним адресом

Два одинаковых OLED на одной шине откликаются оба, и оба показывают мусор. На части модулей адрес меняется перемычкой на обороте; если нет — второй вешают на программную шину на других выводах.

Длинные провода на I2C

Шина рассчитана на сантиметры, не метры. Полметра шлейфа — и модуль то виден, то нет. Укоротить провода или снизить скорость шины помогает почти всегда.

Модуль на выводах 0 и 1

Скетч перестал загружаться с ошибкой «not in sync». Bluetooth-модуль на аппаратном порту перебивает загрузчик. Отключите его на время прошивки или переведите на другие выводы.

Скорость не совпала

В мониторе порта иероглифы. Цифра внизу окна монитора и цифра в Serial.begin должны быть одинаковыми. У модулей своя скорость по умолчанию: у Bluetooth HC-05 чаще 38400, у GPS — 9600.

Что дальше

Сканер I2C собран под Arduino UNO перед публикацией