Основы · 10 мин чтения

Что такое SPI? Пины MOSI, MISO, SCK и CS в Arduino простыми словами

Что такое SPI и как он работает? Пины MOSI, MISO, SCK и CS, режимы и скорость SPI, несколько устройств, пины Uno, Mega, ESP32 и Pico, конфликт SD-карты и RFID.

SPI (Serial Peripheral Interface, последовательный периферийный интерфейс) — это способ связи, который позволяет микроконтроллеру очень быстро обмениваться данными по четырём проводам с такими устройствами, как SD-карты, RFID-считыватели, цветные TFT-дисплеи или беспроводные модули. Есть линия тактового сигнала (SCK), две отдельные линии данных для каждого направления (MOSI и MISO) и линия CS, которая выбирает, с каким устройством идёт обмен. SPI может быть в десятки раз быстрее I2C, поэтому именно его выбирают для модулей, передающих большие объёмы данных.

В этой статье разберём, как работает SPI, названия пинов и новую систему обозначений, четыре режима SPI, ограничения скорости, пины на Arduino Uno, Mega, ESP32 (VSPI и HSPI) и Raspberry Pi Pico, как подключить несколько устройств к одной шине и почему SD-карта и RFID-считыватель RC522 вместе создают проблемы. В последнем разделе сравним SPI с I2C и UART.

Что такое SPI и как он работает?

В SPI всегда есть один контроллер (раньше его называли master, ведущий) и одно или несколько периферийных устройств (раньше — slave, ведомые). Тактовый сигнал формирует только контроллер: то есть Arduino начинает обмен, задаёт его скорость и завершает его. На каждом тактовом импульсе контроллер отправляет один бит по MOSI, а периферийное устройство в тот же момент отправляет один бит обратно по MISO. Сдвиговые регистры на обеих сторонах образуют своего рода кольцо: после 8 тактовых импульсов байт контроллера оказывается в периферийном устройстве, а байт устройства — в контроллере. Поэтому SPI работает в режиме полного дуплекса: чтение и запись происходят одновременно.

ЛинияСтарое названиеНовое названиеНаправлениеНазначение
SCK / SCLKSerial ClockSCKКонтроллер → устройствоТактовый сигнал; на каждом импульсе передаётся один бит
MOSIMaster Out Slave InCOPI / SDOКонтроллер → устройствоДанные от Arduino к модулю
MISOMaster In Slave OutCIPO / SDIУстройство → контроллерДанные от модуля обратно к Arduino
CS / SSSlave SelectCS (Chip Select, выбор микросхемы)Контроллер → устройствоПока линия в LOW, модуль слушает; у каждого устройства свой CS

Надписи на модулях не всегда одинаковы, и это главный источник путаницы. На RFID-модуле RC522 линия CS подписана SDA (к SDA шины I2C она не имеет никакого отношения), на nRF24L01 — CSN, на MCP3008 — CS/SHDN, а на некоторых дисплеях встречаются названия DIN (MOSI), CLK (SCK) и DOUT (MISO). Правило простое: вход данных (DIN, SDI, MOSI) идёт к MOSI Arduino, а выход данных (DOUT, SDO, MISO) — к MISO Arduino.

  1. Контроллер притягивает к LOW линию CS того устройства, с которым хочет говорить. Все остальные устройства держат свой CS в HIGH и освобождают линию MISO.
  2. Контроллер начинает формировать тактовые импульсы на SCK.
  3. На каждом импульсе один бит уходит по MOSI и один бит приходит по MISO. Порядок битов обычно — старший бит первым (MSB first).
  4. Когда нужное количество байтов (команда, адрес, данные) передано, контроллер возвращает CS в HIGH, и устройство завершает операцию.

Режимы SPI: CPOL и CPHA

Единого стандарта у SPI нет: устройства различаются уровнем, на котором тактовый сигнал находится в покое (CPOL, полярность тактового сигнала), и фронтом, по которому читаются данные (CPHA, фаза тактового сигнала). Четыре сочетания этих двух настроек и дают четыре режима SPI. Если контроллер и устройство работают в разных режимах, данные сдвигаются на один бит, и читаются бессмысленные значения. К счастью, подавляющее большинство модулей использует режим 0 (Mode 0).

