This commit is contained in:
2026-05-22 21:52:50 +03:00
commit be7c60e4dd
1854 changed files with 583428 additions and 0 deletions
@@ -0,0 +1,98 @@
/* 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();
}
@@ -0,0 +1,2 @@
requires:
- CONFIG_SOC_SPI_PERIPH_NUM=[2-9]
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

+36
View File
@@ -0,0 +1,36 @@
#######################################
# Syntax Coloring Map SPI
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SPI KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
end KEYWORD2
transfer KEYWORD2
setBitOrder KEYWORD2
setDataMode KEYWORD2
setClockDivider KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
SPI_CLOCK_DIV4 LITERAL1
SPI_CLOCK_DIV16 LITERAL1
SPI_CLOCK_DIV64 LITERAL1
SPI_CLOCK_DIV128 LITERAL1
SPI_CLOCK_DIV2 LITERAL1
SPI_CLOCK_DIV8 LITERAL1
SPI_CLOCK_DIV32 LITERAL1
SPI_CLOCK_DIV64 LITERAL1
SPI_MODE0 LITERAL1
SPI_MODE1 LITERAL1
SPI_MODE2 LITERAL1
SPI_MODE3 LITERAL1
+9
View File
@@ -0,0 +1,9 @@
name=SPI
version=3.3.7
author=Hristo Gochkov
maintainer=Hristo Gochkov <hristo@espressif.com>
sentence=Enables the communication with devices that use the Serial Peripheral Interface (SPI) Bus. For all Arduino boards, BUT Arduino DUE.
paragraph=
category=Signal Input/Output
url=http://arduino.cc/en/Reference/SPI
architectures=esp32
+360
View File
@@ -0,0 +1,360 @@
/*
SPI.cpp - SPI library for esp8266
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "SPI.h"
#if SOC_GPSPI_SUPPORTED
#include "io_pin_remap.h"
#include "esp32-hal-log.h"
#if !CONFIG_DISABLE_HAL_LOCKS
#define SPI_PARAM_LOCK() \
do { \
} while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS)
#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock)
#else
#define SPI_PARAM_LOCK()
#define SPI_PARAM_UNLOCK()
#endif
SPIClass::SPIClass(uint8_t spi_bus)
: _spi_num(spi_bus), _spi(NULL), _use_hw_ss(false), _sck(-1), _miso(-1), _mosi(-1), _ss(-1), _div(0), _freq(1000000), _inTransaction(false)
#if !CONFIG_DISABLE_HAL_LOCKS
,
paramLock(NULL) {
if (paramLock == NULL) {
paramLock = xSemaphoreCreateMutex();
if (paramLock == NULL) {
log_e("xSemaphoreCreateMutex failed");
return;
}
}
}
#else
{
}
#endif
SPIClass::~SPIClass() {
end();
#if !CONFIG_DISABLE_HAL_LOCKS
if (paramLock != NULL) {
vSemaphoreDelete(paramLock);
paramLock = NULL;
}
#endif
}
bool SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) {
if (_spi) {
return true;
}
if (!_div) {
_div = spiFrequencyToClockDiv(NULL, _freq);
}
_spi = spiStartBus(_spi_num, _div, SPI_MODE0, SPI_MSBFIRST);
if (!_spi) {
log_e("SPI bus %d start failed.", _spi_num);
return false;
}
#if defined(CONFIG_IDF_TARGET_ESP32P4)
// ESP32P4: Ensure the divider and clock source info are recalculated and stored correctly.
uint32_t recalculated_div = spiFrequencyToClockDiv(_spi, _freq);
if (recalculated_div != _div) {
// Update divider and clock source info if changed
_div = recalculated_div;
spiSetClockDiv(_spi, _div);
} else {
// Store correct clock source info
_div = recalculated_div;
}
#endif
if (sck == -1 && miso == -1 && mosi == -1 && ss == -1) {
#if CONFIG_IDF_TARGET_ESP32
_sck = (_spi_num == VSPI) ? SCK : 14;
_miso = (_spi_num == VSPI) ? MISO : 12;
_mosi = (_spi_num == VSPI) ? MOSI : 13;
_ss = (_spi_num == VSPI) ? SS : 15;
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
_sck = (_spi_num == FSPI) ? SCK : -1;
_miso = (_spi_num == FSPI) ? MISO : -1;
_mosi = (_spi_num == FSPI) ? MOSI : -1;
_ss = (_spi_num == FSPI) ? SS : -1;
#else
_sck = SCK;
_miso = MISO;
_mosi = MOSI;
_ss = SS;
#endif
} else {
_sck = sck;
_miso = miso;
_mosi = mosi;
_ss = ss;
}
if (!spiAttachSCK(_spi, _sck)) {
goto err;
}
if (_miso >= 0 && !spiAttachMISO(_spi, _miso)) {
goto err;
}
if (_mosi >= 0 && !spiAttachMOSI(_spi, _mosi)) {
goto err;
}
return true;
err:
log_e("Attaching pins to SPI failed.");
return false;
}
void SPIClass::end() {
if (!_spi) {
return;
}
spiDetachSCK(_spi);
spiDetachMISO(_spi);
spiDetachMOSI(_spi);
setHwCs(false);
if (spiGetClockDiv(_spi) != 0) {
spiStopBus(_spi);
}
_spi = NULL;
}
void SPIClass::setHwCs(bool use) {
if (_ss < 0) {
return;
}
if (use && !_use_hw_ss) {
spiAttachSS(_spi, 0, _ss);
spiSSEnable(_spi);
} else if (!use && _use_hw_ss) {
spiSSDisable(_spi);
spiDetachSS(_spi);
}
_use_hw_ss = use;
}
void SPIClass::setSSInvert(bool invert) {
if (_spi) {
spiSSInvert(_spi, invert);
}
}
void SPIClass::setFrequency(uint32_t freq) {
SPI_PARAM_LOCK();
//check if last freq changed
uint32_t cdiv = spiGetClockDiv(_spi);
if (_freq != freq || _div != cdiv) {
_freq = freq;
_div = spiFrequencyToClockDiv(_spi, _freq);
spiSetClockDiv(_spi, _div);
}
SPI_PARAM_UNLOCK();
}
void SPIClass::setClockDivider(uint32_t clockDiv) {
SPI_PARAM_LOCK();
_div = clockDiv;
spiSetClockDiv(_spi, _div);
SPI_PARAM_UNLOCK();
}
uint32_t SPIClass::getClockDivider() {
return spiGetClockDiv(_spi);
}
void SPIClass::setDataMode(uint8_t dataMode) {
spiSetDataMode(_spi, dataMode);
}
void SPIClass::setBitOrder(uint8_t bitOrder) {
spiSetBitOrder(_spi, bitOrder);
}
void SPIClass::beginTransaction(SPISettings settings) {
SPI_PARAM_LOCK();
//check if last freq changed
uint32_t cdiv = spiGetClockDiv(_spi);
if (_freq != settings._clock || _div != cdiv) {
_freq = settings._clock;
_div = spiFrequencyToClockDiv(_spi, _freq);
}
spiTransaction(_spi, _div, settings._dataMode, settings._bitOrder);
_inTransaction = true;
}
void SPIClass::endTransaction() {
if (_inTransaction) {
_inTransaction = false;
spiEndTransaction(_spi);
SPI_PARAM_UNLOCK(); // <-- Im not sure should it be here or right after spiTransaction()
}
}
void SPIClass::write(uint8_t data) {
if (_inTransaction) {
return spiWriteByteNL(_spi, data);
}
spiWriteByte(_spi, data);
}
uint8_t SPIClass::transfer(uint8_t data) {
if (_inTransaction) {
return spiTransferByteNL(_spi, data);
}
return spiTransferByte(_spi, data);
}
void SPIClass::write16(uint16_t data) {
if (_inTransaction) {
return spiWriteShortNL(_spi, data);
}
spiWriteWord(_spi, data);
}
uint16_t SPIClass::transfer16(uint16_t data) {
if (_inTransaction) {
return spiTransferShortNL(_spi, data);
}
return spiTransferWord(_spi, data);
}
void SPIClass::write32(uint32_t data) {
if (_inTransaction) {
return spiWriteLongNL(_spi, data);
}
spiWriteLong(_spi, data);
}
uint32_t SPIClass::transfer32(uint32_t data) {
if (_inTransaction) {
return spiTransferLongNL(_spi, data);
}
return spiTransferLong(_spi, data);
}
void SPIClass::transferBits(uint32_t data, uint32_t *out, uint8_t bits) {
if (_inTransaction) {
return spiTransferBitsNL(_spi, data, out, bits);
}
spiTransferBits(_spi, data, out, bits);
}
/**
* @param data uint8_t *
* @param size uint32_t
*/
void SPIClass::writeBytes(const uint8_t *data, uint32_t size) {
if (_inTransaction) {
return spiWriteNL(_spi, data, size);
}
spiSimpleTransaction(_spi);
spiWriteNL(_spi, data, size);
spiEndTransaction(_spi);
}
void SPIClass::transfer(void *data, uint32_t size) {
transferBytes((const uint8_t *)data, (uint8_t *)data, size);
}
/**
* @param data void *
* @param size uint32_t
*/
void SPIClass::writePixels(const void *data, uint32_t size) {
if (_inTransaction) {
return spiWritePixelsNL(_spi, data, size);
}
spiSimpleTransaction(_spi);
spiWritePixelsNL(_spi, data, size);
spiEndTransaction(_spi);
}
/**
* @param data uint8_t * data buffer. can be NULL for Read Only operation
* @param out uint8_t * output buffer. can be NULL for Write Only operation
* @param size uint32_t
*/
void SPIClass::transferBytes(const uint8_t *data, uint8_t *out, uint32_t size) {
if (_inTransaction) {
return spiTransferBytesNL(_spi, data, out, size);
}
spiTransferBytes(_spi, data, out, size);
}
/**
* @param data uint8_t *
* @param size uint8_t max for size is 64Byte
* @param repeat uint32_t
*/
void SPIClass::writePattern(const uint8_t *data, uint8_t size, uint32_t repeat) {
if (size > 64) {
return; //max Hardware FIFO
}
uint32_t byte = (size * repeat);
uint8_t r = (64 / size);
const uint8_t max_bytes_FIFO = r * size; // Max number of whole patterns (in bytes) that can fit into the hardware FIFO
while (byte) {
if (byte > max_bytes_FIFO) {
writePattern_(data, size, r);
byte -= max_bytes_FIFO;
} else {
writePattern_(data, size, (byte / size));
byte = 0;
}
}
}
void SPIClass::writePattern_(const uint8_t *data, uint8_t size, uint8_t repeat) {
uint8_t bytes = (size * repeat);
uint8_t buffer[64];
uint8_t *bufferPtr = &buffer[0];
const uint8_t *dataPtr;
uint8_t dataSize = bytes;
for (uint8_t i = 0; i < repeat; i++) {
dataSize = size;
dataPtr = data;
while (dataSize--) {
*bufferPtr = *dataPtr;
dataPtr++;
bufferPtr++;
}
}
writeBytes(&buffer[0], bytes);
}
#if CONFIG_IDF_TARGET_ESP32
SPIClass SPI(VSPI);
#else
SPIClass SPI(FSPI);
#endif
#endif /* SOC_GPSPI_SUPPORTED */
+106
View File
@@ -0,0 +1,106 @@
/*
SPI.h - SPI library for esp32
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SPI_H_INCLUDED
#define _SPI_H_INCLUDED
#include "soc/soc_caps.h"
#if SOC_GPSPI_SUPPORTED
#include <stdlib.h>
#include "pins_arduino.h"
#include "esp32-hal-spi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#define SPI_HAS_TRANSACTION
class SPISettings {
public:
SPISettings() : _clock(1000000), _bitOrder(SPI_MSBFIRST), _dataMode(SPI_MODE0) {}
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) : _clock(clock), _bitOrder(bitOrder), _dataMode(dataMode) {}
uint32_t _clock;
uint8_t _bitOrder;
uint8_t _dataMode;
};
class SPIClass {
private:
int8_t _spi_num;
spi_t *_spi;
bool _use_hw_ss;
int8_t _sck;
int8_t _miso;
int8_t _mosi;
int8_t _ss;
uint32_t _div;
uint32_t _freq;
bool _inTransaction;
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t paramLock = NULL;
#endif
void writePattern_(const uint8_t *data, uint8_t size, uint8_t repeat);
public:
SPIClass(uint8_t spi_bus = HSPI);
~SPIClass();
bool begin(int8_t sck = -1, int8_t miso = -1, int8_t mosi = -1, int8_t ss = -1);
void end();
void setHwCs(bool use);
void setSSInvert(bool invert); //use before setHwCS for change to be used by setHwCs
void setBitOrder(uint8_t bitOrder);
void setDataMode(uint8_t dataMode);
void setFrequency(uint32_t freq);
void setClockDivider(uint32_t clockDiv);
uint32_t getClockDivider();
void beginTransaction(SPISettings settings);
void endTransaction(void);
void transfer(void *data, uint32_t size);
uint8_t transfer(uint8_t data);
uint16_t transfer16(uint16_t data);
uint32_t transfer32(uint32_t data);
void transferBytes(const uint8_t *data, uint8_t *out, uint32_t size);
void transferBits(uint32_t data, uint32_t *out, uint8_t bits);
void write(uint8_t data);
void write16(uint16_t data);
void write32(uint32_t data);
void writeBytes(const uint8_t *data, uint32_t size);
void writePixels(const void *data, uint32_t size); //ili9341 compatible
void writePattern(const uint8_t *data, uint8_t size, uint32_t repeat);
spi_t *bus() {
return _spi;
}
int8_t pinSS() {
return _ss;
}
};
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPI)
extern SPIClass SPI;
#endif
#endif /* SOC_GPSPI_SUPPORTED */
#endif /* _SPI_H_INCLUDED */