Инструкции

Шины I2C, SPI и UART: чем отличаются и что выбрать

13 мин чтения·Август 2026

Выводов у микроконтроллера мало, а датчиков хочется много. Шины решают именно эту задачу: по двум-четырём проводам можно поговорить с десятком устройств. Три шины покрывают почти всю любительскую электронику — I2C, SPI и UART. Разбираем, чем они отличаются, какие выводы занимают и что делать, когда датчик не отвечает.

Сравнение

I2CSPIUART
Проводов (кроме питания)24 и по одному на устройство2
Устройств на шине112 адресовсколько хватит выводоводно
Скорость100–400 кГцдесятки МГцобычно до 115 200 бод
Обменпо очередиодновременно в обе стороныодновременно в обе стороны
Синхронизациятактовый проводтактовый проводпо скорости, заранее
Нужны резисторыда, подтяжкинетнет

Практическое правило: I2C — когда устройств много, а скорость не важна. SPI — когда нужна скорость. UART — когда устройство одно и оно «говорит» само.

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

I2C использует всего две линии: SDA для данных и SCL для тактов. Все устройства висят на них параллельно, а различаются адресом — числом от 0x08 до 0x77, зашитым производителем.

#include <Wire.h>

const uint8_t sensorAddress = 0x76;

void setup() {
  Serial.begin(115200);
  Wire.begin();                      // на ESP32 можно указать выводы: Wire.begin(21, 22)

  Wire.beginTransmission(sensorAddress);
  Wire.write(0xD0);                  // адрес регистра внутри датчика
  Wire.endTransmission();

  Wire.requestFrom(sensorAddress, (uint8_t) 1);
  if (Wire.available()) {
    Serial.println(Wire.read(), HEX);
  }
}

void loop() {
}

Схема обмена почти всегда одинакова: открыть передачу на адрес, написать номер регистра, закрыть передачу, запросить нужное число байтов, прочитать.

Выводы I2C

ПлатаSDASCL
Arduino UNO, NanoA4A5
Arduino Mega 25602021
Классический ESP32 (по умолчанию в Arduino)GPIO 21GPIO 22
ℹ️

У ESP32 аппаратных выводов I2C нет вообще: контроллер маршрутизируется через коммутационную матрицу на любые свободные пины. Пара 21 и 22 — это значение по умолчанию ядра Arduino, а не свойство чипа. Задать свои можно прямо в begin(): Wire.begin(18, 19). Какие выводы свободны — в статье Пины ESP32.

Подтягивающие резисторы

Это то, на чём чаще всего спотыкаются. Линии I2C устроены так, что устройства умеют только «прижимать» их к земле, а поднимать обратно к питанию должны резисторы — обычно 4,7 кОм на каждую линию.

Без них шина не работает вовсе. Хорошая новость: почти на всех готовых модулях с AliExpress эти резисторы уже стоят. Плохая: если повесить на шину пять таких модулей, пять пар резисторов окажутся включены параллельно, суммарное сопротивление упадёт до килоома, и шина начнёт сбоить. При большом числе устройств лишние подтяжки выпаивают, оставляя одну пару.

Сканер адресов

Первое, что стоит запустить при любой проблеме с I2C, — сканер. Он перебирает все адреса и печатает те, что ответили:

#include <Wire.h>

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Serial.println("Сканирую шину I2C…");

  uint8_t found = 0;
  for (uint8_t address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    if (Wire.endTransmission() == 0) {
      Serial.print("Найдено устройство: 0x");
      if (address < 16) Serial.print("0");
      Serial.println(address, HEX);
      found++;
    }
  }

  if (found == 0) {
    Serial.println("Ничего не найдено — проверьте провода и подтяжки");
  }
}

void loop() {
}

Если сканер молчит — проблема в проводах, питании или подтяжках, и лезть в код датчика бессмысленно. Если адрес нашёлся, но не тот, что в документации, — почти наверняка дело в семибитной адресации, см. ниже.

⚠️

Библиотека Wire работает с семибитными адресами, а в даташитах датчиков часто приводят восьмибитный вариант — тот же адрес, сдвинутый влево на один бит, и обычно два числа: для чтения и для записи. Если в документации 0xEC и 0xED, а сканер нашёл 0x76 — это одно и то же устройство. Делите на два.

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

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