РежимCPOLCPHAТактовый сигнал в покоеФронт чтения данныхПримеры устройств
SPI_MODE000LOWНарастающийSD-карта, RC522, nRF24L01, MAX7219, MCP3008, 74HC595
SPI_MODE101LOWСпадающийНекоторые АЦП и микросхемы драйверов
SPI_MODE210HIGHСпадающийИспользуется редко
SPI_MODE311HIGHНарастающийАкселерометр ADXL345, некоторые дисплеи ST7789 без пина CS
Если модуль каждый раз возвращает 0xFF, 0x00 или разные бессмысленные значения, сначала проверь режим SPI и максимальную тактовую частоту в даташите. Снизь скорость до 1 MHz и попробуй ещё раз: так быстро станет ясно, в чём проблема — в подключении или в настройках.

Скорость SPI и длина проводов

В SPI скорость задаёт контроллер, а ограничение обычно накладывает само устройство. Arduino Uno может работать до 8 MHz — половины своей тактовой частоты 16 MHz; по умолчанию библиотека SPI использует 4 MHz. ESP32 на собственных пинах SPI (IO_MUX) выдаёт до 80 MHz, а при выводе сигнала на другие пины через матрицу GPIO предел снижается. Raspberry Pi Pico при системной частоте 125 MHz достигает примерно 62,5 MHz. Быстрым сигналам SPI нужны короткие провода: на макетной плате с перемычками 10–20 см несколько MHz — не проблема; на 20–30 MHz и выше держи провода короткими и по возможности используй пайку. Длина в несколько метров для SPI не подходит.

УстройствоНазначениеМаксимальная частота SPI (даташит)Режим SPI
Карта microSDЗапись данных400 kHz при запуске, затем до 25 MHzРежим 0
RC522 (MFRC522)RFID-считыватель10 MHzРежим 0
nRF24L01Беспроводная связь 2,4 GHz10 MHzРежим 0
MAX7219Светодиодная матрица / 7-сегментный индикатор10 MHzРежим 0
MCP30088-канальный 10-битный АЦП3,6 MHz при 5 V, 1,35 MHz при 2,7 VРежим 0 или 3
ILI9341Цветной TFT 2,4–3,2″10 MHz на запись (на практике гоняют гораздо быстрее)Режим 0

Пины SPI: Arduino Uno, Mega, ESP32 и Raspberry Pi Pico

ЛинияArduino Uno / NanoArduino MegaESP32 VSPI (по умолчанию)ESP32 HSPIRaspberry Pi Pico SPI0
SCKD13D52GPIO 18GPIO 14GP18
MOSID11D51GPIO 23GPIO 13GP19 (TX)
MISOD12D50GPIO 19GPIO 12GP16 (RX)
CS (по умолчанию)D10D53GPIO 5GPIO 15GP17
Логический уровень5 V5 V3,3 V3,3 V3,3 V

На Uno те же линии SPI выведены и на 6-контактный разъём ICSP у края платы; на таких платах, как Leonardo и Micro, SPI доступен только через ICSP. D10 не обязателен для CS: подойдёт любой цифровой пин. Однако на Uno не оставляй D10 входом: если аппаратный SPI прочитает LOW на D10, пока тот настроен как вход, он выйдет из режима контроллера. SPI.begin() автоматически делает этот пин выходом, и переводить его во вход для другой задачи не стоит. На ESP32 объект Arduino SPI по умолчанию использует VSPI; благодаря матрице GPIO SPI можно перенести почти на любые пины. На Pico SPI0 и SPI1 можно назначить на несколько разных групп пинов (например, GP10, GP11, GP12 для SPI1); подробности — в утилите распиновки.

Сами RC522, nRF24L01 и SD-карты — устройства на 3,3 V. Прямое подключение к сигналам 5 V платы Uno со временем может их повредить. В большинстве готовых модулей SD-карт есть преобразователь уровней; RC522 и nRF24L01 питай от пина 3V3, а на сигнальных линиях подумай о преобразователе логических уровней. ESP32 и Pico и так работают от 3,3 V, поэтому совместимы с этими устройствами напрямую.

Библиотека SPI для Arduino: основные функции

