ESP32 SPI: настройка пинов, несколько шин и периферийных устройств (Arduino IDE)
Это простое руководство о протоколе SPI-связи с ESP32 с использованием Arduino IDE. Мы рассмотрим пины SPI на ESP32, как подключать SPI-устройства, как задавать пользовательские пины SPI, как использовать несколько SPI-устройств и многое другое.
В этом руководстве рассматривается программирование ESP32 с использованием ядра Arduino, поэтому перед началом работы у вас должно быть установлено дополнение ESP32 в Arduino IDE. Следуйте следующему руководству для установки ESP32 в Arduino IDE, если вы ещё этого не сделали.
Также вы можете использовать VS Code с расширением PlatformIO для программирования ваших плат с использованием ядра Arduino:
Введение в протокол SPI-связи ESP32
SPI расшифровывается как Serial Peripheral Interface (последовательный периферийный интерфейс), и это синхронный протокол последовательной передачи данных, используемый микроконтроллерами для связи с одним или несколькими периферийными устройствами. Например, ваша плата ESP32, связывающаяся с датчиком, поддерживающим SPI, или с другим микроконтроллером.
В SPI-связи всегда есть контроллер (также называемый мастером), который управляет периферийными устройствами (также называемыми ведомыми). Данные могут отправляться и приниматься одновременно. Это означает, что мастер может отправлять данные ведомому, а ведомый может отправлять данные мастеру в одно и то же время.
У вас может быть только один мастер, которым будет микроконтроллер (ESP32), но вы можете иметь несколько ведомых. Ведомым может быть датчик, дисплей, карта microSD и т. д., или другой микроконтроллер. Это означает, что к ESP32 можно подключить несколько датчиков, но один и тот же датчик нельзя подключить к нескольким платам ESP32 одновременно.
Интерфейс SPI
Для SPI-связи вам нужны четыре линии:
MISO: Master In Slave Out (данные от ведомого к мастеру)
MOSI: Master Out Slave In (данные от мастера к ведомому)
SCK: Serial Clock (тактовый сигнал)
CS / SS: Chip Select (используется для выбора устройства, когда несколько периферийных устройств подключены к одной шине SPI)
На устройствах, работающих только в режиме ведомого, таких как датчики, дисплеи и другие, вы можете встретить другую терминологию:
MISO может быть обозначен как SDO (Serial Data Out)
MOSI может быть обозначен как SDI (Serial Data In)
Периферийные устройства SPI ESP32
ESP32 интегрирует 4 периферийных устройства SPI: SPI0, SPI1, SPI2 (обычно называемый HSPI) и SPI3 (обычно называемый VSPI).
SPI0 и SPI1 используются внутренне для связи со встроенной флеш-памятью, и вы не должны использовать их для других задач.
Вы можете использовать HSPI и VSPI для связи с другими устройствами. HSPI и VSPI имеют независимые сигналы шины, и каждая шина может управлять до трёх ведомых SPI-устройств.
Пины SPI ESP32 по умолчанию
Многие платы ESP32 поставляются с предустановленными пинами SPI. Распиновка для большинства плат следующая (для ESP32-S3 она отличается):
SPI |
MOSI |
MISO |
SCLK |
CS |
|---|---|---|---|---|
VSPI |
GPIO 23 |
GPIO 19 |
GPIO 18 |
GPIO 5 |
HSPI |
GPIO 13 |
GPIO 12 |
GPIO 14 |
GPIO 15 |
Предупреждение
В зависимости от используемой платы, пины SPI по умолчанию могут отличаться. Поэтому обязательно проверьте распиновку для вашей платы. Кроме того, на некоторых платах нет предустановленных пинов SPI, поэтому их необходимо задать в коде.
Примечание
Обычно, если не указано иное, плата будет использовать пины VSPI при инициализации SPI-связи с настройками по умолчанию.
Независимо от того, есть ли у вашей платы предустановленные пины или нет, вы всегда можете задать их в коде.
Определение пинов SPI вашей платы ESP32 по умолчанию
Если вы не уверены, какие пины SPI по умолчанию использует ваша плата, вы можете загрузить следующий код, чтобы узнать это.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-spi-communication-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
//Find the default SPI pins for your board
//Make sure you have the right board selected in Tools > Boards
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.print("MOSI: ");
Serial.println(MOSI);
Serial.print("MISO: ");
Serial.println(MISO);
Serial.print("SCK: ");
Serial.println(SCK);
Serial.print("SS: ");
Serial.println(SS);
}
void loop() {
// put your main code here, to run repeatedly:
}
Важно
Убедитесь, что вы выбрали используемую плату в меню Tools > Board, иначе вы можете получить неправильные пины.
После загрузки кода откройте монитор порта, перезагрузите плату, и вы увидите пины SPI.
Использование пользовательских пинов SPI ESP32
При использовании библиотек для взаимодействия с вашими SPI-периферийными устройствами обычно просто использовать пользовательские пины SPI, потому что вы можете передать их в качестве аргументов в конструктор библиотеки.
Например, посмотрите на следующий пример, который работает с датчиком BME280 с использованием библиотеки Adafruit_BME280.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-spi-communication-arduino/
Based on the Adafruit_BME280_Library example: https://github.com/adafruit/Adafruit_BME280_Library/blob/master/examples/bme280test/bme280test.ino
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <SPI.h>
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
#define SEALEVELPRESSURE_HPA (1013.25)
//Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
unsigned long delayTime;
void setup() {
Serial.begin(9600);
Serial.println(F("BME280 test"));
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin();
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
Serial.println("-- Default Test --");
delayTime = 1000;
Serial.println();
}
void loop() {
printValues();
delay(delayTime);
}
void printValues() {
Serial.print("Temperature = ");
Serial.print(bme.readTemperature());
Serial.println(" *C");
// Convert temperature to Fahrenheit
/*Serial.print("Temperature = ");
Serial.print(1.8 * bme.readTemperature() + 32);
Serial.println(" *F");*/
Serial.print("Pressure = ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(" hPa");
Serial.print("Approx. Altitude = ");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");
Serial.print("Humidity = ");
Serial.print(bme.readHumidity());
Serial.println(" %");
Serial.println();
}
Вы можете легко передать свои пользовательские пины SPI в конструктор библиотеки.
Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
В данном случае использовались следующие пины SPI (не по умолчанию), и всё работало как ожидалось:
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
Если вы не используете библиотеку, или используемая вами библиотека не принимает пины в конструкторе библиотеки, вам может потребоваться инициализировать шину SPI самостоятельно. В этом случае вам нужно вызвать метод SPI.begin() в setup() и передать пины SPI в качестве аргументов:
SPI.begin(SCK, MISO, MOSI, SS);
Вы можете увидеть пример такого сценария в этом руководстве, в котором мы инициализируем SPI-трансивер LoRa, подключённый к пользовательским пинам SPI. Или этот пример, показывающий, как использовать пользовательские пины SPI с модулем microSD-карты.
ESP32 с несколькими SPI-устройствами
Как мы видели ранее, вы можете использовать две различные шины SPI на ESP32, и каждая шина может подключать до трёх различных периферийных устройств. Это означает, что мы можем подключить до шести SPI-устройств к ESP32. Если вам нужно использовать больше, вы можете использовать SPI-мультиплексор.
Несколько SPI-устройств (одна шина, разные пины CS)
Для подключения нескольких SPI-устройств вы можете использовать одну и ту же шину SPI, при условии что каждое периферийное устройство использует разный пин CS.
Чтобы выбрать периферийное устройство, с которым вы хотите связаться, вы должны установить его пин CS в LOW. Например, представьте, что у вас есть периферийное устройство 1 и периферийное устройство 2. Чтобы прочитать данные от периферийного устройства 1, убедитесь, что его пин CS установлен в LOW (здесь обозначен как CS_1):
digitalWrite(CS_1, LOW); // enable CS pin to read from peripheral 1
/*
use any SPI functions to communicate with peripheral 1
*/
Затем, в какой-то момент, вы захотите прочитать данные от периферийного устройства 2. Вы должны отключить пин CS периферийного устройства 1, установив его в HIGH, и включить пин CS периферийного устройства 2, установив его в LOW:
digitalWrite(CS_1, HIGH); // disable CS pin from peripheral 1
digitalWrite(CS_2, LOW); // enable CS pin to read from peripheral 2
/*
use any SPI functions to communicate with peripheral 2
*/
Использование двух шин SPI ESP32 (одновременное использование HSPI и VSPI)
Для одновременной связи с несколькими SPI-периферийными устройствами вы можете использовать две шины SPI ESP32 (HSPI и VSPI). Вы можете использовать пины HSPI и VSPI по умолчанию или пользовательские пины.
Кратко, чтобы использовать HSPI и VSPI одновременно, вам нужно сделать следующее.
1) Сначала убедитесь, что вы включили библиотеку SPI в свой код.
#include <SPI.h>
2) Инициализируйте два объекта SPIClass с разными именами, один на шине HSPI, а другой на шине VSPI. Например:
vspi = new SPIClass(VSPI);
hspi = new SPIClass(HSPI);
3) Вызовите метод begin() для этих объектов.
vspi.begin();
hspi.begin();
Вы можете передать пользовательские пины в метод begin(), если это необходимо.
vspi.begin(VSPI_CLK, VSPI_MISO, VSPI_MOSI, VSPI_SS);
hspi.begin(HSPI_CLK, HSPI_MISO, HSPI_MOSI, HSPI_SS);
4) Наконец, вам также нужно установить пины SS как выходы. Например:
pinMode(VSPI_SS, OUTPUT);
pinMode(HSPI_SS, OUTPUT);
Затем используйте обычные команды для взаимодействия с SPI-устройствами, независимо от того, используете ли вы библиотеку датчика или методы библиотеки SPI.
Вы можете найти пример использования нескольких шин SPI в библиотеке SPI arduino-esp32. Смотрите пример ниже:
/* The ESP32 has four SPi buses, however as of right now only two of
* them are available to use, HSPI and VSPI. Simply using the SPI API
* as illustrated in Arduino examples will use VSPI, leaving HSPI unused.
*
* However if we simply initialize two instance of the SPI class for both
* of these buses both can be used. However when just using these the Arduino
* way only will actually be outputting at a time.
*
* Logic analyzer capture is in the same folder as this example as
* "multiple_bus_output.png"
*
* created 30/04/2018 by Alistair Symonds
*/
#include <SPI.h>
// Define ALTERNATE_PINS to use non-standard GPIO pins for SPI bus
#ifdef ALTERNATE_PINS
#define VSPI_MISO 2
#define VSPI_MOSI 4
#define VSPI_SCLK 0
#define VSPI_SS 33
#define HSPI_MISO 26
#define HSPI_MOSI 27
#define HSPI_SCLK 25
#define HSPI_SS 32
#else
#define VSPI_MISO MISO
#define VSPI_MOSI MOSI
#define VSPI_SCLK SCK
#define VSPI_SS SS
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_SCLK 14
#define HSPI_SS 15
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32)
#define VSPI FSPI
#endif
static const int spiClk = 1000000; // 1 MHz
//uninitialized pointers to SPI objects
SPIClass *vspi = NULL;
SPIClass *hspi = NULL;
void setup() {
//initialize two instances of the SPIClass attached to VSPI and HSPI respectively
vspi = new SPIClass(VSPI);
hspi = new SPIClass(HSPI);
//clock miso mosi ss
#ifndef ALTERNATE_PINS
//initialize vspi with default pins
//SCLK = 18, MISO = 19, MOSI = 23, SS = 5
vspi->begin();
#else
//alternatively route through GPIO pins of your choice
vspi->begin(VSPI_SCLK, VSPI_MISO, VSPI_MOSI, VSPI_SS); //SCLK, MISO, MOSI, SS
#endif
#ifndef ALTERNATE_PINS
//initialize hspi with default pins
//SCLK = 14, MISO = 12, MOSI = 13, SS = 15
hspi->begin();
#else
//alternatively route through GPIO pins
hspi->begin(HSPI_SCLK, HSPI_MISO, HSPI_MOSI, HSPI_SS); //SCLK, MISO, MOSI, SS
#endif
//set up slave select pins as outputs as the Arduino API
//doesn't handle automatically pulling SS low
pinMode(vspi->pinSS(), OUTPUT); //VSPI SS
pinMode(hspi->pinSS(), OUTPUT); //HSPI SS
}
// the loop function runs over and over again until power down or reset
void loop() {
//use the SPI buses
spiCommand(vspi, 0b01010101); // junk data to illustrate usage
spiCommand(hspi, 0b11001100);
delay(100);
}
void spiCommand(SPIClass *spi, byte data) {
//use it as you would the regular arduino SPI API
spi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(spi->pinSS(), LOW); //pull SS slow to prep other end for transfer
spi->transfer(data);
digitalWrite(spi->pinSS(), HIGH); //pull ss high to signify end of data transfer
spi->endTransaction();
}
Заключение
Эта статья была кратким и простым руководством, показывающим, как использовать SPI-связь с ESP32 с помощью ядра Arduino — при этом ESP32 выступает в роли контроллера (мастера).
Подводя итог, ESP32 имеет четыре шины SPI, но только две из них могут быть использованы для управления периферийными устройствами — HSPI и VSPI. Большинство плат ESP32 имеют предустановленные пины HSPI и VSPI, но вы всегда можете изменить назначение пинов в коде.
Вы можете использовать шины HSPI и VSPI одновременно для управления несколькими SPI-периферийными устройствами, или вы можете использовать несколько периферийных устройств на одной шине, при условии что их пин CS подключён к разным GPIO.
Мы не углублялись в примеры, потому что каждый датчик, библиотека и сценарий использования отличаются. Но теперь у вас должно быть лучшее представление о том, как подключить одно или несколько SPI-устройств к ESP32.
Для получения более подробной информации о драйвере SPI Master на ESP32 вы можете ознакомиться с официальной документацией espressif.
Мы не рассмотрели настройку ESP32 в качестве SPI-ведомого, но вы можете ознакомиться с этими примерами.
Надеемся, что это руководство окажется для вас полезным. У нас есть аналогичная статья, но о протоколе I2C. Ознакомьтесь с ней по следующей ссылке:
Узнайте больше об ESP32 с нашими ресурсами:
Источник: ESP32 SPI Communication: Set Pins, Multiple SPI Bus Interfaces, and Peripherals (Arduino IDE)