#include <SPI.h>

const uint8_t chipSelectPin = 5;

void setup() {
  pinMode(chipSelectPin, OUTPUT);
  digitalWrite(chipSelectPin, HIGH);   // не выбрано
  SPI.begin();
}

uint8_t readRegister(uint8_t address) {
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  digitalWrite(chipSelectPin, LOW);

  SPI.transfer(address | 0x80);        // старший бит — признак чтения у многих чипов
  uint8_t value = SPI.transfer(0x00);  // отправляем пустышку, получаем ответ

  digitalWrite(chipSelectPin, HIGH);
  SPI.endTransaction();
  return value;
}

void loop() {
}

SPISettings задаёт три параметра: скорость в герцах, порядок битов и режим. Режимы SPI_MODE0SPI_MODE3 отличаются полярностью и фазой тактового сигнала — нужный указан в даташите устройства. Если данные приходят как мусор, а провода точно верные, первым делом пробуют сменить режим.

Выводы SPI

ПлатаMOSIMISOSCKCS по умолчанию
Arduino UNO, Nano11121310
Arduino Mega 256051505253
Классический ESP32, шина VSPIGPIO 23GPIO 19GPIO 18GPIO 5
Классический ESP32, шина HSPIGPIO 13GPIO 12GPIO 14GPIO 15

У классического ESP32 две свободные шины SPI, и их выводы, как и у I2C, переназначаются:

SPI.begin(18, 19, 23, 5);   // SCK, MISO, MOSI, CS

Вывод CS библиотека не трогает — им управляют вручную через digitalWrite(). Именно поэтому в примере выше он поднимается и опускается явно.

UART: один на один

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

Из-за отсутствия адресации на одной паре проводов живёт ровно одно устройство. Зато подключение предельно простое, и так работает большинство GPS-модулей, Bluetooth-модулей и датчиков качества воздуха.

У классического ESP32 свободны Serial1 и Serial2, выводы задаются в begin():

Serial2.begin(9600, SERIAL_8N1, 16, 17);   // RX на GPIO 16, TX на GPIO 17

Подробный разбор — в отдельной статье про Serial-порт.

Как выбрать

Берите I2C, если устройств несколько, а данных мало: датчики температуры, давления, освещённости, часы реального времени, небольшие OLED-дисплеи, расширители портов. Два провода на всё — главный аргумент.

Берите SPI, если данных много: цветные дисплеи, карты памяти, радиомодули, быстрые АЦП. Разница в скорости не косметическая: обновление TFT-дисплея по I2C займёт секунды, по SPI — десятки миллисекунд.

UART достаётся без выбора — если модуль умеет только его, вопрос закрыт.

Комбинировать шины можно свободно: дисплей по SPI, датчики по I2C и GPS по UART одновременно — обычная ситуация, они друг другу не мешают.

Частые проблемы

Сканер I2C ничего не находит. Проверьте питание модуля (3,3 В или 5 В — смотрите документацию), не перепутаны ли SDA и SCL, и есть ли подтяжки. На ESP32 убедитесь, что выбранные выводы вообще свободны.

Адрес не совпадает с даташитом. Семь бит против восьми, см. врезку выше.

Устройство работало одно, перестало в паре. Совпали адреса. У многих модулей есть перемычка или площадка для смены адреса — обычно она меняет младший бит.

SPI отдаёт мусор. Неверный режим SPI_MODE, слишком высокая скорость или длинные провода. Начните с 1 МГц и SPI_MODE0, потом ускоряйтесь.

Всё работает на макетке и сбоит в корпусе. Длина линий. I2C рассчитан на сантиметры, а не метры: на длинных проводах растёт ёмкость, фронты заваливаются. Для расстояний берите UART с преобразователем в RS-485.

Смешались уровни 3,3 и 5 вольт. ESP32 работает от 3,3 В, и подача пяти вольт на его вывод может повредить чип. Пятивольтовые модули подключают через преобразователь уровней.

Что дальше

Связанные статьи