ФункцияЧто делает?
SPI.begin()Запускает аппаратный SPI; делает пины SCK, MOSI и SS выходами.
SPISettings(скорость, MSBFIRST, SPI_MODE0)Хранит скорость, порядок битов и режим устройства в одном объекте.
SPI.beginTransaction(настройки)Применяет настройки и резервирует шину для этого устройства; делает безопасным переключение между устройствами с разными настройками.
SPI.transfer(байт)Отправляет один байт и одновременно возвращает принятый байт.
SPI.transfer16(значение)Отправляет и принимает 16-битное значение за один раз.
SPI.transfer(буфер, длина)Отправляет массив; принятые байты записываются поверх того же массива.
SPI.endTransaction()Освобождает шину; вызывается после установки CS в HIGH.

Первый пример: отправка данных в 74HC595 по SPI

Проще всего понять SPI на сдвиговом регистре 74HC595, ведь 595-й — по сути самое простое периферийное устройство SPI, без MISO. Пин MOSI Arduino идёт на вход DS микросхемы 595, SCK — на SH_CP, а D10 — на ST_CP, который работает как защёлка. В отличие от функции shiftOut(), аппаратный SPI отправляет байт за несколько микросекунд:

C++ (Arduino IDE)
1// Donanım SPI ile 74HC595: 8 LED'i iki kabloyla sürmek / 74HC595 over hardware SPI: 8 LEDs from two data wires
2// D11 (MOSI) -> DS (14), D13 (SCK) -> SH_CP (11), D10 -> ST_CP / latch (12)
3#include <SPI.h>
4
5const int LATCH_PIN = 10;     // 595'te CS gibi davranır / acts like CS on the 595
6
7void write595(byte value) {
8  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));  // 4 MHz, mod 0 / 4 MHz, mode 0
9  digitalWrite(LATCH_PIN, LOW);
10  SPI.transfer(value);                 // 8 biti gönder / shift out 8 bits
11  digitalWrite(LATCH_PIN, HIGH);       // yükselen kenarda çıkışlara aktar / latch on the rising edge
12  SPI.endTransaction();
13}
14
15void setup() {
16  pinMode(LATCH_PIN, OUTPUT);
17  digitalWrite(LATCH_PIN, HIGH);
18  SPI.begin();
19}
20
21void loop() {
22  for (int i = 0; i < 8; i++) {        // yürüyen ışık / running light
23    write595(1 << i);
24    delay(100);
25  }
26}

Чтение данных по SPI: АЦП MCP3008

Чтобы увидеть обмен в режиме полного дуплекса, возьмём MCP3008. Эта микросхема добавляет Arduino ещё 8 аналоговых входов с разрешением 10 бит; она хорошая альтернатива нелинейному АЦП ESP32 или всего 3 аналоговым входам Pico. Основы аналогового чтения разобраны в статье Что такое АЦП?. Подключение: CLK → D13, DOUT → D12, DIN → D11, CS → D10, VDD и VREF → 5V, AGND и DGND → GND.

C++ (Arduino IDE)
1// MCP3008 (8 kanal, 10 bit ADC) okuma / Reading an MCP3008 (8-channel 10-bit ADC)
2// D13 -> CLK, D12 <- DOUT, D11 -> DIN, D10 -> CS/SHDN
3#include <SPI.h>
4
5const int CS_PIN = 10;
6SPISettings mcpSettings(1000000, MSBFIRST, SPI_MODE0);   // 1 MHz, mod 0 / 1 MHz, mode 0
7
8int readMCP3008(byte channel) {
9  SPI.beginTransaction(mcpSettings);
10  digitalWrite(CS_PIN, LOW);                   // cihazı seç / select the chip
11  SPI.transfer(0x01);                          // başlangıç biti / start bit
12  byte high = SPI.transfer(0x80 | (channel << 4));   // tek uçlu + kanal / single-ended + channel
13  byte low = SPI.transfer(0x00);               // kalan 8 bit / remaining 8 bits
14  digitalWrite(CS_PIN, HIGH);                  // seçimi bırak / release the chip
15  SPI.endTransaction();
16  return ((high & 0x03) << 8) | low;           // 10 bitlik sonuç / 10-bit result
17}
18
19void setup() {
20  Serial.begin(9600);
21  pinMode(CS_PIN, OUTPUT);
22  digitalWrite(CS_PIN, HIGH);                  // başta seçili değil / not selected at start
23  SPI.begin();
24}
25
26void loop() {
27  int raw = readMCP3008(0);
28  float volts = raw * 5.0 / 1023.0;
29  Serial.print("CH0: ");
30  Serial.print(raw);
31  Serial.print("  ");
32  Serial.print(volts, 2);
33  Serial.println(" V");
34  delay(500);
35}

