L'ESP32 mette a disposizione due controller SPI utilizzabili dall'utente (comunemente indicati come HSPI e VSPI, oltre a quello riservato alla flash), gestibili tramite la libreria SPI standard o l'oggetto SPIClass. Nella maggior parte dei progetti bastano e avanzano. Ma ci sono situazioni in cui conviene rinunciare all'accelerazione hardware e implementare SPI via software, cioè pilotando manualmente i pin con digitalWrite(). Vediamo quando ha senso e come farlo.
I limiti reali dell'SPI hardware
Grazie alla GPIO matrix, l'ESP32 può in teoria mappare i segnali SPI hardware su quasi qualsiasi pin GPIO, quindi il problema "non ho i pin giusti" è meno frequente di quanto sembri. Il limite vero è che hai solo due istanze hardware disponibili. Se nel tuo progetto convivono, ad esempio, un display TFT (che vuole un bus SPI dedicato, veloce, con transazioni frequenti) e uno o più sensori con requisiti di clock/mode diversi o con librerie che gestiscono il bus in modo "invasivo" (cambiano frequenza, modalità, o si aspettano controllo esclusivo), mettere tutto sullo stesso controller hardware porta a conflitti: transazioni interrotte, letture sporche, glitch sul display.
Quando conviene passare al software SPI
- Hai già saturato i due bus hardware con periferiche "pesanti" (display, SD card) e serve collegare un ulteriore dispositivo lento (es. un termocoppia amplifier come MAX31855, un sensore di pressione, una EEPROM SPI).
- La libreria del dispositivo aggiuntivo bit-banga già di suo il protocollo e non condivide bene lo stato di un controller hardware usato in parallelo da un'altra libreria (tipico con display TFT + sensori su bus fisicamente diverso).
- Vuoi isolare completamente un dispositivo critico da eventuali interferenze di timing generate da un altro bus SPI hardware molto attivo (es. refresh continuo di un display), a costo di velocità.
- Stai prototipando su pin che non passano comodamente per l'IO_MUX ad alta velocità e preferisci non stressare la GPIO matrix per un dispositivo che comunque lavora a poche decine di kHz.
Il prezzo da pagare è la velocità: il software SPI bit-banged arriva a poche centinaia di kHz nella migliore delle ipotesi, contro le decine di MHz dell'hardware. Va bene per sensori e chip di controllo, non per display o SD card ad alta velocità.
Implementazione: bit-banging manuale
Il principio è semplice: per ogni bit, imposti il livello su MOSI, generi un fronte di clock, ed eventualmente leggi MISO. Ecco una funzione minimale per trasferire un byte in modalità SPI Mode 0 (CPOL=0, CPHA=0):
#define PIN_SCK 18
#define PIN_MOSI 23
#define PIN_MISO 19
#define PIN_CS 5
void softSPIBegin() {
pinMode(PIN_SCK, OUTPUT);
pinMode(PIN_MOSI, OUTPUT);
pinMode(PIN_MISO, INPUT);
pinMode(PIN_CS, OUTPUT);
digitalWrite(PIN_SCK, LOW);
digitalWrite(PIN_CS, HIGH);
}
uint8_t softSPITransfer(uint8_t data) {
uint8_t received = 0;
for (int i = 7; i >= 0; i--) {
digitalWrite(PIN_MOSI, (data >> i) & 0x01);
digitalWrite(PIN_SCK, HIGH);
received <<= 1;
if (digitalRead(PIN_MISO)) received |= 0x01;
digitalWrite(PIN_SCK, LOW);
}
return received;
}
uint8_t leggiRegistro(uint8_t indirizzo) {
digitalWrite(PIN_CS, LOW);
softSPITransfer(indirizzo);
uint8_t valore = softSPITransfer(0x00);
digitalWrite(PIN_CS, HIGH);
return valore;
}
Usare una libreria che già lo fa per te
Molte librerie per sensori SPI espongono già una modalità "software SPI" nel costruttore, proprio per lasciarti libertà sui pin e per non toccare i controller hardware. Esempio tipico con una libreria per termocoppie tipo MAX31855:
#include <Adafruit_MAX31855.h>
#define MAXCS 5
#define MAXCLK 18
#define MAXDO 19
// Costruttore a 3 pin = software SPI dedicato,
// indipendente dal bus hardware usato da altre periferiche
Adafruit_MAX31855 termocoppia(MAXCLK, MAXCS, MAXDO);
void setup() {
Serial.begin(115200);
delay(500);
}
void loop() {
double c = termocoppia.readCelsius();
if (isnan(c)) {
Serial.println("Errore lettura termocoppia");
} else {
Serial.println("Temperatura: " + String(c) + " C");
}
delay(1000);
}
Nota pratica
Se scegli il software SPI per isolare un sensore da un display che gira su bus hardware, usa pin fisicamente diversi e non condivisi in alcun modo (nemmeno lo stesso pin CS di un altro dispositivo), e non fare mai coesistere sullo stesso bus fisico un dispositivo hardware SPI e uno software: il senso stesso della scelta è la separazione completa. Tieni anche conto che il bit-banging blocca la CPU per tutta la trasmissione: se lo esegui spesso o con payload grandi, valuta di girarlo su un task FreeRTOS dedicato per non far accumulare latenza sul resto del firmware.