Cos'è SPI? Pin MOSI, MISO, SCK e CS con Arduino spiegati bene
Cos'è SPI e come funziona: pin MOSI, MISO, SCK e CS, modalità e velocità, più dispositivi, pin di Uno, Mega, ESP32 e Pico, e il conflitto tra SD e RFID.
SPI (Serial Peripheral Interface, interfaccia seriale per periferiche) è un metodo di comunicazione che permette a un microcontrollore di scambiare dati molto velocemente con dispositivi come schede SD, lettori RFID, display TFT a colori o moduli wireless attraverso quattro fili. C'è una linea di clock (SCK), due linee separate per i dati nei due sensi (MOSI e MISO) e una linea CS che sceglie con quale dispositivo si sta parlando. Poiché può essere decine di volte più veloce dell'I2C, è il bus preferito dai moduli che trasferiscono molti dati.
In questo articolo imparerai come funziona SPI, i nomi dei pin e la nuova terminologia, le quattro modalità SPI, i limiti di velocità, i pin di Arduino Uno, Mega, ESP32 (VSPI e HSPI) e Raspberry Pi Pico, come collegare più dispositivi allo stesso bus e perché una scheda SD e un lettore RFID RC522 insieme danno problemi. Nell'ultima sezione confrontiamo SPI con I2C e UART.
Cos'è SPI e come funziona?
In SPI c'è sempre un controller (un tempo chiamato master) e una o più periferiche (un tempo chiamate slave). Solo il controller genera il clock; quindi è Arduino ad avviare la comunicazione, fissarne la velocità e chiuderla. A ogni impulso di clock il controller invia un bit su MOSI mentre, nello stesso momento, la periferica rimanda un bit su MISO. I registri a scorrimento dei due lati formano una specie di anello: dopo 8 impulsi il byte del controller è passato nella periferica e quello della periferica nel controller. Per questo SPI è full duplex: lettura e scrittura avvengono insieme.
| Linea | Vecchio nome | Nuovo nome | Direzione | Funzione |
|---|---|---|---|---|
| SCK / SCLK | Serial Clock | SCK | Controller → periferica | Segnale di clock; a ogni impulso viaggia un bit |
| MOSI | Master Out Slave In | COPI / SDO | Controller → periferica | Dati da Arduino al modulo |
| MISO | Master In Slave Out | CIPO / SDI | Periferica → controller | Dati dal modulo di ritorno ad Arduino |
| CS / SS | Slave Select | CS (Chip Select) | Controller → periferica | Finché è LOW quel modulo ascolta; ogni dispositivo ha il suo CS |
Le scritte sui moduli non sono sempre uguali, ed è la principale fonte di confusione. Sul modulo RFID RC522 la linea CS si chiama SDA (non ha niente a che vedere con l'SDA dell'I2C), sul nRF24L01 CSN, sul MCP3008 CS/SHDN, e su alcuni display troverai nomi come DIN (MOSI), CLK (SCK) e DOUT (MISO). La regola è semplice: l'ingresso dati (DIN, SDI, MOSI) va al MOSI di Arduino, l'uscita dati (DOUT, SDO, MISO) al MISO di Arduino.
- Il controller porta a LOW la linea CS del dispositivo con cui vuole parlare. Tutti gli altri tengono il CS a HIGH e lasciano libera la linea MISO.
- Il controller inizia a generare impulsi di clock su SCK.
- A ogni impulso esce un bit su MOSI ed entra un bit su MISO. Di solito si trasmette prima il bit più significativo (MSB first).
- Trasferiti i byte necessari (comando, indirizzo, dati), il controller riporta CS a HIGH e il dispositivo completa l'operazione.
Modalità SPI: CPOL e CPHA
SPI non ha uno standard unico; i dispositivi differiscono per il livello a cui resta il clock a riposo (CPOL, polarità del clock) e per il fronte del clock su cui vengono letti i dati (CPHA, fase del clock). Le quattro combinazioni di queste due impostazioni formano le quattro modalità SPI. Se controller e dispositivo non sono nella stessa modalità, i dati scivolano di un bit e leggi valori senza senso. Per fortuna la grande maggioranza dei moduli usa la modalità 0.
| Modalità | CPOL | CPHA | Clock a riposo | Fronte di lettura | Dispositivi di esempio |
|---|---|---|---|---|---|
| SPI_MODE0 | 0 | 0 | LOW | Salita | Scheda SD, RC522, nRF24L01, MAX7219, MCP3008, 74HC595 |
| SPI_MODE1 | 0 | 1 | LOW | Discesa | Alcuni ADC e integrati driver |
| SPI_MODE2 | 1 | 0 | HIGH | Discesa | Usata raramente |
| SPI_MODE3 | 1 | 1 | HIGH | Salita | Accelerometro ADXL345, alcuni display ST7789 senza pin CS |
Velocità SPI e lunghezza dei cavi
In SPI la velocità la decide il controller e il limite di solito lo pone il dispositivo stesso. Arduino Uno arriva fino a 8 MHz, metà del suo clock a 16 MHz; il valore predefinito della libreria SPI è 4 MHz. L'ESP32 può generare fino a 80 MHz sui suoi pin SPI dedicati (IO_MUX), e il limite si abbassa quando il segnale passa dalla matrice GPIO verso altri pin. Il Raspberry Pi Pico raggiunge circa 62,5 MHz con un clock di sistema di 125 MHz. I segnali SPI veloci vogliono fili corti: con breadboard e cavetti jumper da 10–20 cm qualche MHz non dà problemi; da 20–30 MHz in su tieni i fili corti e, se puoi, salda i collegamenti. Diversi metri di cavo non sono adatti a SPI.
| Dispositivo | Funzione | Clock SPI massimo (datasheet) | Modalità SPI |
|---|---|---|---|
| Scheda microSD | Registrazione dati | 400 kHz all'avvio, poi fino a 25 MHz | Modalità 0 |
| RC522 (MFRC522) | Lettore RFID | 10 MHz | Modalità 0 |
| nRF24L01 | Radio a 2,4 GHz | 10 MHz | Modalità 0 |
| MAX7219 | Matrice LED / 7 segmenti | 10 MHz | Modalità 0 |
| MCP3008 | ADC a 8 canali e 10 bit | 3,6 MHz a 5 V, 1,35 MHz a 2,7 V | Modalità 0 o 3 |
| ILI9341 | TFT a colori da 2,4–3,2 pollici | 10 MHz in scrittura (in pratica pilotato molto più veloce) | Modalità 0 |
Pin SPI: Arduino Uno, Mega, ESP32 e Raspberry Pi Pico
| Linea | Arduino Uno / Nano | Arduino Mega | ESP32 VSPI (predefinito) | 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 (predefinito) | D10 | D53 | GPIO 5 | GPIO 15 | GP17 |
| Livello logico | 5 V | 5 V | 3,3 V | 3,3 V | 3,3 V |
Sull'Uno le stesse linee SPI si trovano anche sul connettore ICSP a 6 pin sul bordo della scheda; su schede come Leonardo e Micro lo SPI è disponibile solo sull'ICSP. Per il CS non è obbligatorio usare D10: va bene qualsiasi pin digitale. Però sull'Uno non lasciare D10 come ingresso: se l'hardware SPI legge LOW su D10 mentre è un ingresso, esce dalla modalità controller. SPI.begin() imposta automaticamente questo pin come uscita, e non dovresti trasformarlo in ingresso per altri scopi. Sull'ESP32 l'oggetto SPI di Arduino usa VSPI per impostazione predefinita; grazie alla matrice GPIO puoi spostare SPI su quasi tutti i pin. Sul Pico, SPI0 e SPI1 si possono assegnare a diversi gruppi di pin (es. GP10, GP11, GP12 per SPI1); trovi i dettagli nello strumento Pinout.
La libreria SPI di Arduino: funzioni principali
| Funzione | Cosa fa? |
|---|---|
| SPI.begin() | Avvia l'hardware SPI; imposta come uscite i pin SCK, MOSI e SS. |
| SPISettings(velocità, MSBFIRST, SPI_MODE0) | Raccoglie in un oggetto velocità, ordine dei bit e modalità di un dispositivo. |
| SPI.beginTransaction(impostazioni) | Applica le impostazioni e riserva il bus per quel dispositivo; rende sicuro il passaggio tra dispositivi con impostazioni diverse. |
| SPI.transfer(byte) | Invia un byte e restituisce il byte ricevuto nello stesso momento. |
| SPI.transfer16(valore) | Invia e riceve un valore a 16 bit in un colpo solo. |
| SPI.transfer(buffer, lunghezza) | Invia un array; i byte ricevuti sovrascrivono lo stesso array. |
| SPI.endTransaction() | Libera il bus; va chiamata dopo aver riportato CS a HIGH. |
Primo esempio: inviare dati a un 74HC595 via SPI
Il modo più semplice per capire SPI è usare un registro a scorrimento 74HC595, perché il 595 è in pratica la periferica SPI più semplice, senza MISO. Il pin MOSI di Arduino va all'ingresso DS del 595, SCK a SH_CP e D10 a ST_CP, che fa da latch. A differenza della funzione shiftOut(), lo SPI hardware invia il byte in pochi microsecondi:
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}Leggere dati via SPI: l'ADC MCP3008
Per vedere la comunicazione full duplex usiamo il MCP3008. Questo integrato aggiunge ad Arduino altri 8 ingressi analogici a 10 bit; è una buona alternativa all'ADC poco lineare dell'ESP32 o ai soli 3 ingressi analogici del Pico. Abbiamo spiegato le basi della lettura analogica nell'articolo Cos'è un ADC?. Collegamenti: CLK → D13, DOUT → D12, DIN → D11, CS → D10, VDD e VREF → 5V, AGND e 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}Qui inviamo tre byte e ne riceviamo tre nello stesso momento. Il primo byte (0x01) è solo il bit di start; nel secondo viaggiano la modalità single-ended e il numero di canale, mentre il MCP3008 rimanda i 2 bit più alti del risultato. Il terzo byte è 0x00; il suo contenuto non conta, serve solo a generare impulsi di clock per ricevere gli 8 bit restanti. In SPI "leggere" significa sempre inviare qualcosa. Il risultato va da 0 a 1023 e si converte in volt rispetto a VREF (qui 5 V).
Più dispositivi SPI: linee CS separate
La differenza più grande tra SPI e I2C è che SPI non usa indirizzi. I dispositivi condividono le linee SCK, MOSI e MISO, e ognuno riceve da Arduino il proprio filo CS. Ascolta solo il dispositivo con il CS a LOW, e solo lui scrive dati su MISO. Tre dispositivi richiedono 3 + 3 = 6 fili, cinque dispositivi 3 + 5 = 8; il numero di pin cresce con quello dei dispositivi. Alcuni integrati come il 74HC595 e il MAX7219 si possono invece collegare in cascata (daisy chain): l'uscita di uno entra nel successivo e tutti si pilotano con un solo CS.
- Un solo CS a LOW alla volta. Se due dispositivi vengono selezionati insieme si scontrano sulla linea MISO e leggi dati corrotti da entrambi.
- In setup() imposta prima tutti i pin CS come uscita a HIGH, poi avvia le librerie. Un pin CS flottante può sembrare LOW a caso e occupare il bus.
- Usa beginTransaction per ogni dispositivo. Una scheda SD può volere 25 MHz e un MCP3008 1 MHz; le transazioni applicano automaticamente velocità e modalità giuste a ogni cambio.
- Se usi SPI dentro gli interrupt, comunicalo alla libreria con SPI.usingInterrupt(), oppure non fare operazioni SPI dentro l'interrupt.
- Una pull-up da 10 kΩ sulla linea CS di ogni dispositivo evita che i dispositivi si selezionino da soli mentre Arduino si avvia o si resetta.
Scheda SD e RFID sullo stesso bus: perché non funziona?
Una domanda classica dei forum Arduino: l'RFID RC522 funziona da solo, il modulo scheda SD funziona da solo, ma collegati allo stesso bus SPI uno dei due, o entrambi, smettono di rispondere. Anche se i pin CS sono giusti nel software, il problema di solito è nell'hardware. Molti moduli SD economici hanno un buffer 74LVC125 che converte i segnali da 5 V a 3,3 V, e il canale di questo integrato che pilota la linea MISO è sempre abilitato. Quindi, anche con il CS della scheda SD a HIGH, il modulo non rilascia MISO; quando l'RC522 prova a rispondere, due uscite si contendono la linea.
- Scegli il modulo giusto: i moduli che mettono MISO in alta impedenza quando CS è HIGH condividono il bus senza problemi. Per capire se un modulo rilascia la linea, con CS a HIGH tira MISO con 10 kΩ prima verso GND e poi verso 5 V e misura con il multimetro.
- Collega il pin di abilitazione del buffer al CS: se sai saldare, staccare da GND il pin OE del canale MISO del 74LVC125 e collegarlo alla linea CS del modulo risolve il problema alla radice.
- Aggiungi una resistenza in serie su MISO: una resistenza di circa 1 kΩ in serie all'uscita MISO del modulo SD permette all'RC522 di imporre il suo segnale. È un rimedio facile ma che limita la velocità.
- Usa un bus SPI separato: sull'ESP32, se colleghi la scheda SD a VSPI e l'RC522 a HSPI, i due dispositivi non si incontrano mai. L'Uno non ha un secondo SPI hardware, quindi lì questa possibilità non esiste.
- Rispetta l'ordine di avvio: porta prima tutti i CS a HIGH, poi chiama SPI.begin(), quindi SD.begin() e PCD_Init(). L'esempio qui sotto segue questo ordine.
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}In questo codice la libreria SD e la libreria MFRC522 portano da sole a LOW e HIGH i propri pin CS; tu devi solo lasciarli entrambi a HIGH all'inizio e assicurarti che l'hardware condivida correttamente MISO. Il codice scrive l'UID di ogni tessera letta, insieme a un'indicazione di tempo, nel file kartlar.txt. Sull'Uno queste due librerie insieme usano circa metà della RAM; la macro F() libera spazio tenendo i testi costanti nella flash.
VSPI e HSPI sull'ESP32: due bus SPI separati
L'ESP32 contiene quattro unità SPI; SPI0 e SPI1 sono riservate alla flash interna, quindi a te restano VSPI (SPI3) e HSPI (SPI2). Usarle entrambe è il modo più pulito per separare dispositivi con velocità e modalità molto diverse o per evitare il conflitto su MISO visto sopra. Nel core Arduino crei un oggetto SPIClass per il secondo bus e gli assegni i pin con begin(). Il codice seguente usa la scheda SD sul VSPI predefinito e il MCP3008 su HSPI, e ogni secondo aggiunge una misura a un file 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 con il Raspberry Pi Pico (MicroPython)
Il Pico ha due unità SPI hardware (SPI0 e SPI1), che in MicroPython si usano con la classe machine.SPI. I parametri polarity e phase corrispondono a CPOL e CPHA; se valgono entrambi 0 è la modalità 0. Il CS lo gestisci tu come un normale pin di uscita. La funzione write_readinto() mostra al meglio la natura full duplex di SPI: mentre invia un array, scrive i byte in arrivo in un altro. Leggiamo lo stesso MCP3008, questa volta alimentato a 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 e UART a confronto
| Caratteristica | SPI | I2C | UART |
|---|---|---|---|
| Numero di fili | 4 (+1 CS per ogni dispositivo in più) | 2 (SDA, SCL) | 2 (TX, RX) |
| Velocità tipica | 1–80 MHz | 100 kHz, 400 kHz, 1 MHz | 9600–115200 baud (fino a qualche Mbit/s) |
| Clock | Sì (generato dal controller) | Sì (generato dal controller) | No; i due lati concordano la stessa velocità in baud |
| Direzione | Full duplex | Half duplex | Full duplex |
| Selezione del dispositivo | Filo CS dedicato | Indirizzo a 7 bit | Solo due dispositivi (punto a punto) |
| Componenti aggiuntivi | Non servono | Resistenze di pull-up | Non servono |
| Distanza | Breve (sulla scheda, qualche decina di cm) | Breve (circa 1 m) | Qualche metro; molto di più con RS-485 |
| Moduli tipici | Scheda SD, RC522, TFT, nRF24L01, MAX7219 | OLED, adattatore I2C per LCD, RTC, BME280, MPU6050 | GPS, HC-05, SIM800L, ESP-01, computer |
Spesso la scelta la fa il modulo, ma nei tuoi progetti questa regola aiuta: se contano velocità e quantità di dati (immagini sul display, file, audio) usa SPI; se ti servono pochi fili e tanti sensori lenti usa I2C; se vuoi un collegamento semplice, a testo, tra due dispositivi usa UART. Trovi UART nel dettaglio nell'articolo Comunicazione seriale UART. È molto comune anche usarli tutti e tre nello stesso progetto: per esempio un data logger che legge i sensori via I2C, scrive su una scheda SD via SPI e riceve la posizione da un GPS via UART.
Moduli che usano SPI e idee di progetto
- Serratura a tessera con registro degli accessi: leggi la tessera con un RC522 e scrivi data e UID su una scheda SD.
- Registratore di temperatura: salva i dati dei sensori su una microSD in formato CSV e fai un grafico in Excel.
- Pannello a scritte scorrevoli: mostra ora e messaggi su moduli a matrice LED MAX7219 in cascata.
- Interfaccia a colori: disegna grafici e menu su un display TFT ILI9341; collega anche il pannello touch allo stesso bus SPI.
- Radiocomando: invia comandi alla tua auto robot con due moduli nRF24L01.
- Misura multicanale: costruisci con un MCP3008 un controller MIDI con 8 potenziometri o un impianto di irrigazione con 8 sensori di umidità del terreno.
Errori comuni e soluzioni
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| Si legge sempre 0xFF o 0x00 | MISO non collegato, MOSI e MISO invertiti o dispositivo non alimentato | Collega DIN a MOSI e DOUT a MISO; misura il VCC del dispositivo |
| Valori senza senso o spostati di un bit | Modalità SPI sbagliata o clock troppo alto per il dispositivo | Usa la modalità del datasheet, abbassa la velocità a 1 MHz e riprova |
| Due dispositivi che funzionano da soli non funzionano insieme | Un modulo non rilascia MISO o i pin CS non sono HIGH all'avvio | Porta prima tutti i CS a HIGH; sostituisci il modulo che non rilascia MISO o usa un bus SPI separato |
| Lo SPI si blocca all'improvviso sull'Uno | D10 è stato usato come ingresso e portato a LOW | Lascia D10 come uscita; puoi usarlo come CS |
| L'ESP32 non si avvia con HSPI collegato | GPIO 12 resta HIGH all'avvio | Sposta il MISO con pull-up su VSPI o scegli un altro pin per MISO |
| Errori casuali con cavi lunghi | Riflessioni e disturbi su un segnale veloce | Accorcia i cavi, abbassa la velocità, fai correre un filo GND accanto ai segnali |
SPI è la lingua comune dei moduli veloci del mondo Arduino, dalle schede SD ai display a colori: una volta imparati i quattro fili, la logica del CS e le quattro modalità, collegare un nuovo modulo SPI è questione di minuti. Nell'app BlueGrays trovi i progetti di lettura di tessere RFID e serratura, registratore di temperatura su microSD e orologio a matrice di punti MAX7219, passo dopo passo con schemi del circuito e codice.
Domande frequenti
Cosa significa SPI?
SPI è la sigla di Serial Peripheral Interface (interfaccia seriale per periferiche). Permette a un controller di scambiare dati in modo veloce e sincrono con le periferiche su quattro linee: SCK, MOSI, MISO e CS.
Cosa sono MOSI e MISO?
MOSI (Master Out Slave In) è la linea dati dal controller alla periferica; MISO (Master In Slave Out) è la linea dati dalla periferica di ritorno al controller. Nella nuova terminologia si chiamano anche COPI e CIPO. Sui moduli, DIN di solito va a MOSI e DOUT a MISO.
Quali sono i pin SPI di Arduino Uno?
Su Arduino Uno e Nano, SCK è D13, MISO è D12, MOSI è D11 e il CS predefinito è D10. Le stesse linee si trovano anche sul connettore ICSP a 6 pin della scheda. Su Arduino Mega sono SCK D52, MISO D50, MOSI D51 e CS D53.
Che differenza c'è tra SPI e I2C?
SPI usa quattro fili, è full duplex e può funzionare a decine di MHz; ogni dispositivo ha bisogno del suo filo CS. I2C usa solo due fili e seleziona i dispositivi tramite indirizzo, ma lavora a velocità molto più basse, come 100–400 kHz, e richiede resistenze di pull-up.
Quanti dispositivi si possono collegare a un bus SPI?
Non c'è un limite teorico; basta avere un pin CS libero sul controller per ogni dispositivo. In pratica a velocità elevate nascono problemi quando crescono la capacità del bus e la lunghezza dei cavi. Se i pin non bastano, puoi moltiplicare le linee CS con un decoder 74HC138 o usare dispositivi collegabili in cascata.
Perché scheda SD e RFID non funzionano sullo stesso bus SPI?
Il traslatore di livello di molti moduli SD economici non rilascia la linea MISO nemmeno con CS a HIGH e rovina la risposta dell'RC522. Si risolve usando un modulo che rilascia correttamente MISO, collegando il pin di abilitazione del buffer al CS oppure, sull'ESP32, mettendo i due dispositivi su bus SPI separati.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
Lettura del valore RFID
Il portachiavi del condominio o la tessera d'ingresso nascondono un numero di identificazione segreto, e questo progetto lo legge. Avvicina la tessera al lettore RFID e l'Arduino scrive il suo numero