Здесь мы отправляем три байта и одновременно получаем три байта. Первый байт (0x01) — это только стартовый бит; второй байт несёт несимметричный режим (single-ended) и номер канала, а MCP3008 в ответ присылает старшие 2 бита результата. Третий байт — 0x00; его содержимое не важно, он отправляется только для того, чтобы сформировать тактовые импульсы и получить оставшиеся 8 бит результата. В SPI «читать» всегда значит что-то отправлять. Результат лежит в диапазоне от 0 до 1023 и переводится в вольты относительно VREF (здесь 5 V).

Несколько устройств SPI: отдельные линии CS

Главное отличие SPI от I2C — отсутствие адресов. Устройства делят линии SCK, MOSI и MISO, и каждое получает от Arduino свой провод CS. Слушает только устройство, у которого CS в LOW, и только оно выдаёт данные на MISO. Трём устройствам нужно 3 + 3 = 6 проводов, пяти — 3 + 5 = 8; число пинов растёт вместе с числом устройств. Некоторые микросхемы, например 74HC595 и MAX7219, можно вместо этого соединить цепочкой: выход одной идёт на вход следующей, и все они управляются одним CS.

  • Одновременно в LOW должен быть только один CS. Если выбраны сразу два устройства, они сталкиваются на линии MISO, и данные от обоих приходят испорченными.
  • В setup() сначала сделай каждый пин CS выходом и выставь HIGH, а уже потом запускай библиотеки. «Висящий» пин CS может случайно оказаться в LOW и захватить шину.
  • Используй beginTransaction для каждого устройства. SD-карте может понадобиться 25 MHz, а MCP3008 — 1 MHz; транзакции при каждом переключении автоматически применяют нужные скорость и режим.
  • Если SPI используется внутри прерываний, сообщи об этом библиотеке через SPI.usingInterrupt() или не выполняй работу с SPI в прерывании.
  • Подтягивающий резистор 10 kΩ на линии CS каждого устройства не даёт устройствам выбирать самих себя, пока Arduino загружается или перезагружается.

SD-карта и RFID на одной шине: почему не работает?

Классический вопрос на форумах по Arduino: RFID RC522 по отдельности работает, модуль SD-карты по отдельности работает, но когда оба сидят на одной шине SPI, один из них или оба замолкают. Даже если пины CS в программе указаны правильно, проблема обычно в железе. Во многих дешёвых модулях SD-карт стоит буферная микросхема 74LVC125, которая преобразует сигналы 5 V в 3,3 V, и канал этой микросхемы, управляющий линией MISO, всегда включён. Поэтому даже когда CS SD-карты в HIGH, модуль не отпускает MISO; когда RC522 пытается ответить, за линию борются два выхода.

  • Выбери правильный модуль: модули, которые переводят MISO в высокоимпедансное состояние при CS в HIGH, делят шину без проблем. Отпускает ли модуль линию, можно проверить так: при CS в HIGH подтяни MISO через 10 kΩ сначала к GND, затем к 5 V и измерь напряжение мультиметром.
  • Привяжи вход разрешения буфера к CS: если с пайкой проблем нет, отпаяй от GND вывод OE канала MISO микросхемы 74LVC125 и подключи его к линии CS модуля — это решит проблему навсегда.
  • Добавь последовательный резистор на MISO: резистор около 1 kΩ последовательно с выходом MISO модуля SD позволяет RC522 «перетянуть» линию. Это простое временное решение, но оно ограничивает скорость.
  • Используй отдельную шину SPI: на ESP32, если подключить SD-карту к VSPI, а RC522 — к HSPI, два устройства никогда не пересекаются. У Uno второго аппаратного SPI нет, так что там этот вариант недоступен.
  • Следи за порядком запуска: сначала выставь все пины CS в HIGH, затем вызови SPI.begin(), потом SD.begin() и PCD_Init(). Пример ниже следует этому порядку.
