forked from bbulkow/FastLED-idf
169 lines
6.0 KiB
C++
169 lines
6.0 KiB
C++
#pragma once
|
|
#pragma message "ESP32 Hardware SPI support added"
|
|
|
|
FASTLED_NAMESPACE_BEGIN
|
|
|
|
/*
|
|
* ESP32 Hardware SPI Driver
|
|
*
|
|
* Copyright (c) 2020 Nick Wallace
|
|
* Derived from code for ESP8266 hardware SPI by Benoit Anastay.
|
|
*
|
|
* This hardware SPI implementation can drive clocked LEDs from either the
|
|
* VSPI or HSPI bus (aka SPI2 & SPI3). No support is provided for SPI1, because it is
|
|
* shared among devices and the cache for data (code) in the Flash as well as the PSRAM.
|
|
*
|
|
* To enable the hardware SPI driver, add the following line *before* including
|
|
* FastLED.h:
|
|
*
|
|
* #define FASTLED_ALL_PINS_HARDWARE_SPI
|
|
*
|
|
* This driver uses the VSPI bus by default (GPIO 18, 19, 23, & 5). To use the
|
|
* HSPI bus (GPIO 14, 12, 13, & 15) add the following line *before* including
|
|
* FastLED.h:
|
|
*
|
|
* #define FASTLED_ESP32_SPI_BUS HSPI
|
|
*
|
|
*/
|
|
/*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#ifndef FASTLED_ESP32_SPI_BUS
|
|
#define FASTLED_ESP32_SPI_BUS VSPI
|
|
#endif
|
|
|
|
SPIClass ledSPI(FASTLED_ESP32_SPI_BUS);
|
|
|
|
#if FASTLED_ESP32_SPI_BUS == VSPI
|
|
static uint8_t spiClk = 18;
|
|
static uint8_t spiMiso = 19;
|
|
static uint8_t spiMosi = 23;
|
|
static uint8_t spiCs = 5;
|
|
#elif FASTLED_ESP32_SPI_BUS == HSPI
|
|
static uint8_t spiClk = 14;
|
|
static uint8_t spiMiso = 12;
|
|
static uint8_t spiMosi = 13;
|
|
static uint8_t spiCs = 15;
|
|
#endif
|
|
|
|
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED>
|
|
class ESP32SPIOutput {
|
|
Selectable *m_pSelect;
|
|
|
|
public:
|
|
ESP32SPIOutput() { m_pSelect = NULL; }
|
|
ESP32SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
|
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
|
|
|
void init() {
|
|
// set the pins to output and make sure the select is released (which apparently means hi? This is a bit
|
|
// confusing to me)
|
|
ledSPI.begin(spiClk, spiMiso, spiMosi, spiCs);
|
|
release();
|
|
}
|
|
|
|
// stop the SPI output. Pretty much a NOP with software, as there's no registers to kick
|
|
static void stop() { }
|
|
|
|
// wait until the SPI subsystem is ready for more data to write. A NOP when bitbanging
|
|
static void wait() __attribute__((always_inline)) { }
|
|
static void waitFully() __attribute__((always_inline)) { wait(); }
|
|
|
|
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
|
|
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }
|
|
|
|
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
|
|
|
|
// naive writeByte implelentation, simply calls writeBit on the 8 bits in the byte.
|
|
static void writeByte(uint8_t b) {
|
|
ledSPI.transfer(b);
|
|
}
|
|
|
|
public:
|
|
|
|
// select the SPI output (TODO: research whether this really means hi or lo. Alt TODO: move select responsibility out of the SPI classes
|
|
// entirely, make it up to the caller to remember to lock/select the line?)
|
|
void select() {
|
|
ledSPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE0));
|
|
if(m_pSelect != NULL) { m_pSelect->select(); }
|
|
}
|
|
|
|
// release the SPI line
|
|
void release() {
|
|
if(m_pSelect != NULL) { m_pSelect->release(); }
|
|
ledSPI.endTransaction();
|
|
}
|
|
|
|
// Write out len bytes of the given value out over ledSPI. Useful for quickly flushing, say, a line of 0's down the line.
|
|
void writeBytesValue(uint8_t value, int len) {
|
|
select();
|
|
writeBytesValueRaw(value, len);
|
|
release();
|
|
}
|
|
|
|
static void writeBytesValueRaw(uint8_t value, int len) {
|
|
while(len--) {
|
|
ledSPI.transfer(value);
|
|
}
|
|
}
|
|
|
|
// write a block of len uint8_ts out. Need to type this better so that explicit casts into the call aren't required.
|
|
// note that this template version takes a class parameter for a per-byte modifier to the data.
|
|
template <class D> void writeBytes(FASTLED_REGISTER uint8_t *data, int len) {
|
|
select();
|
|
uint8_t *end = data + len;
|
|
while(data != end) {
|
|
writeByte(D::adjust(*data++));
|
|
}
|
|
D::postBlock(len);
|
|
release();
|
|
}
|
|
|
|
// default version of writing a block of data out to the SPI port, with no data modifications being made
|
|
void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
|
|
|
// write a single bit out, which bit from the passed in byte is determined by template parameter
|
|
template <uint8_t BIT> inline void writeBit(uint8_t b) {
|
|
ledSPI.transfer(b);
|
|
}
|
|
|
|
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
|
// parameters indicate how many uint8_ts to skip at the beginning of each grouping, as well as a class specifying a per
|
|
// byte of data modification to be made. (See DATA_NOP above)
|
|
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> pixels) {
|
|
select();
|
|
int len = pixels.mLen;
|
|
while(pixels.has(1)) {
|
|
if(FLAGS & FLAG_START_BIT) {
|
|
writeBit<0>(1);
|
|
}
|
|
writeByte(D::adjust(pixels.loadAndScale0()));
|
|
writeByte(D::adjust(pixels.loadAndScale1()));
|
|
writeByte(D::adjust(pixels.loadAndScale2()));
|
|
pixels.advanceData();
|
|
pixels.stepDithering();
|
|
}
|
|
D::postBlock(len);
|
|
release();
|
|
}
|
|
};
|
|
|
|
FASTLED_NAMESPACE_END
|