Autenticazione RFID
Come l'ingresso con tessera di un condominio: avvicini la tua tessera al lettore RFID e l'Arduino confronta il suo numero con quello salvato. Con la tessera giusta il LED verde resta acceso tre second

Lettore di schede RFID RC522 per ESP32 (con LCD)
In questo progetto collegheremo il modulo RFID RC522 basato su MFRC522 all'ESP32 tramite SPI per leggere l'UID della tessera o dell'etichetta. Costituisce la base per applicazioni quali serrature, sistemi di abbonamento e controllo degli accessi. Visualizzeremo l'UID letto su un display LCD; questa versione non lo confronta con un elenco di tessere autorizzate. ATTENZIONE: l'RC522 funziona solo a 3,3 V; a 5 V il modulo si brucerà!

Serratura RFID ESP32 (RC522 + servomotore + OLED + cicalino + LED)
Realizziamo una serratura intelligente utilizzando un modulo RFID RC522 con ESP32, un servomotore SG90, un display OLED SSD1306, un cicalino attivo e 2 LED. Quando viene letta una tessera, il codice UID viene confrontato con l’elenco delle tessere autorizzate definito nel sistema: se autorizzata, il servomotore ruota sbloccando la serratura (LED verde + 2 brevi bip); dopo 5 secondi, la serratura si blocca automaticamente. In caso di tessera non autorizzata, si accende il LED rosso, viene emesso