C++ (Arduino IDE)
1// Aynı SPI hattında SD kart ve RC522 RFID / SD card and RC522 RFID on the same SPI bus
2// Ortak / shared: D11 MOSI, D12 MISO, D13 SCK. CS: SD -> D4, RC522 SDA(SS) -> D10, RC522 RST -> D9
3#include <SPI.h>
4#include <SD.h>
5#include <MFRC522.h>
6
7const int SD_CS = 4;
8const int RFID_CS = 10;
9const int RFID_RST = 9;
10
11MFRC522 rfid(RFID_CS, RFID_RST);
12
13void setup() {
14  Serial.begin(9600);
15  // Önce TÜM CS pinlerini HIGH yap: hiçbir cihaz hattı meşgul etmesin
16  // First drive ALL CS pins HIGH so no device grabs the bus
17  pinMode(SD_CS, OUTPUT);
18  digitalWrite(SD_CS, HIGH);
19  pinMode(RFID_CS, OUTPUT);
20  digitalWrite(RFID_CS, HIGH);
21
22  SPI.begin();
23  if (!SD.begin(SD_CS)) {
24    Serial.println(F("SD bulunamadi / SD not found"));
25  }
26  rfid.PCD_Init();
27  Serial.println(F("Karti okut / Scan a card"));
28}
29
30void loop() {
31  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
32
33  String uid = "";
34  for (byte i = 0; i < rfid.uid.size; i++) {
35    if (rfid.uid.uidByte[i] < 0x10) uid += "0";
36    uid += String(rfid.uid.uidByte[i], HEX);
37  }
38  rfid.PICC_HaltA();
39
40  File f = SD.open("kartlar.txt", FILE_WRITE);   // kütüphane CS'yi kendisi yönetir / the library handles CS itself
41  if (f) {
42    f.print(millis());
43    f.print(";");
44    f.println(uid);
45    f.close();
46    Serial.println("Kaydedildi / Saved: " + uid);
47  } else {
48    Serial.println(F("Dosya acilamadi / File open failed"));
49  }
50}

В этом коде библиотеки SD и MFRC522 сами переключают свои пины CS в LOW и HIGH; от тебя требуется только оставить оба в HIGH при старте и убедиться, что железо правильно делит MISO. Код записывает UID каждой считанной карты вместе с отметкой времени в файл kartlar.txt. На Uno эти две библиотеки вместе занимают около половины ОЗУ; макрос F() освобождает место, храня постоянные строки во флеш-памяти.

VSPI и HSPI на ESP32: две отдельные шины SPI

Внутри ESP32 четыре блока SPI; SPI0 и SPI1 заняты под встроенную флеш-память, так что тебе остаются VSPI (SPI3) и HSPI (SPI2). Использовать обе — самый чистый способ развести устройства с сильно разными скоростями и режимами или уйти от описанного выше конфликта на MISO. В ядре Arduino для второй шины создаётся объект SPIClass, а пины ему передаются через begin(). Код ниже запускает SD-карту на VSPI по умолчанию, а MCP3008 — на HSPI, и каждую секунду дописывает показание в CSV-файл:

C++ (Arduino IDE)
1// ESP32: iki ayrı SPI hattı / two separate SPI buses
2// VSPI (varsayılan SPI / default SPI): SD kart / SD card -> SCK 18, MISO 19, MOSI 23, CS 5
3// HSPI: MCP3008 -> SCK 14, MISO 12, MOSI 13, CS 15
4#include <SPI.h>
5#include <SD.h>
6
7const int SD_CS = 5;
8const int ADC_CS = 15;
9
10SPIClass hspi(HSPI);                              // ikinci donanım SPI / second hardware SPI
11SPISettings mcpSettings(1000000, MSBFIRST, SPI_MODE0);
12
13int readMCP3008(byte channel) {
14  hspi.beginTransaction(mcpSettings);
15  digitalWrite(ADC_CS, LOW);
16  hspi.transfer(0x01);
17  byte high = hspi.transfer(0x80 | (channel << 4));
18  byte low = hspi.transfer(0x00);
19  digitalWrite(ADC_CS, HIGH);
20  hspi.endTransaction();
21  return ((high & 0x03) << 8) | low;
22}
23
24void setup() {
25  Serial.begin(115200);
26  pinMode(ADC_CS, OUTPUT);
27  digitalWrite(ADC_CS, HIGH);
28  hspi.begin(14, 12, 13, ADC_CS);                 // SCK, MISO, MOSI, SS
29
30  if (!SD.begin(SD_CS)) {                         // SD varsayılan VSPI'yi kullanır / SD uses the default VSPI
31    Serial.println("SD bulunamadi / SD not found");
32  }
33}
34
35void loop() {
36  int raw = readMCP3008(0);
37  File f = SD.open("/olcum.csv", FILE_APPEND);    // ekle kipinde aç / open in append mode
38  if (f) {
39    f.printf("%lu,%d\n", millis(), raw);
40    f.close();
41  }
42  Serial.printf("CH0: %d\n", raw);
43  delay(1000);
44}
GPIO 12, пин MISO шины HSPI, — один из пинов конфигурации при старте (strapping) ESP32: если при загрузке на нём HIGH, плата решает, что флеш-память работает от 1,8 V, и не запускается. Не подключай к HSPI модули с подтягивающим резистором на MISO (как большинство модулей SD-карт) или перенеси MISO на другой пин. Именно поэтому в этом примере SD-карта сидит на VSPI.

