이 튜토리얼에서는 ESP32 의 SPI 통신 버스를 사용하는 방법을 알아봅니다 . 이를 통해 Arduino IDE를 사용하여 ESP32 보드와 마스터/슬레이브 SPI 통신을 수행하는 방법을 알아 봅니다. 또한, SPI 핀, ESP32에서 지원하는 여러 SPI 버스 인터페이스의 사용 방법 및 구성 방법을 살펴봅니다.
Arduino IDE를 사용한 ESP32 SPI 통신 튜토리얼
SPI 커뮤니케이션 소개
SPI는 직렬 주변 장치 인터페이스(Serial Peripheral Interface)의 약자로, 직렬 전이중 및 동기식 인터페이스입니다. 동기식 인터페이스는 데이터 전송 및 수신에 클럭 신호가 필요하다는 것을 의미합니다. 클럭 신호는 마스터와 슬레이브 간에 동기화됩니다. 비동기식 UART 통신 과 달리 , 클럭 신호는 슬레이브로 데이터를 전송할 시점과 읽을 준비가 될 시점을 제어합니다.
마스터 장치만 클록을 제어하고 모든 슬레이브 장치에 클록 신호를 제공할 수 있습니다. 클록 신호 없이는 데이터를 전송할 수 없습니다. 마스터와 슬레이브는 서로 데이터를 교환할 수 있으며, 주소 디코딩은 수행되지 않습니다.
두 장치 간 SPI 연결
마스터와 슬레이브는 모두 클록 신호의 상승 및 하강 에지에서 데이터를 교환할 수 있습니다. 아래 블록 다이어그램은 마스터와 슬레이브 간의 인터페이스를 보여줍니다. SPI 인터페이스는 3개 또는 4개의 신호로 구성됩니다. 하지만 일반적으로 4선 인터페이스를 사용하며, 네 번째 선은 슬레이브를 선택하는 데 사용됩니다.
SPI 통신 인터페이스
각 신호의 기능은 다음과 같습니다.
SCLK 또는 SCK 핀: 이 신호는 슬레이브에 클럭을 제공하며, 마스터만 클럭 신호를 제어할 수 있습니다. 이 핀은 유휴 상태, 즉 아무런 동작이 수행되지 않을 때는 비활성 상태 또는 3중 상태(Tri-State)로 유지됩니다.
- SS 또는 CS: 칩 선택 또는 슬레이브 선택 핀이라고 합니다. 이 라인은 마스터가 데이터를 전송할 슬레이브를 선택합니다.
- MOSI: 단방향 핀입니다. Master Output과 Slave Input 핀을 의미합니다. 이름에서 알 수 있듯이, 이 핀은 마스터에서 슬레이브로 데이터를 전송하는 데 사용됩니다.
- MISO: 마스터 입력과 슬레이브 출력이라고 합니다. 이 회선은 슬레이브에서 마스터로 데이터를 전송하는 데 사용됩니다.
간단히 말해, 이 통신 프로토콜에서 장치들은 마스터/슬레이브 모드로 데이터를 교환합니다. 마스터 장치는 주로 데이터 프레임의 시작을 담당합니다. 또한 마스터 장치는 데이터를 전송할 슬레이브 장치를 선택합니다. 칩 선택 라인은 일반적으로 특정 슬레이브 장치를 식별하거나 선택하는 데 사용됩니다.
마스터 장치가 슬레이브로 데이터를 전송하거나 슬레이브로부터 데이터를 수신하려고 할 때마다, 마스터는 클록 신호를 액티브 로우에서 액티브 하이 상태로 활성화하여 이를 수행합니다. 모든 마스터 장치는 MOSI 회선을 통해 데이터를 전송하고 MISO 회선을 통해 데이터를 수신합니다.
ESP32 SPI 통신 버스
ESP32에는 네 개의 SPi 버스가 있지만, 사용 가능한 버스는 HSPI와 VSPI 두 개뿐입니다. 앞서 언급했듯이 SPI 통신에서는 항상 하나의 컨트롤러(마스터라고도 함)가 존재하며, 이 컨트롤러는 다른 주변 장치(슬레이브라고도 함)를 제어합니다.
ESP32를 마스터 또는 슬레이브로 구성할 수 있습니다. 이 글의 끝부분에서 각 구성의 예를 살펴보겠습니다.
앞서 언급했듯이 SPI는 풀 듀플렉스 통신으로, 마스터와 슬레이브가 동시에 서로 데이터를 주고받을 수 있습니다.
ESP32 SPI 통신 튜토리얼
ESP32 SPI 컨트롤러를 사용하면 SD 카드, BME680, BME280 및 SPI 인터페이스를 통해 데이터를 제공하는 다른 센서와 같은 많은 슬레이브 주변 장치나 장치와 인터페이스할 수 있습니다.
ESP32 SPI 핀
앞서 설명했듯이 ESP32에는 SPI0, SPI1, SPI2, SPI3 등 네 개의 SPI 채널이 있습니다. 하지만 SPI0과 SP1은 칩의 플래시 메모리와 통신하기 위해 내부적으로 연결되어 있기 때문에 사용할 수 없습니다.
기본적으로 ESP32는 SPI2(HSPI)와 SPI3(VSPI) 두 개의 사용 가능한 SPI 통신 채널을 가지고 있으며, 아래 표는 두 채널의 기본 SPI 핀을 보여줍니다. 하지만 이 핀들을 아두이노나 esp-idf의 다른 GPIO 핀에도 매핑할 수 있습니다.
VSPI와 HSPI는 모두 별도의 버스 신호를 갖습니다. 따라서 마스터 또는 슬레이브로 각각 사용할 수 있습니다. 컨트롤러 모드에서는 각 버스가 최대 3개의 SPI 장치 또는 슬레이브를 제어할 수 있습니다.
ESP32 보드 기본 SPI 핀 찾기
참고: 다양한 유형의 ESP32 보드가 있습니다. 사용하는 ESP32 보드 마다 기본 SPI 핀이 다를 수 있습니다. 기본 핀에 대한 정보는 해당 보드의 데이터시트에서 확인할 수 있습니다. 기본 핀이 명시되어 있지 않은 경우, 아두이노 스케치를 사용하여 찾을 수 있습니다.
다음 코드를 Arduino IDE에 복사하여 ESP32 보드에 업로드하세요.
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.print("MOSI Pin: ");
Serial.println(MOSI);
Serial.print("MISO Pin: ");
Serial.println(MISO);
Serial.print("SCK Pin: ");
Serial.println(SCK);
Serial.print("SS Pin: ");
Serial.println(SS);
}
void loop() {
// put your main code here, to run repeatedly:
}
도구 > 보드에서 보드를 선택하세요. 위 코드를 업로드한 후 시리얼 모니터를 열고 ESP32 보드의 리셋 버튼을 클릭하세요.
ESP32 재설정 버튼 활성화
활성화/재설정 버튼을 누르자마자 직렬 모니터에서 기본 SPI 핀 번호를 볼 수 있습니다.
esp32 기본 SPI 핀
ESP32 SPI를 마스터(컨트롤러)로 사용
이 섹션에서는 ESP32 SPI를 마스터로 사용하여 슬레이브 역할을 하는 BME680 장치에서 데이터를 읽는 방법을 살펴보겠습니다.
다음 회로도에서 ESP32는 기본 SPI 핀을 사용하여 BME680에 연결됩니다.
esp32 spi를 마스터로 사용
다음 예제 코드에서 ESP32는 마스터 역할을 하며 SPI 통신을 통해 BME680 센서 데이터를 받습니다. ESP32와 BME680의 전체 가이드는 이 문서를 참조하세요.
Arduino IDE(가스, 압력, 온도, 습도)를 사용하는 ESP32가 있는 BME680
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"
#define SEALEVELPRESSURE_HPA (1013.25)
//Adafruit_BME680 bme; // I2C
Adafruit_BME680 bme(SS); // hardware SPI
Adafruit_BME680 bme(SS, MOSI, MISO, SCK);
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println(F("BME680 async test"));
if (!bme.begin()) {
Serial.println(F("Could not find a valid BME680 sensor, check wiring!"));
while (1);
}
// Set up oversampling and filter initialization
bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150); // 320*C for 150 ms
}
void loop() {
// Tell BME680 to begin measurement.
unsigned long endTime = bme.beginReading();
if (endTime == 0) {
Serial.println(F("Failed to begin reading :("));
return;
}
Serial.print(F("Reading started at "));
Serial.print(millis());
Serial.print(F(" and will finish at "));
Serial.println(endTime);
Serial.println(F("You can do other work during BME680 measurement."));
delay(50); // This represents parallel work.
// There's no need to delay() until millis() >= endTime: bme.endReading()
// takes care of that. It's okay for parallel work to take longer than
// BME680's measurement time.
// Obtain measurement results from BME680. Note that this operation isn't
// instantaneous even if milli() >= endTime due to I2C/SPI latency.
if (!bme.endReading()) {
Serial.println(F("Failed to complete reading :("));
return;
}
Serial.print(F("Reading completed at "));
Serial.println(millis());
Serial.print(F("Temperature = "));
Serial.print(bme.temperature);
Serial.println(F(" *C"));
Serial.print(F("Pressure = "));
Serial.print(bme.pressure / 100.0);
Serial.println(F(" hPa"));
Serial.print(F("Humidity = "));
Serial.print(bme.humidity);
Serial.println(F(" %"));
Serial.print(F("Gas = "));
Serial.print(bme.gas_resistance / 1000.0);
Serial.println(F(" KOhms"));
Serial.print(F("Approx. Altitude = "));
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(F(" m"));
Serial.println();
delay(2000);
}
위 코드에서 볼 수 있듯이, BME680 객체를 초기화하기 위해 기본 SPI 핀을 사용하고 있습니다.
Adafruit_BME680 bme(SS); // hardware SPI
Adafruit_BME680 bme(SS, MOSI, MISO, SCK);
ESP32 사용자 정의 SPI 핀 사용
기본 핀 대신 ESP32 SPI 버스에 대한 사용자 정의 핀을 쉽게 정의할 수 있습니다. Arduino IDE에서 센서 라이브러리를 사용하면 사용자 정의 핀을 매우 쉽게 정의할 수 있습니다. 하지만 나중에 ESP32 SPI 라이브러리에서 사용자 정의 핀을 정의하는 방법도 살펴보겠습니다.
여기에서는 BM680 라이브러리 생성자에 핀 번호를 전달한 것처럼 라이브러리 생성자에 핀 이름을 전달하여 사용자 정의 핀을 정의할 수 있습니다.
#define BME_SCK 25
#define BME_MISO 32
#define BME_MOSI 26
#define BME_CS 33
Adafruit_BME680 bme(BME_CS); // hardware SPI
Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
센서 라이브러리를 사용하지 않고 SPI 라이브러리를 직접 사용하려는 경우, 다음과 같이 사용자 정의 핀을 설정할 수 있습니다. 다음 코드에서는 HSPI 버스에는 기본 핀을, VSPI 버스에는 사용자 정의 핀을 사용합니다.
#include <SPI.h>
#define VSPI_MISO 2
#define VSPI_MOSI 4
#define VSPI_SCLK 0
#define VSPI_SS 33
void setup()
{
vspi = new SPIClass(VSPI);
hspi = new SPIClass(HSPI);
vspi->begin();
vspi->begin(VSPI_SCLK, VSPI_MISO, VSPI_MOSI, VSPI_SS); //SCLK, MISO, MOSI, SS)
}
여러 SPI 슬레이브와 함께 ESP32 SPI 마스터 사용
이 섹션에서는 ESP32 SPI 버스를 여러 SPI 슬레이브 장치와 함께 사용하는 방법을 살펴봅니다. 단일 SPI 마스터를 여러 슬레이브 장치와 함께 사용하려면 컨트롤러(마스터)에서 여러 개의 칩 선택(CS) 또는 슬레이브 선택(SS) 라인이 필요합니다.
아래 다이어그램에서 볼 수 있듯이 ESP32 SPI 컨트롤러가 하나 있고, 두 개의 SPI 슬레이브 장치가 동일한 MOSI, MISO, SCLK 핀을 사용하여 연결되어 있습니다. 하지만 각 슬레이브 장치에는 CS1과 CS2처럼 서로 다른 칩 선택 라인이 사용됩니다.
ESP32 SPI 단일 마스터 다중 슬레이브 통신
Arduino 스케치에서 특정 CS 라인을 액티브 로우로 만들어 통신하려는 슬레이브 장치를 선택합니다.
예를 들어, SPI 주변 장치 2와 통신하려면 CS2 라인을 액티브 로우(active low)로 설정하여 해당 주변 장치를 선택합니다. 또한, SPI 버스에서 다른 모든 칩 선택 라인도 액티브 하이(active high) 상태여야 합니다. 따라서 위 예에서 SPI 주변 장치 2와 통신하려면 CS1은 액티브 하이(active high) 상태이고 CS2는 액티브 로우(active low) 상태여야 합니다.
요약하자면, 하나의 SPI 마스터 컨트롤러에 여러 개의 슬레이브 장치를 사용하는 경우, 한 번에 하나의 슬레이브 장치와만 통신할 수 있으며 다른 슬레이브 장치는 유휴 상태가 됩니다. 여러 개의 SPI 장치와 동시에 통신하려면 여러 개의 SPI 컨트롤러를 사용해야 합니다.
두 개의 ESP32 SPI 버스 HSPI와 VSPI를 동시에 사용
앞서 설명했듯이 ESP32에는 HSPI와 VSPI라는 두 개의 사용 가능한 SPI 버스가 있습니다. 이 두 버스를 별도로 사용할 수 있습니다. 아래 그림에서 볼 수 있듯이 두 개의 SPI 주변 장치가 HSPI와 VSPI로 연결되어 있습니다.
ESP32 다중 SPI 버스 아두이노 IDE
ESP32 SPI 핀 섹션 에서 HSPI 및 VSPI 버스의 기본 핀에 대해 이미 살펴보았습니다 . 하지만 두 가지 모두에 기본 핀이나 사용자 지정 핀을 사용할 수 있습니다.
이제 Arduino 스케치에서 HSPI와 VSPI 버스를 구성, 초기화하고 사용하는 방법을 살펴보겠습니다.
먼저 ESP32 SPI 컨트롤러 라이브러리의 헤더 파일을 포함합니다.
#include <SPI.h>
참고: 이 라이브러리는 ESP32를 마스터 모드로만 구성할 수 있습니다. 다음 섹션에서는 ESP32 SPI를 슬레이브로 사용하는 방법과 별도의 ESP32 SPI 슬레이브 라이브러리를 설치하는 방법을 살펴보겠습니다.
SPI 마스터 라이브러리의 SPIClass에는 SPI 버스를 구성하고 사용하는 데 필요한 모든 함수 정의가 포함되어 있습니다. SPIClass 객체 두 개를 원하는 이름으로 생성합니다. 여기서는 vpsi_controller와 hspi_controller라는 두 객체를 정의하고, 두 SPI 컨트롤러의 이름을 클래스 생성자에 전달했습니다. 각 ESP32 SPI 컨트롤러마다 하나씩 생성했습니다.
vspi_controller = new SPIClass(VSPI);
hspi_controller = new SPIClass(HSPI);
이전 단계에서는 객체를 생성했지만, SPIClass 생성자는 SPI 컨트롤러만 구성했습니다. SPI 통신을 초기화하고 시작하려면 vspi_controller 및 hspi_controller 객체에서 begin() 메서드를 호출합니다. 이 메서드는 컨트롤러를 초기화하고 기본 SPI 핀에서 사용할 수 있도록 준비합니다.
vspi_controller->begin();
hspi_controller->begin();
사용자 정의 핀을 사용하려면 begin() 메서드에 사용자 정의 핀 번호를 전달할 수도 있습니다. 예를 들면 다음과 같습니다.
#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
vspi->begin(VSPI_CLK, VSPI_MISO, VSPI_MOSI, VSPI_SS);
hspi->begin(HSPI_CLK, HSPI_MISO, HSPI_MOSI, HSPI_SS);
사용자 정의 핀을 사용하는 경우 setup() 내부에서 CS/SS 핀을 출력 핀으로 선언해야 합니다.
pinMode(VSPI_SS, OUTPUT);
pinMode(HSPI_SS, OUTPUT);
이 ES32 SPI 다중 버스 아두이노 스케치를 참고하실 수 있습니다. 다음 섹션에서는 이 스케치를 사용하여 두 아두이노 보드 간의 SPI 통신을 구현해 보겠습니다.
#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
static const int spiClk = 1000000; // 1 MHz
//uninitalised pointers to SPI objects
SPIClass * vspi = NULL;
SPIClass * hspi = NULL;
void setup() {
//initialise 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
//initialise 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
//initialise 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_SS, OUTPUT); //VSPI SS
pinMode(HSPI_SS, OUTPUT); //HSPI SS
}
// the loop function runs over and over again until power down or reset
void loop() {
//use the SPI buses
vspiCommand();
hspiCommand();
delay(100);
}
void vspiCommand() {
byte data = 0b01010101; // junk data to illustrate usage
//use it as you would the regular arduino SPI API
vspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(VSPI_SS, LOW); //pull SS slow to prep other end for transfer
vspi->transfer(data);
digitalWrite(VSPI_SS, HIGH); //pull ss high to signify end of data transfer
vspi->endTransaction();
}
void hspiCommand() {
byte stuff = 0b11001100;
hspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(HSPI_SS, LOW);
hspi->transfer(stuff);
digitalWrite(HSPI_SS, HIGH);
hspi->endTransaction();
}
ESP32 SPI 마스터-슬레이브 통신 예제
이 섹션에서는 두 ESP32 보드 간의 SPI 통신을 수행하는 예를 살펴보겠습니다. 한 EPS32 는 마스터로, 다른 ESP32는 슬레이브로 설정합니다.
ESP32 컨트롤러에서 슬레이브 장치로 명령을 전송하여 온보드 LED를 제어합니다. 마스터 장치는 1초 지연 후 '0'과 '1'을 전송합니다. ESP32 슬레이브 장치는 이를 수신하여 온보드 LED를 켜고 끕니다.
다음 예제에서는 마스터와 슬레이브 모두에 HPSI 버스와 기본 핀을 사용했습니다. ESP32 보드 두 개를 사용하여 아래 표에 표시된 연결 방식에 따라 서로 연결합니다.
ESP32 SPI 마스터 스케치
이 Arduino 스케치는 1초 지연으로 HSPI 컨트롤러를 사용하여 SPI 버스에서 온/오프 바이트를 전송합니다.
#include <SPI.h>
// Define ALTERNATE_PINS to use non-standard GPIO pins for SPI bus
#ifdef ALTERNATE_PINS
#define HSPI_MISO 26
#define HSPI_MOSI 27
#define HSPI_SCLK 25
#define HSPI_SS 32
#else
#define HSPI_MISO MISO
#define HSPI_MOSI MOSI
#define HSPI_SCLK SCK
#define HSPI_SS SS
#endif
static const int spiClk = 1000000; // 1 MHz
//uninitalised pointers to SPI objects
SPIClass * hspi = NULL;
void setup() {
//initialise instance of the SPIClass attached to HSPI
hspi = new SPIClass(HSPI);
//clock miso mosi ss
#ifndef ALTERNATE_PINS
//initialise 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(HSPI_SS, OUTPUT); //HSPI SS
}
// the loop function runs over and over again until power down or reset
void loop() {
hspi_send_command();
delay(100);
}
void hspi_send_command() {
byte data_on = 0b00000001; // data 1 to turn on LED of slave
byte data_off = 0b0000000; // data 0 to turn off LED of slave
hspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(HSPI_SS, LOW);
hspi->transfer(data_on);
digitalWrite(HSPI_SS, HIGH);
hspi->endTransaction();
delay(1000);
hspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(HSPI_SS, LOW);
hspi->transfer(data_off);
digitalWrite(HSPI_SS, HIGH);
hspi->endTransaction();
delay(1000);
}
코드는 어떻게 작동하나요?
먼저 ESP32 SPI 컨트롤러 라이브러리의 헤더 파일을 포함합니다.
#include <SPI.h>
다음으로, HSPI 핀을 정의합니다. 여기서는 기본 핀과 사용자 지정 핀을 모두 정의했습니다. 이 예제에서는 기본 핀을 사용합니다. 하지만 사용자 지정 핀을 사용하려면 #include <SPI.h> 뒤에 #define을 사용하여 ALTERNATE_PINS를 정의하여 활성화할 수 있습니다.
#ifdef ALTERNATE_PINS
#define HSPI_MISO 26
#define HSPI_MOSI 27
#define HSPI_SCLK 25
#define HSPI_SS 32
#else
#define HSPI_MISO MISO
#define HSPI_MOSI MOSI
#define HSPI_SCLK SCK
#define HSPI_SS SS
#endif
SPI 버스의 클럭 속도를 정의하고 여기서는 1MHz로 정의합니다.
static const int spiClk = 1000000; // 1 MHz
여기서는 hpsi라는 이름의 SPI 클래스 객체를 정의하고 HSPI 컨트롤러의 이름을 컨트롤러를 구성할 클래스 생성자에 전달했습니다.
SPIClass * hspi = NULL;
hspi = new SPIClass(HSPI);
SPI 통신을 초기화하고 시작하려면 hspi 객체에서 begin() 메서드를 호출합니다. 이 메서드는 컨트롤러를 초기화하고 기본 핀이나 사용자 정의 핀에서 사용할 수 있도록 준비합니다.
//clock miso mosi ss
#ifndef ALTERNATE_PINS
//initialise 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
루프 내부에서 100ms마다 hspi_send_command() 함수를 호출합니다. 이 함수는 1초마다 '1'과 '0'을 전송합니다.
void hspi_send_command() {
byte data_on = 0b00000001; // data 1 to turn on LED of slave
byte data_off = 0b0000000; // data 0 to turn off LED of slave
hspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(HSPI_SS, LOW);
hspi->transfer(data_on);
digitalWrite(HSPI_SS, HIGH);
hspi->endTransaction();
delay(1000);
hspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(HSPI_SS, LOW);
hspi->transfer(data_off);
digitalWrite(HSPI_SS, HIGH);
hspi->endTransaction();
delay(1000);
}
ESP32 SPI 슬레이브 스케치
앞서 설명했듯이 SPI.h 라이브러리는 ESP32를 마스터 모드 또는 컨트롤러 모드로만 설정합니다. ESP32를 슬레이브 또는 주변 장치로 사용하려면 ESP32SPISlave 라이브러리를 설치해야 합니다.
ESP32 SPI Slave 라이브러리 설치
Arduino IDE를 열고 스케치 > 라이브러리 > 라이브러리 관리를 클릭합니다 .
라이브러리 관리 옵션을 클릭하면 이 창이 나타납니다. 이 창의 검색창 에 ' ESP32SPISlave '를 입력하고 Enter 키를 누르세요.
강조 표시된 라이브러리를 선택하고 설치를 클릭하세요.
ESP32 SPI 슬레이브 라이브러리 Arduino IDE
이제 다음 아두이노 스케치를 복사하여 ESP32 슬레이브에 업로드하세요. 다음 코드는 마스터에서 차단 모드로 SPI 데이터를 읽습니다. 컨트롤러에서 수신한 데이터에 따라 보드에 내장된 LED를 켜고 끕니다.
#include <ESP32SPISlave.h>
ESP32SPISlave slave;
static constexpr uint32_t BUFFER_SIZE {32};
uint8_t spi_slave_tx_buf[BUFFER_SIZE];
uint8_t spi_slave_rx_buf[BUFFER_SIZE];
#define LED 2
void setup() {
Serial.begin(115200);
delay(2000);
pinMode(LED, OUTPUT);
// begin() after setting
// HSPI = CS: 15, CLK: 14, MOSI: 13, MISO: 12 -> default
// VSPI = CS: 5, CLK: 18, MOSI: 23, MISO: 19
slave.setDataMode(SPI_MODE0);
slave.begin();
// slave.begin(VSPI); // you can use VSPI like this
// clear buffers
memset(spi_slave_tx_buf, 0, BUFFER_SIZE);
memset(spi_slave_rx_buf, 0, BUFFER_SIZE);
}
void loop() {
// block until the transaction comes from master
slave.wait(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE);
// if transaction has completed from master,
// available() returns size of results of transaction,
// and buffer is automatically updated
char data;
while (slave.available()) {
// show received data
Serial.print("Command Received: ");
Serial.println(spi_slave_rx_buf[0]);
data = spi_slave_rx_buf[0];
slave.pop();
}
if(data == 1 )
{
Serial.println("Setting LED active HIGH ");
digitalWrite(LED, HIGH);
}
else if(data == 0 )
{
Serial.println("Setting LED active LOW ");
digitalWrite(LED, LOW);
}
Serial.println("");
}
코드는 어떻게 작동하나요?
먼저 ESP32 SPI 주변 장치나 슬레이브 라이브러리의 헤더 파일을 포함합니다.
#include <ESP32SPISlave.h>
ESP32SPISlave 클래스의 객체를 slave라는 이름으로 생성합니다.
ESP32SPISlave slave;
ESP32 SPI 슬레이브의 MOSI 핀에서 수신된 데이터를 저장하기 위한 버퍼를 정의합니다.
static constexpr uint32_t BUFFER_SIZE {32};
uint8_t spi_slave_tx_buf[BUFFER_SIZE];
uint8_t spi_slave_rx_buf[BUFFER_SIZE];
#define 전처리기 지시어를 사용하여 온보드 LED의 이름을 정의합니다. 이 이름을 사용하여 ESP32의 온보드 LED를 구성하고 제어합니다.
#define LED 2
115200의 전송 속도로 직렬 통신을 초기화합니다. 이를 사용하여 HSPI 슬레이브가 수신할 직렬 모니터에 데이터를 인쇄합니다.
Serial.begin(115200);
ESP32의 온보드 LED를 GPIO2에 연결된 디지털 출력 핀으로 구성합니다.
pinMode(LED, OUTPUT);
슬레이브 객체의 데이터 모드를 설정합니다.
slave.setDataMode(SPI_MODE0);
HSPI를 초기화하고 시작하려면 hspi 객체에서 begin() 메서드를 호출합니다. 이 메서드는 HSPI 버스를 초기화하고 기본 핀에서 사용할 수 있도록 salve 모드로 설정합니다.
slave.begin();
버퍼를 지우고 0으로 초기화합니다.
memset(spi_slave_tx_buf, 0, BUFFER_SIZE);
memset(spi_slave_rx_buf, 0, BUFFER_SIZE);
for 루프 내부에서 ESP32 마스터로부터 데이터 트랜잭션이 올 때까지 차단 상태를 기다립니다.
slave.wait(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE);
마스터에서 트랜잭션이 완료되면 available() 함수는 트랜잭션 결과의 크기를 반환하고, 버퍼는 자동으로 업데이트됩니다. 그 후, 수신된 바이트를 시리얼 모니터로 전송하여 표시합니다. 또한, 이 바이트를 데이터 변수에 저장합니다.
char data;
while (slave.available())
{
// show received data
Serial.print("Command Received: ");
Serial.println(spi_slave_rx_buf[0]);
data = spi_slave_rx_buf[0];
slave.pop();
}
이 if 문 블록은 수신된 데이터가 '1'이면 LED를 켜고, '0'이면 LED를 끕니다. 또한, 직렬 모니터에 LED 상태를 출력합니다.
if(data == 1 )
{
Serial.println("Setting LED active HIGH ");
digitalWrite(LED, HIGH);
}
else if(data == 0 )
{
Serial.println("Setting LED active LOW ");
digitalWrite(LED, LOW);
}
두 ESP32 보드에 Arduino 스케치를 업로드한 후 슬레이브 장치의 직렬 모니터를 열면 직렬 모니터에 다음 메시지가 표시됩니다.
두 보드 간 ESP32 SPI 마스터 슬레이브 통신
ESP32 슬레이브의 온보드 LED가 1초 지연으로 켜지고 꺼지는 것도 볼 수 있습니다.
Arduino IDE 마스터 슬레이브를 사용한 두 ESP32 간 SPI 통신
비디오 데모: 아래 첨부 링크 참고
다음 내용도 읽어보시기 바랍니다.
- ESP32 I2C 통신 설정 핀, 다중 장치 인터페이스 및 변경 핀
- ESP32 UART 통신 설명(예제 포함)
- Arduino IDE를 사용한 ESP32 MQTT 게시 및 구독 DHT22 판독
- Arduino IDE를 사용한 ESP32 MQTT 게시 및 구독 - 제어 출력
- SPIFFS(SPI 플래시 파일 시스템)를 탑재한 ESP32 웹 서버
- VS Code 및 PlatformIO IDE를 사용하여 ESP32 SPIFFS 파일 시스템에 파일 업로드
- Arduino IDE에 ESP32 파일 시스템 업로더 설치 – SPIFFS
다른 SPI 튜토리얼:
튜토리얼의 출처는 다음 링크를 참고하세요.
'ESP32' 카테고리의 다른 글
RTSP를 이용한 Arduino ESP32 오디오 스트리밍 (0) | 2025.10.01 |
---|---|
ESP32C3 시작하기 ESP32-C3-DevKitC-02 보드 3 (0) | 2025.09.30 |
ESP32C3 Ultra-Low Power SoCs 소개 2 (0) | 2025.09.30 |
ESP32C3 Ultra-Low Power SoCs 소개 1 (0) | 2025.09.30 |
ESP32 WROOM32 GPIO 할당 테이블 양식 한글 파일 (0) | 2025.09.28 |
ESP32-WROOM-32 핀아웃 참조 Pinout (0) | 2025.09.22 |
esp32 충돌센서 방향 지시등 구현 (0) | 2025.09.19 |
ESP32S3 Super Mini GPIO 핀 사용법 5 (0) | 2025.09.15 |
더욱 좋은 정보를 제공하겠습니다.~ ^^