Data Logger di Temperatura ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
Costruisci un data logger di temperatura con timestamp usando ESP32! Leggi temperatura/umidità dal DHT22 ogni minuto, ottieni l'ora reale dal DS1307 RTC e registra su scheda microSD in formato CSV. L'OLED mostra dal vivo temperatura (font grande), percentuale di umidità, timestamp e contatore di log. Il file /temp_log.csv sulla scheda SD si apre in Excel, Google Sheets o Python pandas per l'analisi — perfetto per il monitoraggio di serre, tracciamento della temperatura domestica, raccolta

Orologio e calendario a matrice di punti (MAX7219, DS1307)
Un pannello luminoso per la scrivania o la parete che mostra ora e data con scritte scorrevoli. Sul display LED 8x32 c'è un orologio a cifre grandi con i due punti che lampeggiano ogni secondo; all'in

Animazione e testo LED a matrice 4x 32x8
Impilando quattro lunghi moduli a matrice di LED ottieni uno schermo quadrato con più di mille punti luminosi. Prima le lettere cadono dall'alto come una pioggia da film, poi si apre un sipario e due

Serpente di pixel Matrice 8x8
Costruisci con le tue mani il classico gioco del serpente e giocaci. Su una matrice di luci colorate 8x8 guidi un serpente verde con un joystick e mangi mele rosse; a ogni mela il serpente si allunga,
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 200 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