SPI на Raspberry Pi Pico (MicroPython)

У Pico два аппаратных блока SPI (SPI0 и SPI1); в MicroPython с ними работают через класс machine.SPI. Параметры polarity и phase соответствуют CPOL и CPHA; если оба равны 0, это режим 0. CS управляется вручную, как обычный выходной пин. Функция write_readinto() лучше всего показывает полнодуплексную природу SPI: отправляя один массив, она записывает приходящие байты в другой. Прочитаем тот же MCP3008, на этот раз с питанием от 3,3 V:

MicroPython
1# Raspberry Pi Pico: SPI0 ile MCP3008 okuma / reading an MCP3008 on SPI0
2# GP18 -> CLK, GP19 -> DIN (MOSI), GP16 <- DOUT (MISO), GP17 -> CS. MCP3008 VDD ve VREF 3V3 / VDD and VREF on 3V3
3from machine import Pin, SPI
4import time
5
6spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0,   # mod 0 / mode 0
7          sck=Pin(18), mosi=Pin(19), miso=Pin(16))
8cs = Pin(17, Pin.OUT, value=1)                          # başta seçili değil / not selected at start
9
10def read_mcp3008(channel):
11    tx = bytearray([0x01, 0x80 | (channel << 4), 0x00])
12    rx = bytearray(3)
13    cs.value(0)                                          # cihazı seç / select the chip
14    spi.write_readinto(tx, rx)                           # aynı anda gönder ve al / send and receive at once
15    cs.value(1)
16    return ((rx[1] & 0x03) << 8) | rx[2]
17
18while True:
19    raw = read_mcp3008(0)
20    print("CH0:", raw, "{:.2f} V".format(raw * 3.3 / 1023))
21    time.sleep(0.5)

SPI, I2C и UART: сравнение

ХарактеристикаSPII2CUART
Количество проводов4 (+1 CS на каждое дополнительное устройство)2 (SDA, SCL)2 (TX, RX)
Типичная скорость1–80 MHz100 kHz, 400 kHz, 1 MHz9600–115200 бод (до нескольких Мбит/с)
Тактовый сигналЕсть (формирует контроллер)Есть (формирует контроллер)Нет; обе стороны договариваются об одной скорости в бодах
НаправлениеПолный дуплексПолудуплексПолный дуплекс
Выбор устройстваОтдельный провод CS7-битный адресТолько два устройства (точка — точка)
Дополнительные деталиНе нужныПодтягивающие резисторыНе нужны
РасстояниеКороткое (на плате, несколько десятков см)Короткое (около 1 м)Несколько метров; с RS-485 намного больше
Типичные модулиSD-карта, RC522, TFT, nRF24L01, MAX7219OLED, I2C-переходник для LCD, RTC, BME280, MPU6050GPS, HC-05, SIM800L, ESP-01, компьютер

Обычно выбор делает сам модуль, но в собственных разработках помогает такое правило: если важны скорость и объём данных (изображения на дисплее, файлы, звук), бери SPI; если нужны мало проводов и много медленных датчиков — I2C; если нужна простая текстовая связь между двумя устройствами — UART. Подробно об UART — в статье Последовательная связь UART. Все три в одном проекте — тоже обычное дело: например, регистратор данных, который читает датчики по I2C, пишет на SD-карту по SPI и получает координаты от GPS по UART.

