Что такое 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 / SCLK | Serial Clock | SCK | Контроллер → устройство | Тактовый сигнал; на каждом импульсе передаётся один бит |
| MOSI | Master Out Slave In | COPI / SDO | Контроллер → устройство | Данные от Arduino к модулю |
| MISO | Master In Slave Out | CIPO / SDI | Устройство → контроллер | Данные от модуля обратно к Arduino |
| CS / SS | Slave Select | CS (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.
- Контроллер притягивает к LOW линию CS того устройства, с которым хочет говорить. Все остальные устройства держат свой CS в HIGH и освобождают линию MISO.
- Контроллер начинает формировать тактовые импульсы на SCK.
- На каждом импульсе один бит уходит по MOSI и один бит приходит по MISO. Порядок битов обычно — старший бит первым (MSB first).
- Когда нужное количество байтов (команда, адрес, данные) передано, контроллер возвращает CS в HIGH, и устройство завершает операцию.
Режимы SPI: CPOL и CPHA
Единого стандарта у SPI нет: устройства различаются уровнем, на котором тактовый сигнал находится в покое (CPOL, полярность тактового сигнала), и фронтом, по которому читаются данные (CPHA, фаза тактового сигнала). Четыре сочетания этих двух настроек и дают четыре режима SPI. Если контроллер и устройство работают в разных режимах, данные сдвигаются на один бит, и читаются бессмысленные значения. К счастью, подавляющее большинство модулей использует режим 0 (Mode 0).
| Режим | CPOL | CPHA | Тактовый сигнал в покое | Фронт чтения данных | Примеры устройств |
|---|---|---|---|---|---|
| SPI_MODE0 | 0 | 0 | LOW | Нарастающий | SD-карта, RC522, nRF24L01, MAX7219, MCP3008, 74HC595 |
| SPI_MODE1 | 0 | 1 | LOW | Спадающий | Некоторые АЦП и микросхемы драйверов |
| SPI_MODE2 | 1 | 0 | HIGH | Спадающий | Используется редко |
| SPI_MODE3 | 1 | 1 | HIGH | Нарастающий | Акселерометр ADXL345, некоторые дисплеи ST7789 без пина CS |
Скорость 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 GHz | 10 MHz | Режим 0 |
| MAX7219 | Светодиодная матрица / 7-сегментный индикатор | 10 MHz | Режим 0 |
| MCP3008 | 8-канальный 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 / Nano | Arduino Mega | ESP32 VSPI (по умолчанию) | ESP32 HSPI | Raspberry Pi Pico SPI0 |
|---|---|---|---|---|---|
| SCK | D13 | D52 | GPIO 18 | GPIO 14 | GP18 |
| MOSI | D11 | D51 | GPIO 23 | GPIO 13 | GP19 (TX) |
| MISO | D12 | D50 | GPIO 19 | GPIO 12 | GP16 (RX) |
| CS (по умолчанию) | D10 | D53 | GPIO 5 | GPIO 15 | GP17 |
| Логический уровень | 5 V | 5 V | 3,3 V | 3,3 V | 3,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); подробности — в утилите распиновки.
Библиотека 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 отправляет байт за несколько микросекунд:
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.
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(). Пример ниже следует этому порядку.
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-файл:
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}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:
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: сравнение
| Характеристика | SPI | I2C | UART |
|---|---|---|---|
| Количество проводов | 4 (+1 CS на каждое дополнительное устройство) | 2 (SDA, SCL) | 2 (TX, RX) |
| Типичная скорость | 1–80 MHz | 100 kHz, 400 kHz, 1 MHz | 9600–115200 бод (до нескольких Мбит/с) |
| Тактовый сигнал | Есть (формирует контроллер) | Есть (формирует контроллер) | Нет; обе стороны договариваются об одной скорости в бодах |
| Направление | Полный дуплекс | Полудуплекс | Полный дуплекс |
| Выбор устройства | Отдельный провод CS | 7-битный адрес | Только два устройства (точка — точка) |
| Дополнительные детали | Не нужны | Подтягивающие резисторы | Не нужны |
| Расстояние | Короткое (на плате, несколько десятков см) | Короткое (около 1 м) | Несколько метров; с RS-485 намного больше |
| Типичные модули | SD-карта, RC522, TFT, nRF24L01, MAX7219 | OLED, I2C-переходник для LCD, RTC, BME280, MPU6050 | GPS, 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 или 0x00 | MISO не подключён, MOSI и MISO перепутаны или у устройства нет питания | Подключи DIN к MOSI, а DOUT к MISO; измерь VCC устройства |
| Значения бессмысленные или сдвинуты на один бит | Неверный режим SPI или слишком высокая для устройства тактовая частота | Используй режим из даташита, снизь скорость до 1 MHz и попробуй ещё раз |
| Два устройства по отдельности работают, а вместе — нет | Один модуль не отпускает MISO или пины CS при старте не в HIGH | Сначала выставь все пины CS в HIGH; замени модуль, который не отпускает MISO, или используй отдельную шину SPI |
| SPI на Uno внезапно перестаёт работать | D10 использовался как вход и был притянут к LOW | Оставь D10 выходом; его можно использовать как CS |
| ESP32 не загружается с подключённым HSPI | GPIO 12 при загрузке удерживается в HIGH | Перенеси подтянутый MISO на VSPI или выбери для MISO другой пин |
| Случайные ошибки на длинных проводах | Отражения и помехи на быстром сигнале | Укороти провода, снизь скорость, проложи провод GND рядом с сигнальными |
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
В брелоке от подъезда или карте доступа спрятан секретный идентификационный номер, и этот проект его читает. Поднеси карту к считывателю RFID, и Arduino выведет номер карты на экран компьютера. Первый

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

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

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

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

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

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

Пиксельная змейка на матрице 8x8
Собери классическую игру «Змейка» своими руками и сыграй в неё. На цветной светодиодной матрице 8x8 ты джойстиком управляешь зелёной змейкой и ешь красные яблоки; с каждым яблоком змейка растёт, немно
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

