ESP32 SPI: настройка пинов, несколько шин и периферийных устройств (Arduino IDE)

Это простое руководство о протоколе SPI-связи с ESP32 с использованием Arduino IDE. Мы рассмотрим пины SPI на ESP32, как подключать SPI-устройства, как задавать пользовательские пины SPI, как использовать несколько SPI-устройств и многое другое.

ESP32 SPI Communication Set Pins Multiple SPI Bus Interfaces and Peripherals Arduino IDE

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

Также вы можете использовать VS Code с расширением PlatformIO для программирования ваших плат с использованием ядра Arduino:

Введение в протокол SPI-связи ESP32

SPI расшифровывается как Serial Peripheral Interface (последовательный периферийный интерфейс), и это синхронный протокол последовательной передачи данных, используемый микроконтроллерами для связи с одним или несколькими периферийными устройствами. Например, ваша плата ESP32, связывающаяся с датчиком, поддерживающим SPI, или с другим микроконтроллером.

В SPI-связи всегда есть контроллер (также называемый мастером), который управляет периферийными устройствами (также называемыми ведомыми). Данные могут отправляться и приниматься одновременно. Это означает, что мастер может отправлять данные ведомому, а ведомый может отправлять данные мастеру в одно и то же время.

Introducing ESP32 SPI Communication Protocol

У вас может быть только один мастер, которым будет микроконтроллер (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.

ESP32 Finding out the Default SPI pins

Использование пользовательских пинов 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.

SPI communication multiple peripherals same bus

Чтобы выбрать периферийное устройство, с которым вы хотите связаться, вы должны установить его пин 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 по умолчанию или пользовательские пины.

ESP32 multiple SPI bus interfaces

Кратко, чтобы использовать 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)