Модули с SPI и идеи проектов

  • Дверной замок по карте и журнал доступа: читай карту с помощью RC522 и записывай дату и UID на SD-карту.
  • Регистратор температуры: записывай данные датчика на карту microSD в формате CSV и строй график в Excel.
  • Табло с бегущей строкой: выводи время и сообщения на соединённые цепочкой модули светодиодной матрицы MAX7219.
  • Цветной интерфейс: рисуй графику и меню на TFT-дисплее ILI9341; сенсорную панель тоже подключи к той же шине SPI.
  • Беспроводной пульт: отправляй команды своей машинке-роботу с помощью двух модулей nRF24L01.
  • Многоканальные измерения: на MCP3008 собери MIDI-контроллер с 8 потенциометрами или систему полива с 8 датчиками влажности почвы.

Частые ошибки и как их исправить

СимптомВероятная причинаРешение
Всегда читается 0xFF или 0x00MISO не подключён, MOSI и MISO перепутаны или у устройства нет питанияПодключи DIN к MOSI, а DOUT к MISO; измерь VCC устройства
Значения бессмысленные или сдвинуты на один битНеверный режим SPI или слишком высокая для устройства тактовая частотаИспользуй режим из даташита, снизь скорость до 1 MHz и попробуй ещё раз
Два устройства по отдельности работают, а вместе — нетОдин модуль не отпускает MISO или пины CS при старте не в HIGHСначала выставь все пины CS в HIGH; замени модуль, который не отпускает MISO, или используй отдельную шину SPI
SPI на Uno внезапно перестаёт работатьD10 использовался как вход и был притянут к LOWОставь D10 выходом; его можно использовать как CS
ESP32 не загружается с подключённым HSPIGPIO 12 при загрузке удерживается в HIGHПеренеси подтянутый MISO на VSPI или выбери для MISO другой пин
Случайные ошибки на длинных проводахОтражения и помехи на быстром сигналеУкороти провода, снизь скорость, проложи провод GND рядом с сигнальными
Чтобы увидеть, что происходит на шине SPI, очень помогает дешёвый 8-канальный логический анализатор: он одновременно записывает SCK, MOSI, MISO и CS и расшифровывает, какой байт ушёл и какой вернулся. Как только возникает ситуация «код правильный, а не работает», он за секунды показывает, в чём проблема — в подключении или в настройках.

SPI — общий язык быстрых модулей мира Arduino, от SD-карт до цветных дисплеев: когда четыре провода, логика CS и четыре режима понятны, подключение нового модуля SPI занимает несколько минут. В приложении BlueGrays есть проекты чтения RFID-карт и дверного замка, регистратора температуры на microSD и часов на точечной матрице MAX7219 — пошагово, со схемами подключения и кодом.

Частые вопросы

Как расшифровывается SPI?

SPI — это Serial Peripheral Interface, последовательный периферийный интерфейс. Он позволяет контроллеру быстро и синхронно обмениваться данными с периферийными устройствами по четырём линиям: SCK, MOSI, MISO и CS.

Что такое MOSI и MISO?

MOSI (Master Out Slave In) — линия данных от контроллера к периферийному устройству; MISO (Master In Slave Out) — линия данных от устройства обратно к контроллеру. В новой системе обозначений их также называют COPI и CIPO. На модулях DIN обычно подключается к MOSI, а DOUT — к MISO.

Какие пины SPI на Arduino Uno?

На Arduino Uno и Nano SCK — это D13, MISO — D12, MOSI — D11, а CS по умолчанию — D10. Те же линии выведены и на 6-контактный разъём ICSP платы. На Arduino Mega SCK — D52, MISO — D50, MOSI — D51, CS — D53.

Чем SPI отличается от I2C?

SPI использует четыре провода, работает в режиме полного дуплекса и может достигать десятков MHz; каждому устройству нужен свой провод CS. I2C использует всего два провода и выбирает устройства по адресу, но работает на гораздо меньших скоростях, например 100–400 kHz, и требует подтягивающих резисторов.

Сколько устройств можно подключить к одной шине SPI?

Теоретического предела нет: нужен лишь свободный пин CS на контроллере для каждого устройства. На практике на высоких скоростях проблемы появляются по мере роста ёмкости шины и длины проводов. Если пины закончились, линии CS можно размножить дешифратором 74HC138 или использовать устройства, которые соединяются цепочкой.

Почему SD-карта и RFID-считыватель не работают на одной шине SPI?

Преобразователь уровней во многих дешёвых модулях SD-карт не отпускает линию MISO даже при CS в HIGH и портит ответ RC522. Решение — модуль, который правильно отпускает MISO, привязка входа разрешения буферной микросхемы к CS или разнесение двух устройств по отдельным шинам SPI на ESP32.

Все проекты
Чтение значения RFID
#53Новичок
Premium

Чтение значения RFID

В брелоке от подъезда или карте доступа спрятан секретный идентификационный номер, и этот проект его читает. Поднеси карту к считывателю RFID, и Arduino выведет номер карты на экран компьютера. Первый

4.7(47)
61
Аутентификация по RFID
#54Средний
Premium

Аутентификация по RFID

Совсем как вход по карте у двери подъезда: ты подносишь карту к считывателю RFID, а Arduino сравнивает её номер с сохранённым. Правильная карта зажигает зелёный светодиод на три секунды, чужая — красн

4.8(64)
83
Считыватель карт RFID RC522 на ESP32 (с LCD)
#123Средний

Считыватель карт RFID RC522 на ESP32 (с LCD)

В этом проекте модуль RFID RC522 на базе MFRC522 подключается к ESP32 по SPI и читает UID карты или метки. Это основа для дверных замков, клубных систем и контроля доступа. Прочитанный UID выводится на LCD-экран; эта версия не сравнивает его со списком разрешённых карт. ВНИМАНИЕ: RC522 работает только от 3.3V; подача 5V сожжёт модуль!

4.8(21)
39
Дверной замок с RFID на ESP32 (RC522 + сервопривод + OLED + зуммер + светодиод)
#137Продвинутый
Premium

Дверной замок с RFID на ESP32 (RC522 + сервопривод + OLED + зуммер + светодиод)

Собираем умный дверной замок на ESP32, модуле RFID RC522, сервоприводе SG90, OLED-дисплее SSD1306, активном зуммере и 2 светодиодах. При поднесении карты её UID сравнивается со списком разрешённых карт в коде: если доступ разрешён, сервопривод поворачивается и открывает дверь (зелёный светодиод + 2 коротких сигнала), а через 5 секунд дверь снова запирается сама. При чужой карте загорается красный светодиод, звучат 3 быстрых сигнала, а прочитанный UID показывается на экране.

4.8(9)
15
Регистратор температуры на ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
#144Продвинутый
Premium

Регистратор температуры на ESP32 (DHT22 + DS1307 RTC + microSD + OLED)

Собери на ESP32 регистратор температуры с отметками времени! Каждую минуту читай температуру и влажность с DHT22, бери реальное время из DS1307 RTC и записывай всё на карту microSD в формате CSV. OLED показывает текущую температуру (крупным шрифтом), влажность в процентах, время и счётчик записей. Файл /temp_log.csv на SD-карте открывается в Excel, Google Sheets или Python pandas — идеально для теплиц, комнат, лабораторий и хранения продуктов.

4.7(7)
12
Часы и календарь на матрице (MAX7219, DS1307)
#84Средний
Premium

Часы и календарь на матрице (MAX7219, DS1307)

Светящееся табло для стола или стены, которое показывает время и дату бегущей строкой. На светодиодном дисплее 8x32 крупные цифры часов, а две точки между ними мигают каждую секунду; в начале каждой м

5.0(37)
59
Анимация и текст на 4 матрицах 32x8
#90Средний
Premium

Анимация и текст на 4 матрицах 32x8

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

4.6(40)
66
Пиксельная змейка на матрице 8x8
#88Продвинутый
Premium

Пиксельная змейка на матрице 8x8

Собери классическую игру «Змейка» своими руками и сыграй в неё. На цветной светодиодной матрице 8x8 ты джойстиком управляешь зелёной змейкой и ешь красные яблоки; с каждым яблоком змейка растёт, немно

4.7(39)
57