forked from Makuna/NeoPixelBus
Method File Cleanup (#683)
This commit is contained in:
1 parent
3a96023c56
commit
c74febefef
32 files changed
+1016
-985
No files matched your search
@@ -0,0 +1,332 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for DotStars using Esp32, DMA and SPI (APA102).
|
||||
|
||||
Written by Michael C. Miller.
|
||||
DotStarEsp32DmaSpiMethod written by Louis Beaudoin (Pixelvation)
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "driver/spi_master.h"
|
||||
|
||||
// API and type use require newer IDF versions
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 1)
|
||||
|
||||
template<typename T_SPISPEED, typename T_SPIBUS> class DotStarEsp32DmaSpiMethodBase
|
||||
{
|
||||
public:
|
||||
typedef typename T_SPISPEED::SettingsObject SettingsObject;
|
||||
|
||||
DotStarEsp32DmaSpiMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeStartFrame(4 * T_SPIBUS::ParallelBits),
|
||||
_sizePixelData(pixelCount * elementSize + settingsSize),
|
||||
_sizeEndFrame((pixelCount + 15) / 16 * T_SPIBUS::ParallelBits) // 16 = div 2 (bit for every two pixels) div 8 (bits to bytes)
|
||||
{
|
||||
_spiBufferSize = _sizeStartFrame + _sizePixelData + _sizeEndFrame;
|
||||
|
||||
// must have a 4 byte aligned buffer for i2s
|
||||
uint32_t alignment = _spiBufferSize % 4;
|
||||
if (alignment)
|
||||
{
|
||||
_spiBufferSize += 4 - alignment;
|
||||
}
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_spiBufferSize));
|
||||
_dmadata = static_cast<uint8_t*>(heap_caps_malloc(_spiBufferSize, MALLOC_CAP_DMA));
|
||||
|
||||
// data cleared later in NeoPixelBus::Begin()
|
||||
}
|
||||
|
||||
// Support constructor specifying pins by ignoring pins
|
||||
DotStarEsp32DmaSpiMethodBase(uint8_t, uint8_t, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
DotStarEsp32DmaSpiMethodBase(pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
|
||||
~DotStarEsp32DmaSpiMethodBase()
|
||||
{
|
||||
if (_spiHandle)
|
||||
{
|
||||
deinitSpiDevice();
|
||||
esp_err_t ret = spi_bus_free(T_SPIBUS::SpiHostDevice);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
free(_data);
|
||||
heap_caps_free(_dmadata);
|
||||
_spiHandle = NULL;
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
spi_transaction_t t;
|
||||
spi_transaction_t* tptr = &t;
|
||||
|
||||
esp_err_t ret = spi_device_get_trans_result(_spiHandle, &tptr, 0);
|
||||
|
||||
// we are ready if prev result is back (ESP_OK) or if we got a timeout and
|
||||
// transaction length of 0 (we didn't start a transaction)
|
||||
return (ret == ESP_OK || (ret == ESP_ERR_TIMEOUT && 0 == _spiTransaction.length));
|
||||
}
|
||||
|
||||
void Initialize(int8_t sck, int8_t dat0, int8_t dat1, int8_t dat2, int8_t dat3, int8_t dat4, int8_t dat5, int8_t dat6, int8_t dat7, int8_t ss)
|
||||
{
|
||||
memset(_data, 0x00, _sizeStartFrame);
|
||||
memset(_data + _sizeStartFrame + _sizePixelData, 0x00, _spiBufferSize - (_sizeStartFrame + _sizePixelData));
|
||||
|
||||
_ssPin = ss;
|
||||
|
||||
esp_err_t ret;
|
||||
spi_bus_config_t buscfg = { 0 };
|
||||
|
||||
buscfg.sclk_io_num = sck;
|
||||
buscfg.data0_io_num = dat0;
|
||||
buscfg.data1_io_num = dat1;
|
||||
buscfg.data2_io_num = dat2;
|
||||
buscfg.data3_io_num = dat3;
|
||||
buscfg.data4_io_num = dat4;
|
||||
buscfg.data5_io_num = dat5;
|
||||
buscfg.data6_io_num = dat6;
|
||||
buscfg.data7_io_num = dat7;
|
||||
buscfg.max_transfer_sz = _spiBufferSize;
|
||||
if (T_SPIBUS::ParallelBits == 8)
|
||||
{
|
||||
buscfg.flags = SPICOMMON_BUSFLAG_OCTAL;
|
||||
}
|
||||
|
||||
//Initialize the SPI bus
|
||||
ret = spi_bus_initialize(T_SPIBUS::SpiHostDevice, &buscfg, SPI_DMA_CH_AUTO);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
_spiTransaction = { 0 };
|
||||
initSpiDevice();
|
||||
}
|
||||
|
||||
void Initialize(int8_t sck, int8_t dat0, int8_t dat1, int8_t dat2, int8_t dat3, int8_t ss)
|
||||
{
|
||||
Initialize(sck, dat0, dat1, dat2, dat3, -1, -1, -1, -1, ss);
|
||||
}
|
||||
|
||||
void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
Initialize(sck, mosi, miso, -1, -1, ss);
|
||||
}
|
||||
|
||||
// If pins aren't specified, initialize bus with just the default SCK and MOSI pins for the SPI peripheral (no SS, no >1-bit pins)
|
||||
void Initialize()
|
||||
{
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
if (T_SPIBUS::SpiHostDevice == VSPI_HOST)
|
||||
{
|
||||
Initialize(SCK, -1, MOSI, -1, -1, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Initialize(14, -1, 13, -1, -1, -1);
|
||||
}
|
||||
#else
|
||||
Initialize(SCK, -1, MOSI, -1, -1, -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
while(!IsReadyToUpdate())
|
||||
{
|
||||
portYIELD();
|
||||
}
|
||||
|
||||
memcpy(_dmadata, _data, _spiBufferSize);
|
||||
|
||||
_spiTransaction = { 0 };
|
||||
_spiTransaction.length = (_spiBufferSize) * 8; // in bits not bytes!
|
||||
|
||||
if (T_SPIBUS::ParallelBits == 1)
|
||||
{
|
||||
_spiTransaction.flags = 0;
|
||||
}
|
||||
if (T_SPIBUS::ParallelBits == 2)
|
||||
{
|
||||
_spiTransaction.flags = SPI_TRANS_MODE_DIO;
|
||||
}
|
||||
if (T_SPIBUS::ParallelBits == 4)
|
||||
{
|
||||
_spiTransaction.flags = SPI_TRANS_MODE_QIO;
|
||||
}
|
||||
if (T_SPIBUS::ParallelBits == 8)
|
||||
{
|
||||
_spiTransaction.flags = SPI_TRANS_MODE_OCT;
|
||||
}
|
||||
_spiTransaction.tx_buffer = _dmadata;
|
||||
|
||||
esp_err_t ret = spi_device_queue_trans(_spiHandle, &_spiTransaction, 0); //Transmit!
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data + _sizeStartFrame;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizePixelData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_speed.applySettings(settings);
|
||||
if (_spiHandle)
|
||||
{
|
||||
deinitSpiDevice();
|
||||
initSpiDevice();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void initSpiDevice()
|
||||
{
|
||||
spi_device_interface_config_t devcfg = {0};
|
||||
|
||||
devcfg.clock_speed_hz = _speed.Clock;
|
||||
devcfg.mode = 0; //SPI mode 0
|
||||
devcfg.spics_io_num = _ssPin; //CS pin
|
||||
devcfg.queue_size = 1;
|
||||
if (T_SPIBUS::ParallelBits == 1)
|
||||
{
|
||||
devcfg.flags = 0;
|
||||
}
|
||||
if (T_SPIBUS::ParallelBits >= 2)
|
||||
{
|
||||
devcfg.flags = SPI_DEVICE_HALFDUPLEX;
|
||||
}
|
||||
|
||||
//Allocate the LEDs on the SPI bus
|
||||
esp_err_t ret = spi_bus_add_device(T_SPIBUS::SpiHostDevice, &devcfg, &_spiHandle);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
|
||||
void deinitSpiDevice()
|
||||
{
|
||||
while(!IsReadyToUpdate());
|
||||
esp_err_t ret = spi_bus_remove_device(_spiHandle);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
|
||||
const size_t _sizeStartFrame;
|
||||
const size_t _sizePixelData; // Size of '_data' buffer below, minus (_sizeStartFrame + _sizeEndFrame)
|
||||
const size_t _sizeEndFrame;
|
||||
|
||||
size_t _spiBufferSize;
|
||||
uint8_t* _data; // Holds start/end frames and LED color values
|
||||
uint8_t* _dmadata; // Holds start/end frames and LED color values
|
||||
spi_device_handle_t _spiHandle = NULL;
|
||||
spi_transaction_t _spiTransaction;
|
||||
T_SPISPEED _speed;
|
||||
int8_t _ssPin;
|
||||
};
|
||||
|
||||
|
||||
// Unfortunately we have a bit of a mess with SPI bus names across different version of the ESP32
|
||||
// e.g ESP32 has SPI, HSPI, and VSPI (1, 2, and 3), ESP32-S2 has SPI, FSPI, and HSPI (1, 2, and 3)
|
||||
// and the S3 and C3 dropped the silly names entirely and just uses SPI1, SPI2, and SPI3.
|
||||
//
|
||||
// SPI1 can be only be used by ESP32 and supports up to 4 bit
|
||||
// SPI2 supports up to 4 bit output across all of those devices (!) and supports 8 bit on S2 and S3
|
||||
// SPI3 supports up to 4 bit output on ESP32 and S3, and 1 bit only on the S2
|
||||
|
||||
enum spi_bus_width_t {
|
||||
WIDTH1 = 1,
|
||||
WIDTH2 = 2,
|
||||
WIDTH4 = 4,
|
||||
WIDTH8 = 8,
|
||||
};
|
||||
|
||||
template <spi_host_device_t bus, spi_bus_width_t bits = WIDTH1>
|
||||
struct Esp32SpiBus
|
||||
{
|
||||
const static spi_host_device_t SpiHostDevice = bus;
|
||||
const static int ParallelBits = bits;
|
||||
};
|
||||
|
||||
// Define all valid ESP32 SPI Buses with a default speed
|
||||
|
||||
// SPI1 -- ESP32 Only
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
typedef Esp32SpiBus<SPI1_HOST, WIDTH1> Esp32Spi1Bus;
|
||||
typedef Esp32SpiBus<SPI1_HOST, WIDTH2> Esp32Spi12BitBus;
|
||||
typedef Esp32SpiBus<SPI1_HOST, WIDTH4> Esp32Spi14BitBus;
|
||||
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi1Bus> DotStarEsp32DmaSpi1Method;
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi12BitBus> DotStarEsp32DmaSpi12BitMethod;
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi14BitBus> DotStarEsp32DmaSpi14BitMethod;
|
||||
#endif
|
||||
|
||||
// SPI2
|
||||
typedef Esp32SpiBus<SPI2_HOST, WIDTH1> Esp32Spi2Bus;
|
||||
typedef Esp32SpiBus<SPI2_HOST, WIDTH2> Esp32Spi22BitBus;
|
||||
typedef Esp32SpiBus<SPI2_HOST, WIDTH4> Esp32Spi24BitBus;
|
||||
#if SOC_SPI_SUPPORT_OCT
|
||||
typedef Esp32SpiBus<SPI2_HOST, WIDTH8> Esp32Spi28BitBus;
|
||||
#endif
|
||||
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi2Bus> DotStarEsp32DmaSpi2Method;
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi22BitBus> DotStarEsp32DmaSpi22BitMethod;
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi24BitBus> DotStarEsp32DmaSpi24BitMethod;
|
||||
#if SOC_SPI_SUPPORT_OCT
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi28BitBus> DotStarEsp32DmaSpi28BitMethod;
|
||||
#endif
|
||||
|
||||
|
||||
// SPI3
|
||||
#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3))
|
||||
typedef Esp32SpiBus<SPI3_HOST, WIDTH1> Esp32Spi3Bus;
|
||||
typedef Esp32SpiBus<SPI3_HOST, WIDTH2> Esp32Spi32BitBus;
|
||||
typedef Esp32SpiBus<SPI3_HOST, WIDTH4> Esp32Spi34BitBus;
|
||||
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi3Bus> DotStarEsp32DmaSpi3Method;
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi32BitBus> DotStarEsp32DmaSpi32BitMethod;
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi34BitBus> DotStarEsp32DmaSpi34BitMethod;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
typedef Esp32SpiBus<SPI3_HOST, WIDTH1> Esp32Spi3Bus;
|
||||
typedef DotStarEsp32DmaSpiMethodBase<SpiSpeed10Mhz, Esp32Spi3Bus> DotStarEsp32DmaSpi3Method;
|
||||
#endif
|
||||
|
||||
// Default SpiDma methods if we don't care about bus. It's nice that every single ESP32 out there
|
||||
// supports up to 4 bits on SPI2
|
||||
|
||||
typedef DotStarEsp32DmaSpi2Method DotStarEsp32DmaSpiMethod;
|
||||
typedef DotStarEsp32DmaSpi22BitMethod DotStarEsp32DmaSpi2BitMethod;
|
||||
typedef DotStarEsp32DmaSpi24BitMethod DotStarEsp32DmaSpi4BitMethod;
|
||||
#if SOC_SPI_SUPPORT_OCT
|
||||
typedef DotStarEsp32DmaSpi28BitMethod DotStarEsp32DmaSpi8BitMethod;
|
||||
#endif
|
||||
|
||||
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 1)
|
||||
@@ -0,0 +1,176 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for DotStars using general Pins (APA102).
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
|
||||
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
|
||||
#include "TwoWireBitBangImpleAvr.h"
|
||||
#else
|
||||
#include "TwoWireBitBangImple.h"
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T_TWOWIRE> class DotStarMethodBase
|
||||
{
|
||||
public:
|
||||
typedef typename T_TWOWIRE::SettingsObject SettingsObject;
|
||||
|
||||
DotStarMethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_sizeEndFrame((pixelCount + 15) / 16), // 16 = div 2 (bit for every two pixels) div 8 (bits to bytes)
|
||||
_wire(pinClock, pinData)
|
||||
{
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
DotStarMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
DotStarMethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
~DotStarMethodBase()
|
||||
{
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return true; // dot stars don't have a required delay
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
_wire.begin(sck, miso, mosi, ss);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
_wire.begin();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
const uint8_t startFrame[4] = { 0x00 };
|
||||
const uint8_t resetFrame[4] = { 0x00 };
|
||||
|
||||
_wire.beginTransaction();
|
||||
|
||||
// start frame
|
||||
_wire.transmitBytes(startFrame, sizeof(startFrame));
|
||||
|
||||
// data
|
||||
_wire.transmitBytes(_data, _sizeData);
|
||||
|
||||
// reset frame
|
||||
_wire.transmitBytes(resetFrame, sizeof(resetFrame));
|
||||
|
||||
// end frame
|
||||
|
||||
// one bit for every two pixels with no less than 1 byte
|
||||
for (size_t endFrameByte = 0; endFrameByte < _sizeEndFrame; endFrameByte++)
|
||||
{
|
||||
_wire.transmitByte(0x00);
|
||||
}
|
||||
|
||||
_wire.endTransaction();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_wire.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
const size_t _sizeEndFrame;
|
||||
|
||||
T_TWOWIRE _wire;
|
||||
uint8_t* _data; // Holds LED color values
|
||||
};
|
||||
|
||||
typedef DotStarMethodBase<TwoWireBitBangImple> DotStarMethod;
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
#include "TwoWireSpiImple.h"
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed40Mhz>> DotStarSpi40MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed20Mhz>> DotStarSpi20MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed10Mhz>> DotStarSpi10MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed5Mhz>> DotStarSpi5MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed2Mhz>> DotStarSpi2MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed1Mhz>> DotStarSpi1MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed500Khz>> DotStarSpi500KhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeedHz>> DotStarSpiHzMethod;
|
||||
|
||||
typedef DotStarSpi10MhzMethod DotStarSpiMethod;
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// Give option to use Vspi alias of Spi class if wanting to specify which SPI peripheral is used on the ESP32
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed40Mhz>> DotStarEsp32Vspi40MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed20Mhz>> DotStarEsp32Vspi20MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed10Mhz>> DotStarEsp32Vspi10MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed5Mhz>> DotStarEsp32Vspi5MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed2Mhz>> DotStarEsp32Vspi2MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed1Mhz>> DotStarEsp32Vspi1MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeed500Khz>> DotStarEsp32Vspi500KhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireSpiImple<SpiSpeedHz>> DotStarEsp32VspiHzMethod;
|
||||
|
||||
typedef DotStarSpi10MhzMethod DotStarEsp32VspiMethod;
|
||||
|
||||
#include "TwoWireHspiImple.h"
|
||||
typedef DotStarMethodBase<TwoWireHspiImple<SpiSpeed40Mhz>> DotStarEsp32Hspi40MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireHspiImple<SpiSpeed20Mhz>> DotStarEsp32Hspi20MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireHspiImple<SpiSpeed10Mhz>> DotStarEsp32Hspi10MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireHspiImple<SpiSpeed5Mhz>> DotStarEsp32Hspi5MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireHspiImple<SpiSpeed2Mhz>> DotStarEsp32Hspi2MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireHspiImple<SpiSpeed1Mhz>> DotStarEsp32Hspi1MhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireHspiImple<SpiSpeed500Khz>> DotStarEsp32Hspi500KhzMethod;
|
||||
typedef DotStarMethodBase<TwoWireHspiImple<SpiSpeedHz>> DotStarEsp32HspiHzMethod;
|
||||
|
||||
typedef DotStarEsp32Hspi10MhzMethod DotStarEsp32HspiMethod;
|
||||
#endif
|
||||
@@ -0,0 +1,671 @@
|
||||
// WARNING: This file contains code that is more than likely already
|
||||
// exposed from the Esp32 Arduino API. It will be removed once integration is complete.
|
||||
//
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
#include "sdkconfig.h" // this sets useful config symbols, like CONFIG_IDF_TARGET_ESP32C3
|
||||
|
||||
// ESP32 C3 & S3 I2S is not supported yet due to significant changes to interface
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "stdlib.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR>=4
|
||||
#include "esp_intr_alloc.h"
|
||||
#else
|
||||
#include "esp_intr.h"
|
||||
#endif
|
||||
|
||||
#include "rom/lldesc.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
/* included here for ESP-IDF v4.x compatibility */
|
||||
#include "soc/dport_reg.h"
|
||||
#endif
|
||||
#include "soc/sens_reg.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s.h"
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#include "driver/dac.h"
|
||||
#endif
|
||||
|
||||
#include "Esp32_i2s.h"
|
||||
#include "esp32-hal.h"
|
||||
|
||||
esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits_per_sample);
|
||||
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, bool parallel_mode, size_t bytes_per_sample);
|
||||
|
||||
#define MATRIX_DETACH_OUT_SIG 0x100
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR<=4
|
||||
#define I2S_BASE_CLK (160000000L)
|
||||
#endif
|
||||
|
||||
#define I2S_DMA_BLOCK_COUNT_DEFAULT 0
|
||||
// 20 bytes gives us enough time if we use single stage idle
|
||||
// But it can't be longer due to non-parrallel mode and 50us reset time
|
||||
// there just isn't enough silence at the end to fill more than 20 bytes
|
||||
#define I2S_DMA_SILENCE_SIZE 20 // 4 byte increments
|
||||
#define I2S_DMA_SILENCE_BLOCK_COUNT_FRONT 2 // two front
|
||||
#define I2S_DMA_SILENCE_BLOCK_COUNT_BACK 1 // one back, required for non parallel
|
||||
|
||||
typedef struct
|
||||
{
|
||||
i2s_dev_t* bus;
|
||||
int8_t ws;
|
||||
int8_t bck;
|
||||
int8_t out;
|
||||
int8_t in;
|
||||
|
||||
intr_handle_t isr_handle;
|
||||
lldesc_t* dma_items;
|
||||
size_t dma_count;
|
||||
|
||||
volatile uint32_t is_sending_data;
|
||||
} i2s_bus_t;
|
||||
|
||||
// is_sending_data values
|
||||
#define I2s_Is_Idle 0
|
||||
#define I2s_Is_Pending 1
|
||||
#define I2s_Is_Sending 2
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// (I2S_NUM_MAX == 2)
|
||||
static i2s_bus_t I2S[I2S_NUM_MAX] =
|
||||
{
|
||||
{&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle},
|
||||
{&I2S1, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
|
||||
};
|
||||
#else
|
||||
static i2s_bus_t I2S[I2S_NUM_MAX] =
|
||||
{
|
||||
{&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}
|
||||
};
|
||||
#endif
|
||||
|
||||
void IRAM_ATTR i2sDmaISR(void* arg);
|
||||
|
||||
inline void dmaItemInit(lldesc_t* item, uint8_t* posData, size_t sizeData, lldesc_t* itemNext)
|
||||
{
|
||||
item->eof = 0;
|
||||
item->owner = 1;
|
||||
item->sosf = 0;
|
||||
item->offset = 0;
|
||||
item->buf = posData;
|
||||
item->size = sizeData;
|
||||
item->length = sizeData;
|
||||
item->qe.stqe_next = itemNext;
|
||||
}
|
||||
|
||||
bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize)
|
||||
{
|
||||
if (bus_num >= I2S_NUM_MAX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t dmaCount = I2S[bus_num].dma_count;
|
||||
|
||||
if (I2S[bus_num].dma_items == NULL)
|
||||
{
|
||||
I2S[bus_num].dma_items = (lldesc_t*)heap_caps_malloc(dmaCount * sizeof(lldesc_t), MALLOC_CAP_DMA);
|
||||
if (I2S[bus_num].dma_items == NULL)
|
||||
{
|
||||
log_e("MEM ERROR!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
lldesc_t* itemFirst = &I2S[bus_num].dma_items[0];
|
||||
lldesc_t* item = itemFirst;
|
||||
// lldesc_t* itemsEnd = itemFirst + I2S[bus_num].dma_count;
|
||||
lldesc_t* itemNext = item + 1;
|
||||
size_t dataLeft = dataSize;
|
||||
uint8_t* pos = data;
|
||||
// at the end of the data is the encoded silence reset
|
||||
uint8_t* posSilence = data + dataSize - I2S_DMA_SILENCE_SIZE;
|
||||
|
||||
// front two are silent items used for looping to micmic single fire
|
||||
// default to looping
|
||||
dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemNext);
|
||||
dmaItemInit(itemNext, posSilence, I2S_DMA_SILENCE_SIZE, item);
|
||||
item = itemNext;
|
||||
itemNext++;
|
||||
|
||||
// init blocks with avialable data
|
||||
//
|
||||
while (dataLeft)
|
||||
{
|
||||
item = itemNext;
|
||||
itemNext++;
|
||||
|
||||
size_t blockSize = dataLeft;
|
||||
if (blockSize > I2S_DMA_MAX_DATA_LEN)
|
||||
{
|
||||
blockSize = I2S_DMA_MAX_DATA_LEN;
|
||||
}
|
||||
dataLeft -= blockSize;
|
||||
|
||||
dmaItemInit(item, pos, blockSize, itemNext);
|
||||
|
||||
pos += blockSize;
|
||||
}
|
||||
|
||||
// last data item is EOF to manage send state using EOF ISR
|
||||
item->eof = 1;
|
||||
|
||||
// last block, the back silent item, loops to front
|
||||
item = itemNext;
|
||||
dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemFirst);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool i2sDeinitDmaItems(uint8_t bus_num)
|
||||
{
|
||||
if (bus_num >= I2S_NUM_MAX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
heap_caps_free(I2S[bus_num].dma_items);
|
||||
I2S[bus_num].dma_items = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// normal 4, 10, 63, 12, 16
|
||||
|
||||
esp_err_t i2sSetClock(uint8_t bus_num,
|
||||
uint8_t div_num, // 4 13
|
||||
uint8_t div_b, // 10 20
|
||||
uint8_t div_a, // 63 63
|
||||
uint8_t bck, // 12 60 or 7
|
||||
uint8_t bits) // 16 8
|
||||
{
|
||||
if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//log_i("i2sSetClock bus %u, clkm_div_num %u, clk_div_a %u, clk_div_b %u, bck_div_num %u, bits_mod %u",
|
||||
// bus_num,
|
||||
// div_num,
|
||||
// div_a,
|
||||
// div_b,
|
||||
// bck,
|
||||
// bits);
|
||||
|
||||
i2s_dev_t* i2s = I2S[bus_num].bus;
|
||||
|
||||
typeof(i2s->clkm_conf) clkm_conf;
|
||||
|
||||
clkm_conf.val = 0;
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
clkm_conf.clk_sel = 2; // APPL = 1 APB = 2
|
||||
clkm_conf.clk_en = 1; // examples of i2s show this being set if sel is set to 2
|
||||
#else
|
||||
clkm_conf.clka_en = 0;
|
||||
#endif
|
||||
|
||||
clkm_conf.clkm_div_a = div_a;
|
||||
clkm_conf.clkm_div_b = div_b;
|
||||
clkm_conf.clkm_div_num = div_num;
|
||||
i2s->clkm_conf.val = clkm_conf.val;
|
||||
|
||||
typeof(i2s->sample_rate_conf) sample_rate_conf;
|
||||
sample_rate_conf.val = 0;
|
||||
sample_rate_conf.tx_bck_div_num = bck;
|
||||
sample_rate_conf.rx_bck_div_num = bck;
|
||||
sample_rate_conf.tx_bits_mod = bits;
|
||||
sample_rate_conf.rx_bits_mod = bits;
|
||||
i2s->sample_rate_conf.val = sample_rate_conf.val;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void i2sSetPins(uint8_t bus_num,
|
||||
int8_t out,
|
||||
int8_t parallel,
|
||||
int8_t busSampleSize,
|
||||
bool invert)
|
||||
{
|
||||
if (bus_num >= I2S_NUM_MAX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (out >= 0)
|
||||
{
|
||||
uint32_t i2sSignal;
|
||||
|
||||
pinMode(out, OUTPUT);
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
// S2 only has one bus
|
||||
//
|
||||
// in parallel mode
|
||||
// 8bit mode : I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX
|
||||
// 16bit mode : I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX
|
||||
// 24bit mode : I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX
|
||||
if (parallel == -1)
|
||||
{
|
||||
i2sSignal = I2S0O_DATA_OUT23_IDX;
|
||||
}
|
||||
else if (busSampleSize == 1)
|
||||
{
|
||||
i2sSignal = I2S0O_DATA_OUT16_IDX + parallel;
|
||||
}
|
||||
else if (busSampleSize == 2)
|
||||
{
|
||||
i2sSignal = I2S0O_DATA_OUT8_IDX + parallel;
|
||||
}
|
||||
else
|
||||
{
|
||||
i2sSignal = I2S0O_DATA_OUT0_IDX + parallel;
|
||||
}
|
||||
|
||||
#else
|
||||
if (bus_num == 0)
|
||||
{
|
||||
// in parallel mode
|
||||
// 0-7 bits : I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX
|
||||
// 8-15 bits : I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX
|
||||
// 16-23 bits : I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX
|
||||
if (parallel == -1)
|
||||
{
|
||||
i2sSignal = I2S0O_DATA_OUT23_IDX;
|
||||
}
|
||||
else if (parallel < 8)
|
||||
{
|
||||
i2sSignal = I2S0O_DATA_OUT16_IDX + parallel;
|
||||
}
|
||||
else if (parallel < 16)
|
||||
{
|
||||
i2sSignal = I2S0O_DATA_OUT8_IDX + parallel - 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
i2sSignal = I2S0O_DATA_OUT0_IDX + parallel - 16;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parallel == -1)
|
||||
{
|
||||
i2sSignal = I2S1O_DATA_OUT23_IDX;
|
||||
}
|
||||
else
|
||||
{
|
||||
i2sSignal = I2S1O_DATA_OUT0_IDX + parallel;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//log_i("i2sSetPins bus %u, i2sSignal %u, pin %u, mux %u",
|
||||
// bus_num,
|
||||
// i2sSignal,
|
||||
// out,
|
||||
// parallel);
|
||||
gpio_matrix_out(out, i2sSignal, invert, false);
|
||||
}
|
||||
}
|
||||
|
||||
void i2sSetClkWsPins(uint8_t bus_num,
|
||||
int8_t outClk,
|
||||
bool invertClk,
|
||||
int8_t outWs,
|
||||
bool invertWs)
|
||||
{
|
||||
|
||||
if (bus_num >= I2S_NUM_MAX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t i2sSignalClk = I2S0O_BCK_OUT_IDX;
|
||||
uint32_t i2sSignalWs = I2S0O_WS_OUT_IDX;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
if (bus_num == 1)
|
||||
{
|
||||
i2sSignalClk = I2S1O_BCK_OUT_IDX;
|
||||
i2sSignalWs = I2S1O_WS_OUT_IDX;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (outClk >= 0)
|
||||
{
|
||||
pinMode(outClk, OUTPUT);
|
||||
gpio_matrix_out(outClk, i2sSignalClk, invertClk, false);
|
||||
}
|
||||
|
||||
if (outWs >= 0)
|
||||
{
|
||||
pinMode(outWs, OUTPUT);
|
||||
gpio_matrix_out(outWs, i2sSignalWs, invertWs, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool i2sWriteDone(uint8_t bus_num)
|
||||
{
|
||||
if (bus_num >= I2S_NUM_MAX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (I2S[bus_num].is_sending_data == I2s_Is_Idle);
|
||||
}
|
||||
|
||||
void i2sInit(uint8_t bus_num,
|
||||
bool parallel_mode,
|
||||
size_t bytes_per_sample,
|
||||
uint32_t sample_rate,
|
||||
i2s_tx_chan_mod_t chan_mod,
|
||||
i2s_tx_fifo_mod_t fifo_mod,
|
||||
size_t dma_count,
|
||||
uint8_t* data,
|
||||
size_t dataSize)
|
||||
{
|
||||
if (bus_num >= I2S_NUM_MAX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
I2S[bus_num].dma_count = dma_count +
|
||||
I2S_DMA_SILENCE_BLOCK_COUNT_FRONT +
|
||||
I2S_DMA_SILENCE_BLOCK_COUNT_BACK;
|
||||
|
||||
if (!i2sInitDmaItems(bus_num, data, dataSize))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// (I2S_NUM_MAX == 2)
|
||||
if (bus_num)
|
||||
{
|
||||
periph_module_enable(PERIPH_I2S1_MODULE);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||
}
|
||||
|
||||
esp_intr_disable(I2S[bus_num].isr_handle);
|
||||
i2s_dev_t* i2s = I2S[bus_num].bus;
|
||||
i2s->out_link.stop = 1;
|
||||
i2s->conf.tx_start = 0;
|
||||
i2s->int_ena.val = 0;
|
||||
i2s->int_clr.val = 0xFFFFFFFF;
|
||||
i2s->fifo_conf.dscr_en = 0;
|
||||
|
||||
// reset i2s
|
||||
i2s->conf.tx_reset = 1;
|
||||
i2s->conf.tx_reset = 0;
|
||||
i2s->conf.rx_reset = 1;
|
||||
i2s->conf.rx_reset = 0;
|
||||
|
||||
// reset dma
|
||||
i2s->lc_conf.in_rst = 1;
|
||||
i2s->lc_conf.in_rst = 0;
|
||||
i2s->lc_conf.out_rst = 1;
|
||||
i2s->lc_conf.out_rst = 0;
|
||||
|
||||
// reset fifo
|
||||
i2s->conf.rx_fifo_reset = 1;
|
||||
i2s->conf.rx_fifo_reset = 0;
|
||||
i2s->conf.tx_fifo_reset = 1;
|
||||
i2s->conf.tx_fifo_reset = 0;
|
||||
|
||||
|
||||
// set parallel (LCD) mode
|
||||
{
|
||||
typeof(i2s->conf2) conf2;
|
||||
conf2.val = 0;
|
||||
conf2.lcd_en = parallel_mode;
|
||||
conf2.lcd_tx_wrx2_en = 0; // parallel_mode; // ((parallel_mode) && (bytes_per_sample == 2));
|
||||
i2s->conf2.val = conf2.val;
|
||||
}
|
||||
|
||||
// Enable and configure DMA
|
||||
{
|
||||
typeof(i2s->lc_conf) lc_conf;
|
||||
lc_conf.val = 0;
|
||||
lc_conf.out_eof_mode = 1;
|
||||
i2s->lc_conf.val = lc_conf.val;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
i2s->pdm_conf.pcm2pdm_conv_en = 0;
|
||||
i2s->pdm_conf.pdm2pcm_conv_en = 0;
|
||||
#endif
|
||||
// SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S);
|
||||
|
||||
{
|
||||
typeof(i2s->fifo_conf) fifo_conf;
|
||||
|
||||
fifo_conf.val = 0;
|
||||
fifo_conf.rx_fifo_mod_force_en = 1;
|
||||
fifo_conf.tx_fifo_mod_force_en = 1;
|
||||
fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
|
||||
fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel
|
||||
fifo_conf.rx_data_num = 32; //Thresholds.
|
||||
fifo_conf.tx_data_num = 32;
|
||||
|
||||
i2s->fifo_conf.val = fifo_conf.val;
|
||||
}
|
||||
|
||||
// $REVIEW old code didn't set this
|
||||
{
|
||||
typeof(i2s->conf1) conf1;
|
||||
conf1.val = 0;
|
||||
conf1.tx_stop_en = 0;
|
||||
conf1.tx_pcm_bypass = 1;
|
||||
i2s->conf1.val = conf1.val;
|
||||
}
|
||||
|
||||
{
|
||||
typeof(i2s->conf_chan) conf_chan;
|
||||
conf_chan.val = 0;
|
||||
conf_chan.tx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
|
||||
conf_chan.rx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left
|
||||
i2s->conf_chan.val = conf_chan.val;
|
||||
}
|
||||
|
||||
{
|
||||
typeof(i2s->conf) conf;
|
||||
conf.val = 0;
|
||||
conf.tx_msb_shift = !parallel_mode; // 0:DAC/PCM, 1:I2S
|
||||
conf.tx_right_first = 0; // parallel_mode?;
|
||||
i2s->conf.val = conf.val;
|
||||
}
|
||||
|
||||
i2s->timing.val = 0;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
i2s->pdm_conf.rx_pdm_en = 0;
|
||||
i2s->pdm_conf.tx_pdm_en = 0;
|
||||
#endif
|
||||
|
||||
|
||||
i2sSetSampleRate(bus_num, sample_rate, parallel_mode, bytes_per_sample);
|
||||
|
||||
/* */
|
||||
//Reset FIFO/DMA -> needed? Doesn't dma_reset/fifo_reset do this?
|
||||
i2s->lc_conf.in_rst=1; i2s->lc_conf.out_rst=1; i2s->lc_conf.ahbm_rst=1; i2s->lc_conf.ahbm_fifo_rst=1;
|
||||
i2s->lc_conf.in_rst=0; i2s->lc_conf.out_rst=0; i2s->lc_conf.ahbm_rst=0; i2s->lc_conf.ahbm_fifo_rst=0;
|
||||
i2s->conf.tx_reset=1; i2s->conf.tx_fifo_reset=1; i2s->conf.rx_fifo_reset=1;
|
||||
i2s->conf.tx_reset=0; i2s->conf.tx_fifo_reset=0; i2s->conf.rx_fifo_reset=0;
|
||||
/* */
|
||||
|
||||
// enable intr in cpu //
|
||||
int i2sIntSource;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// (I2S_NUM_MAX == 2)
|
||||
if (bus_num == 1) {
|
||||
i2sIntSource = ETS_I2S1_INTR_SOURCE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
i2sIntSource = ETS_I2S0_INTR_SOURCE;
|
||||
}
|
||||
|
||||
esp_intr_alloc(i2sIntSource, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle);
|
||||
// enable send intr
|
||||
i2s->int_ena.out_eof = 1;
|
||||
i2s->int_ena.out_dscr_err = 1;
|
||||
|
||||
/* ??? */
|
||||
// Enable and configure DMA
|
||||
{
|
||||
typeof(i2s->lc_conf) lc_conf;
|
||||
lc_conf.val = 0;
|
||||
lc_conf.out_data_burst_en = 1;
|
||||
lc_conf.indscr_burst_en = 1;
|
||||
i2s->lc_conf.val = lc_conf.val;
|
||||
}
|
||||
/* */
|
||||
i2s->fifo_conf.dscr_en = 1;// enable dma
|
||||
i2s->out_link.start = 0;
|
||||
i2s->out_link.addr = (uint32_t)(&I2S[bus_num].dma_items[0]); // loads dma_struct to dma
|
||||
i2s->out_link.start = 1; // starts dma
|
||||
i2s->conf.tx_start = 1;// Start I2s module
|
||||
|
||||
esp_intr_enable(I2S[bus_num].isr_handle);
|
||||
}
|
||||
|
||||
void i2sDeinit(uint8_t bus_num)
|
||||
{
|
||||
i2sDeinitDmaItems(bus_num);
|
||||
}
|
||||
|
||||
esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, bool parallel_mode, size_t bytes_per_sample)
|
||||
{
|
||||
if (bus_num >= I2S_NUM_MAX)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t bck = 12;
|
||||
|
||||
// parallel mode needs a higher sample rate
|
||||
//
|
||||
if (parallel_mode)
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
rate *= bytes_per_sample;
|
||||
bck *= bytes_per_sample;
|
||||
|
||||
//rate /= bytes_per_sample;
|
||||
//bck /= bytes_per_sample;
|
||||
#else
|
||||
rate *= bytes_per_sample;
|
||||
#endif
|
||||
}
|
||||
|
||||
// 160,000,000L / (100,000 * 384)
|
||||
double clkmdiv = (double)I2S_BASE_CLK / ((rate * 384) + 1);
|
||||
if (clkmdiv > 256.0)
|
||||
{
|
||||
log_e("rate is too low");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
else if (clkmdiv < 2.0)
|
||||
{
|
||||
log_e("rate is too fast, clkmdiv = %f (%u, %u, %u)",
|
||||
clkmdiv,
|
||||
rate,
|
||||
parallel_mode,
|
||||
bytes_per_sample);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// calc integer and franctional for more precise timing
|
||||
//
|
||||
uint8_t clkmInteger = clkmdiv;
|
||||
uint8_t clkmFraction = (clkmdiv - clkmInteger) * 63.0;
|
||||
|
||||
i2sSetClock(bus_num, clkmInteger, clkmFraction, 63, bck, bytes_per_sample * 8);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void IRAM_ATTR i2sDmaISR(void* arg)
|
||||
{
|
||||
i2s_bus_t* i2s = (i2s_bus_t*)(arg);
|
||||
|
||||
if (i2s->bus->int_st.out_eof)
|
||||
{
|
||||
// lldesc_t* item = (lldesc_t*)(i2s->bus->out_eof_des_addr);
|
||||
if (i2s->is_sending_data != I2s_Is_Idle)
|
||||
{
|
||||
// the second item (last of the two front silent items) is
|
||||
// silent looping item
|
||||
lldesc_t* itemLoop = &i2s->dma_items[0];
|
||||
lldesc_t* itemLoopBreaker = itemLoop + 1;
|
||||
// set to loop on silent items
|
||||
itemLoopBreaker->qe.stqe_next = itemLoop;
|
||||
|
||||
i2s->is_sending_data = I2s_Is_Idle;
|
||||
}
|
||||
}
|
||||
|
||||
i2s->bus->int_clr.val = i2s->bus->int_st.val;
|
||||
}
|
||||
|
||||
bool i2sWrite(uint8_t bus_num)
|
||||
{
|
||||
if (bus_num >= I2S_NUM_MAX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// the second item (last of the two front silent items) is
|
||||
// silent looping item
|
||||
lldesc_t* itemLoopBreaker = &I2S[bus_num].dma_items[1];
|
||||
lldesc_t* itemLoopNext = itemLoopBreaker + 1;
|
||||
|
||||
// set to NOT loop on silent items
|
||||
itemLoopBreaker->qe.stqe_next = itemLoopNext;
|
||||
|
||||
I2S[bus_num].is_sending_data = I2s_Is_Sending;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#endif // defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
// ESP32C3/S3 I2S is not supported yet due to significant changes to interface
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#define I2S_DMA_MAX_DATA_LEN 4092// maximum bytes in one dma item
|
||||
|
||||
typedef enum {
|
||||
I2S_CHAN_STEREO, I2S_CHAN_RIGHT_TO_LEFT, I2S_CHAN_LEFT_TO_RIGHT, I2S_CHAN_RIGHT_ONLY, I2S_CHAN_LEFT_ONLY
|
||||
} i2s_tx_chan_mod_t;
|
||||
|
||||
typedef enum {
|
||||
I2S_FIFO_16BIT_DUAL, I2S_FIFO_16BIT_SINGLE, I2S_FIFO_32BIT_DUAL, I2S_FIFO_32BIT_SINGLE
|
||||
} i2s_tx_fifo_mod_t;
|
||||
|
||||
void i2sInit(uint8_t bus_num,
|
||||
bool parallel_mode,
|
||||
size_t bytes_per_sample,
|
||||
uint32_t sample_rate,
|
||||
i2s_tx_chan_mod_t chan_mod,
|
||||
i2s_tx_fifo_mod_t fifo_mod,
|
||||
size_t dma_count,
|
||||
uint8_t* data,
|
||||
size_t dataSize);
|
||||
void i2sDeinit(uint8_t bus_num);
|
||||
void i2sSetPins(uint8_t bus_num,
|
||||
int8_t out,
|
||||
int8_t parallel,
|
||||
int8_t busSampleSize,
|
||||
bool invert);
|
||||
void i2sSetClkWsPins(uint8_t bus_num,
|
||||
int8_t outClk,
|
||||
bool invertClk,
|
||||
int8_t outWs,
|
||||
bool invertWs);
|
||||
bool i2sWrite(uint8_t bus_num);
|
||||
bool i2sWriteDone(uint8_t bus_num);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for LPD6803 using general Pins
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
|
||||
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
|
||||
#include "TwoWireBitBangImpleAvr.h"
|
||||
#else
|
||||
#include "TwoWireBitBangImple.h"
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T_TWOWIRE> class Lpd6803MethodBase
|
||||
{
|
||||
public:
|
||||
typedef typename T_TWOWIRE::SettingsObject SettingsObject;
|
||||
|
||||
Lpd6803MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_sizeFrame((pixelCount + 7) / 8), // bit for every pixel at least
|
||||
_wire(pinClock, pinData)
|
||||
{
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
Lpd6803MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
Lpd6803MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
~Lpd6803MethodBase()
|
||||
{
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return true; // dot stars don't have a required delay
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
_wire.begin(sck, miso, mosi, ss);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
_wire.begin();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
const uint8_t startFrame[4] = { 0x00 };
|
||||
|
||||
_wire.beginTransaction();
|
||||
|
||||
// start frame
|
||||
_wire.transmitBytes(startFrame, sizeof(startFrame));
|
||||
|
||||
// data
|
||||
_wire.transmitBytes(_data, _sizeData);
|
||||
|
||||
// end frame
|
||||
// one bit for every pixel with no less than 1 byte
|
||||
for (size_t frameByte = 0; frameByte < _sizeFrame; frameByte++)
|
||||
{
|
||||
_wire.transmitByte(0x00);
|
||||
}
|
||||
|
||||
_wire.endTransaction();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_wire.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
const size_t _sizeFrame;
|
||||
|
||||
T_TWOWIRE _wire;
|
||||
uint8_t* _data; // Holds LED color values
|
||||
};
|
||||
|
||||
typedef Lpd6803MethodBase<TwoWireBitBangImple> Lpd6803Method;
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
#include "TwoWireSpiImple.h"
|
||||
typedef Lpd6803MethodBase<TwoWireSpiImple<SpiSpeed20Mhz>> Lpd6803Spi20MhzMethod;
|
||||
typedef Lpd6803MethodBase<TwoWireSpiImple<SpiSpeed10Mhz>> Lpd6803Spi10MhzMethod;
|
||||
typedef Lpd6803MethodBase<TwoWireSpiImple<SpiSpeed5Mhz>> Lpd6803Spi5MhzMethod;
|
||||
typedef Lpd6803MethodBase<TwoWireSpiImple<SpiSpeed2Mhz>> Lpd6803Spi2MhzMethod;
|
||||
typedef Lpd6803MethodBase<TwoWireSpiImple<SpiSpeed1Mhz>> Lpd6803Spi1MhzMethod;
|
||||
typedef Lpd6803MethodBase<TwoWireSpiImple<SpiSpeed500Khz>> Lpd6803Spi500KhzMethod;
|
||||
|
||||
typedef Lpd6803MethodBase<TwoWireSpiImple<SpiSpeedHz>> Lpd6803SpiHzMethod;
|
||||
|
||||
typedef Lpd6803Spi10MhzMethod Lpd6803SpiMethod;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for LPD8806 using general Pins
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
|
||||
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
|
||||
#include "TwoWireBitBangImpleAvr.h"
|
||||
#else
|
||||
#include "TwoWireBitBangImple.h"
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T_TWOWIRE> class Lpd8806MethodBase
|
||||
{
|
||||
public:
|
||||
typedef typename T_TWOWIRE::SettingsObject SettingsObject;
|
||||
|
||||
Lpd8806MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_sizeFrame((pixelCount + 31) / 32),
|
||||
_wire(pinClock, pinData)
|
||||
{
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
Lpd8806MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
Lpd8806MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
~Lpd8806MethodBase()
|
||||
{
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return true; // dot stars don't have a required delay
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
_wire.begin(sck, miso, mosi, ss);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
_wire.begin();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
_wire.beginTransaction();
|
||||
|
||||
// start frame
|
||||
for (size_t frameByte = 0; frameByte < _sizeFrame; frameByte++)
|
||||
{
|
||||
_wire.transmitByte(0x00);
|
||||
}
|
||||
|
||||
// data
|
||||
_wire.transmitBytes(_data, _sizeData);
|
||||
|
||||
// end frame
|
||||
for (size_t frameByte = 0; frameByte < _sizeFrame; frameByte++)
|
||||
{
|
||||
_wire.transmitByte(0xff);
|
||||
}
|
||||
|
||||
_wire.endTransaction();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_wire.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
const size_t _sizeFrame;
|
||||
|
||||
T_TWOWIRE _wire;
|
||||
uint8_t* _data; // Holds LED color values
|
||||
};
|
||||
|
||||
typedef Lpd8806MethodBase<TwoWireBitBangImple> Lpd8806Method;
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
#include "TwoWireSpiImple.h"
|
||||
typedef Lpd8806MethodBase<TwoWireSpiImple<SpiSpeed20Mhz>> Lpd8806Spi20MhzMethod;
|
||||
typedef Lpd8806MethodBase<TwoWireSpiImple<SpiSpeed10Mhz>> Lpd8806Spi10MhzMethod;
|
||||
typedef Lpd8806MethodBase<TwoWireSpiImple<SpiSpeed5Mhz>> Lpd8806Spi5MhzMethod;
|
||||
typedef Lpd8806MethodBase<TwoWireSpiImple<SpiSpeed2Mhz>> Lpd8806Spi2MhzMethod;
|
||||
typedef Lpd8806MethodBase<TwoWireSpiImple<SpiSpeed1Mhz>> Lpd8806Spi1MhzMethod;
|
||||
typedef Lpd8806MethodBase<TwoWireSpiImple<SpiSpeed500Khz>> Lpd8806Spi500KhzMethod;
|
||||
|
||||
typedef Lpd8806MethodBase<TwoWireSpiImple<SpiSpeedHz>> Lpd8806SpiHzMethod;
|
||||
|
||||
typedef Lpd8806Spi10MhzMethod Lpd8806SpiMethod;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,873 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for ARM MCUs.
|
||||
Teensy 3.0, 3.1, LC, Arduino Due
|
||||
|
||||
Written by Michael C. Miller.
|
||||
Some work taken from the Adafruit NeoPixel library.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
The contents of this file were taken from the Adafruit NeoPixel library
|
||||
and modified only to fit within individual calling functions.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__arm__) && !defined(ARDUINO_ARCH_NRF52840)
|
||||
|
||||
template<typename T_SPEED> class NeoArmMethodBase
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoArmMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin)
|
||||
{
|
||||
pinMode(pin, OUTPUT);
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
~NeoArmMethodBase()
|
||||
{
|
||||
pinMode(_pin, INPUT);
|
||||
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
uint32_t delta = micros() - _endTime;
|
||||
|
||||
return (delta >= T_SPEED::ResetTimeUs);
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
digitalWrite(_pin, LOW);
|
||||
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
// the function will simply hold off (if needed) on issuing the
|
||||
// subsequent round of data until the latch time has elapsed. This
|
||||
// allows the mainline code to start generating the next frame of data
|
||||
// rather than stalling for the latch.
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield(); // allows for system yield if needed
|
||||
}
|
||||
|
||||
noInterrupts(); // Need 100% focus on instruction timing
|
||||
|
||||
T_SPEED::send_pixels(_data, _sizeData, _pin);
|
||||
|
||||
interrupts();
|
||||
|
||||
// save EOD time for latch on next call
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
|
||||
uint32_t _endTime; // Latch timing reference
|
||||
uint8_t* _data; // Holds LED color values
|
||||
uint8_t _pin; // output pin number
|
||||
};
|
||||
|
||||
// Teensy 3.0 or 3.1 (3.2) or 3.5 or 3.6
|
||||
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
||||
|
||||
class NeoArmMk20dxSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t CyclesT0h = (F_CPU / 4000000);
|
||||
static const uint32_t CyclesT1h = (F_CPU / 1250000);
|
||||
static const uint32_t Cycles = (F_CPU / 800000);
|
||||
};
|
||||
|
||||
class NeoArmMk20dxSpeedPropsWs2812x : public NeoArmMk20dxSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoArmMk20dxSpeedPropsSk6812 : public NeoArmMk20dxSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoArmMk20dxSpeedPropsTm1814 : public NeoArmMk20dxSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmMk20dxSpeedPropsTm1829 : public NeoArmMk20dxSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmMk20dxSpeedProps800Kbps : public NeoArmMk20dxSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoArmMk20dxSpeedProps400Kbps
|
||||
{
|
||||
public:
|
||||
static const uint32_t CyclesT0h = (F_CPU / 2000000);
|
||||
static const uint32_t CyclesT1h = (F_CPU / 833333);
|
||||
static const uint32_t Cycles = (F_CPU / 400000);
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoArmMk20dxSpeedPropsApa106
|
||||
{
|
||||
public:
|
||||
static const uint32_t CyclesT0h = (F_CPU / 4000000);
|
||||
static const uint32_t CyclesT1h = (F_CPU / 913750);
|
||||
static const uint32_t Cycles = (F_CPU / 584800);
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
template<typename T_SPEEDPROPS> class NeoArmMk20dxSpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs;
|
||||
|
||||
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
|
||||
{
|
||||
uint8_t* p = pixels;
|
||||
uint8_t* end = p + sizePixels;
|
||||
uint8_t pix;
|
||||
uint8_t mask;
|
||||
|
||||
volatile uint8_t* set = portSetRegister(pin);
|
||||
volatile uint8_t* clr = portClearRegister(pin);
|
||||
|
||||
uint32_t cyc;
|
||||
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
|
||||
cyc = ARM_DWT_CYCCNT + T_SPEEDPROPS::Cycles;
|
||||
while (p < end)
|
||||
{
|
||||
pix = *p++;
|
||||
for (mask = 0x80; mask; mask >>= 1)
|
||||
{
|
||||
while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::Cycles);
|
||||
|
||||
cyc = ARM_DWT_CYCCNT;
|
||||
*set = 1;
|
||||
if (pix & mask)
|
||||
{
|
||||
while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::CyclesT1h);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::CyclesT0h);
|
||||
}
|
||||
*clr = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedPropsWs2812x>> NeoArmWs2812xMethod;
|
||||
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedPropsSk6812>> NeoArmSk6812Method;
|
||||
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedPropsTm1814>> NeoArmTm1814InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedPropsTm1829>> NeoArmTm1829InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedPropsApa106>> NeoArmApa106Method;
|
||||
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedProps800Kbps>> NeoArm800KbpsMethod;
|
||||
typedef NeoArmMethodBase<NeoArmMk20dxSpeedBase<NeoArmMk20dxSpeedProps400Kbps>> NeoArm400KbpsMethod;
|
||||
|
||||
typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod;
|
||||
|
||||
#elif defined(__MKL26Z64__) // Teensy-LC
|
||||
|
||||
#if F_CPU == 48000000
|
||||
|
||||
class NeoArmMk26z64Speed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
|
||||
{
|
||||
uint8_t* p = pixels;
|
||||
uint8_t pix;
|
||||
uint8_t count;
|
||||
uint8_t dly;
|
||||
uint8_t bitmask = digitalPinToBitMask(pin);
|
||||
volatile uint8_t* reg = portSetRegister(pin);
|
||||
uint32_t num = sizePixels;
|
||||
|
||||
asm volatile(
|
||||
"L%=_begin:" "\n\t"
|
||||
"ldrb %[pix], [%[p], #0]" "\n\t"
|
||||
"lsl %[pix], #24" "\n\t"
|
||||
"movs %[count], #7" "\n\t"
|
||||
"L%=_loop:" "\n\t"
|
||||
"lsl %[pix], #1" "\n\t"
|
||||
"bcs L%=_loop_one" "\n\t"
|
||||
"L%=_loop_zero:"
|
||||
"strb %[bitmask], [%[reg], #0]" "\n\t"
|
||||
"movs %[dly], #4" "\n\t"
|
||||
"L%=_loop_delay_T0H:" "\n\t"
|
||||
"sub %[dly], #1" "\n\t"
|
||||
"bne L%=_loop_delay_T0H" "\n\t"
|
||||
"strb %[bitmask], [%[reg], #4]" "\n\t"
|
||||
"movs %[dly], #13" "\n\t"
|
||||
"L%=_loop_delay_T0L:" "\n\t"
|
||||
"sub %[dly], #1" "\n\t"
|
||||
"bne L%=_loop_delay_T0L" "\n\t"
|
||||
"b L%=_next" "\n\t"
|
||||
"L%=_loop_one:"
|
||||
"strb %[bitmask], [%[reg], #0]" "\n\t"
|
||||
"movs %[dly], #13" "\n\t"
|
||||
"L%=_loop_delay_T1H:" "\n\t"
|
||||
"sub %[dly], #1" "\n\t"
|
||||
"bne L%=_loop_delay_T1H" "\n\t"
|
||||
"strb %[bitmask], [%[reg], #4]" "\n\t"
|
||||
"movs %[dly], #4" "\n\t"
|
||||
"L%=_loop_delay_T1L:" "\n\t"
|
||||
"sub %[dly], #1" "\n\t"
|
||||
"bne L%=_loop_delay_T1L" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"L%=_next:" "\n\t"
|
||||
"sub %[count], #1" "\n\t"
|
||||
"bne L%=_loop" "\n\t"
|
||||
"lsl %[pix], #1" "\n\t"
|
||||
"bcs L%=_last_one" "\n\t"
|
||||
"L%=_last_zero:"
|
||||
"strb %[bitmask], [%[reg], #0]" "\n\t"
|
||||
"movs %[dly], #4" "\n\t"
|
||||
"L%=_last_delay_T0H:" "\n\t"
|
||||
"sub %[dly], #1" "\n\t"
|
||||
"bne L%=_last_delay_T0H" "\n\t"
|
||||
"strb %[bitmask], [%[reg], #4]" "\n\t"
|
||||
"movs %[dly], #10" "\n\t"
|
||||
"L%=_last_delay_T0L:" "\n\t"
|
||||
"sub %[dly], #1" "\n\t"
|
||||
"bne L%=_last_delay_T0L" "\n\t"
|
||||
"b L%=_repeat" "\n\t"
|
||||
"L%=_last_one:"
|
||||
"strb %[bitmask], [%[reg], #0]" "\n\t"
|
||||
"movs %[dly], #13" "\n\t"
|
||||
"L%=_last_delay_T1H:" "\n\t"
|
||||
"sub %[dly], #1" "\n\t"
|
||||
"bne L%=_last_delay_T1H" "\n\t"
|
||||
"strb %[bitmask], [%[reg], #4]" "\n\t"
|
||||
"movs %[dly], #1" "\n\t"
|
||||
"L%=_last_delay_T1L:" "\n\t"
|
||||
"sub %[dly], #1" "\n\t"
|
||||
"bne L%=_last_delay_T1L" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"L%=_repeat:" "\n\t"
|
||||
"add %[p], #1" "\n\t"
|
||||
"sub %[num], #1" "\n\t"
|
||||
"bne L%=_begin" "\n\t"
|
||||
"L%=_done:" "\n\t"
|
||||
: [p] "+r" (p),
|
||||
[pix] "=&r" (pix),
|
||||
[count] "=&r" (count),
|
||||
[dly] "=&r" (dly),
|
||||
[num] "+r" (num)
|
||||
: [bitmask] "r" (bitmask),
|
||||
[reg] "r" (reg)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class NeoArmMk26z64SpeedWs2812x : public NeoArmMk26z64Speed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoArmMk26z64SpeedSk6812 : public NeoArmMk26z64Speed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoArmMk26z64SpeedTm1814 : public NeoArmMk26z64Speed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmMk26z64SpeedTm1829 : public NeoArmMk26z64Speed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmMk26z64Speed800Kbps : public NeoArmMk26z64Speed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
typedef NeoArmMethodBase<NeoArmMk26z64SpeedWs2812x> NeoArmWs2812xMethod;
|
||||
typedef NeoArmMethodBase<NeoArmMk26z64SpeedSk6812> NeoArmSk6812Method;
|
||||
typedef NeoArmMethodBase<NeoArmMk26z64SpeedTm1814> NeoArmTm1814InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmMk26z64SpeedTm1829> NeoArmTm1829InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmMk26z64Speed800Kbps> NeoArm800KbpsMethod;
|
||||
typedef NeoArm800KbpsMethod NeoArmApa106Method;
|
||||
typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod;
|
||||
|
||||
#else
|
||||
#error "Teensy-LC: Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz"
|
||||
#endif // F_CPU == 48000000
|
||||
|
||||
#elif defined(__SAMD21G18A__) // Arduino Zero
|
||||
|
||||
|
||||
class NeoArmSamd21g18aSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static void BitPreWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;");
|
||||
}
|
||||
static void BitT1hWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop;");
|
||||
}
|
||||
static void BitT0lWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop;");
|
||||
}
|
||||
static void BitPostWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;");
|
||||
}
|
||||
};
|
||||
|
||||
class NeoArmSamd21g18aSpeedPropsWs2812x : public NeoArmSamd21g18aSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoArmSamd21g18aSpeedPropsSk6812 : public NeoArmSamd21g18aSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoArmSamd21g18aSpeedPropsTm1814 : public NeoArmSamd21g18aSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmSamd21g18aSpeedPropsTm1829 : public NeoArmSamd21g18aSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmSamd21g18aSpeedProps800Kbps : public NeoArmSamd21g18aSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
|
||||
class NeoArmSamd21g18aSpeedProps400Kbps
|
||||
{
|
||||
public:
|
||||
static void BitPreWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
|
||||
}
|
||||
static void BitT1hWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop;");
|
||||
}
|
||||
static void BitT0lWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop;");
|
||||
}
|
||||
static void BitPostWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop;");
|
||||
}
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
template<typename T_SPEEDPROPS> class NeoArmSamd21g18aSpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs;
|
||||
|
||||
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
|
||||
{
|
||||
// Tried this with a timer/counter, couldn't quite get adequate
|
||||
// resolution. So yay, you get a load of goofball NOPs...
|
||||
uint8_t* ptr = pixels;
|
||||
uint8_t* end = ptr + sizePixels;;
|
||||
uint8_t p = *ptr++;
|
||||
uint8_t bitMask = 0x80;
|
||||
uint8_t portNum = g_APinDescription[pin].ulPort;
|
||||
uint32_t pinMask = 1ul << g_APinDescription[pin].ulPin;
|
||||
|
||||
volatile uint32_t* set = &(PORT->Group[portNum].OUTSET.reg);
|
||||
volatile uint32_t* clr = &(PORT->Group[portNum].OUTCLR.reg);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
*set = pinMask;
|
||||
T_SPEEDPROPS::BitPreWait();
|
||||
|
||||
if (p & bitMask)
|
||||
{
|
||||
T_SPEEDPROPS::BitT1hWait();
|
||||
*clr = pinMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
*clr = pinMask;
|
||||
T_SPEEDPROPS::BitT0lWait();
|
||||
}
|
||||
if (bitMask >>= 1)
|
||||
{
|
||||
T_SPEEDPROPS::BitPostWait();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ptr >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
p = *ptr++;
|
||||
bitMask = 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef NeoArmMethodBase<NeoArmSamd21g18aSpeedBase<NeoArmSamd21g18aSpeedPropsWs2812x>> NeoArmWs2812xMethod;
|
||||
typedef NeoArmMethodBase<NeoArmSamd21g18aSpeedBase<NeoArmSamd21g18aSpeedPropsSk6812>> NeoArmSk6812Method;
|
||||
typedef NeoArmMethodBase<NeoArmSamd21g18aSpeedBase<NeoArmSamd21g18aSpeedPropsTm1814>> NeoArmTm1814InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmSamd21g18aSpeedBase<NeoArmSamd21g18aSpeedPropsTm1829>> NeoArmTm1829InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmSamd21g18aSpeedBase<NeoArmSamd21g18aSpeedProps800Kbps>> NeoArm800KbpsMethod;
|
||||
typedef NeoArmMethodBase<NeoArmSamd21g18aSpeedBase<NeoArmSamd21g18aSpeedProps400Kbps>> NeoArm400KbpsMethod;
|
||||
typedef NeoArm400KbpsMethod NeoArmApa106Method;
|
||||
typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod;
|
||||
|
||||
#elif defined(ARDUINO_STM32_FEATHER) || defined(ARDUINO_ARCH_STM32L4) || defined(ARDUINO_ARCH_STM32F4) || defined(ARDUINO_ARCH_STM32F1)// FEATHER WICED (120MHz)
|
||||
|
||||
class NeoArmStm32SpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static void BitT1hWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop;");
|
||||
}
|
||||
static void BitT1lWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop;");
|
||||
}
|
||||
static void BitT0hWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop;");
|
||||
}
|
||||
static void BitT0lWait()
|
||||
{
|
||||
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop; nop; nop; nop; nop;"
|
||||
"nop; nop; nop; nop;");
|
||||
}
|
||||
};
|
||||
|
||||
class NeoArmStm32SpeedPropsWs2812x : public NeoArmStm32SpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoArmStm32SpeedPropsSk6812 : public NeoArmStm32SpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoArmStm32SpeedPropsTm1814 : public NeoArmStm32SpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmStm32SpeedPropsTm1829 : public NeoArmStm32SpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmStm32SpeedProps800Kbps : public NeoArmStm32SpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
/* TODO - not found in Adafruit library
|
||||
class NeoArmStm32SpeedProps400Kbps
|
||||
{
|
||||
static void BitT1hWait()
|
||||
{
|
||||
}
|
||||
static void BitT1lWait()
|
||||
{
|
||||
}
|
||||
static void BitT0hWait()
|
||||
{
|
||||
}
|
||||
static void BitT0lWait()
|
||||
{
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
template<typename T_SPEEDPROPS> class NeoArmStm32SpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs;
|
||||
|
||||
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
|
||||
{
|
||||
// Tried this with a timer/counter, couldn't quite get adequate
|
||||
// resolution. So yay, you get a load of goofball NOPs...
|
||||
|
||||
uint8_t* ptr = pixels;
|
||||
uint8_t* end = ptr + sizePixels;
|
||||
uint8_t p = *ptr++;
|
||||
uint8_t bitMask = 0x80;
|
||||
|
||||
#if defined(ARDUINO_STM32_FEATHER)
|
||||
uint32_t pinMask = BIT(PIN_MAP[pin].gpio_bit);
|
||||
|
||||
volatile uint16_t* set = &(PIN_MAP[pin].gpio_device->regs->BSRRL);
|
||||
volatile uint16_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH);
|
||||
|
||||
#elif defined(ARDUINO_ARCH_STM32F4)
|
||||
uint32_t pinMask = BIT(pin & 0x0f);
|
||||
|
||||
volatile uint16_t* set = &(PIN_MAP[pin].gpio_device->regs->BSRRL);
|
||||
volatile uint16_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH);
|
||||
|
||||
#elif defined(ARDUINO_ARCH_STM32F1)
|
||||
|
||||
uint32_t pinMask = BIT(PIN_MAP[pin].gpio_bit);
|
||||
|
||||
volatile uint32_t* set = &(PIN_MAP[pin].gpio_device->regs->BRR);
|
||||
volatile uint32_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRR);
|
||||
|
||||
#elif defined(ARDUINO_ARCH_STM32L4)
|
||||
|
||||
uint32_t pinMask = g_APinDescription[pin].bit;
|
||||
|
||||
GPIO_TypeDef* GPIO = static_cast<GPIO_TypeDef*>(g_APinDescription[pin].GPIO);
|
||||
|
||||
volatile uint32_t* set = &(GPIO->BRR);
|
||||
volatile uint32_t* clr = &(GPIO->BSRR);
|
||||
|
||||
#endif
|
||||
for (;;)
|
||||
{
|
||||
if (p & bitMask)
|
||||
{
|
||||
// ONE
|
||||
// High 800ns
|
||||
*set = pinMask;
|
||||
T_SPEEDPROPS::BitT1hWait();
|
||||
// Low 450ns
|
||||
*clr = pinMask;
|
||||
T_SPEEDPROPS::BitT1lWait();
|
||||
}
|
||||
else
|
||||
{
|
||||
// ZERO
|
||||
// High 400ns
|
||||
*set = pinMask;
|
||||
T_SPEEDPROPS::BitT0hWait();
|
||||
// Low 850ns
|
||||
*clr = pinMask;
|
||||
T_SPEEDPROPS::BitT0lWait();
|
||||
}
|
||||
if (bitMask >>= 1)
|
||||
{
|
||||
// Move on to the next pixel
|
||||
asm("nop;");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ptr >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
p = *ptr++;
|
||||
bitMask = 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef NeoArmMethodBase<NeoArmStm32SpeedBase<NeoArmStm32SpeedPropsWs2812x>> NeoArmWs2812xMethod;
|
||||
typedef NeoArmMethodBase<NeoArmStm32SpeedBase<NeoArmStm32SpeedPropsSk6812>> NeoArmSk6812Method;
|
||||
typedef NeoArmMethodBase<NeoArmStm32SpeedBase<NeoArmStm32SpeedPropsTm1814>> NeoArmTm1814InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmStm32SpeedBase<NeoArmStm32SpeedPropsTm1829>> NeoArmTm1829InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmStm32SpeedBase<NeoArmStm32SpeedProps800Kbps>> NeoArm800KbpsMethod;
|
||||
typedef NeoArm800KbpsMethod NeoArmApa106Method;
|
||||
typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod;
|
||||
|
||||
#else // Other ARM architecture -- Presumed Arduino Due
|
||||
|
||||
|
||||
#define ARM_OTHER_SCALE VARIANT_MCK / 2UL / 1000000UL
|
||||
#define ARM_OTHER_INST (2UL * F_CPU / VARIANT_MCK)
|
||||
|
||||
class NeoArmOtherSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t CyclesT0h = static_cast<uint32_t>((0.40 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
|
||||
static const uint32_t CyclesT1h = static_cast<uint32_t>((0.80 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
|
||||
static const uint32_t Cycles = static_cast<uint32_t>((1.25 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
|
||||
};
|
||||
|
||||
class NeoArmOtherSpeedPropsWs2812x : public NeoArmOtherSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoArmOtherSpeedPropsSk6812 : public NeoArmOtherSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoArmOtherSpeedPropsTm1814 : public NeoArmOtherSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmOtherSpeedPropsTm1829 : public NeoArmOtherSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoArmOtherSpeedProps800Kbps : public NeoArmOtherSpeedProps800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoArmOtherSpeedProps400Kbps
|
||||
{
|
||||
public:
|
||||
static const uint32_t CyclesT0h = static_cast<uint32_t>((0.50 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
|
||||
static const uint32_t CyclesT1h = static_cast<uint32_t>((1.20 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
|
||||
static const uint32_t Cycles = static_cast<uint32_t>((2.50 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST));
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
template<typename T_SPEEDPROPS> class NeoArmOtherSpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs;
|
||||
|
||||
static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin)
|
||||
{
|
||||
uint32_t pinMask;
|
||||
uint32_t t;
|
||||
Pio* port;
|
||||
volatile WoReg* portSet;
|
||||
volatile WoReg* portClear;
|
||||
volatile WoReg* timeValue;
|
||||
volatile WoReg* timeReset;
|
||||
uint8_t* p;
|
||||
uint8_t* end;
|
||||
uint8_t pix;
|
||||
uint8_t mask;
|
||||
|
||||
pmc_set_writeprotect(false);
|
||||
pmc_enable_periph_clk(static_cast<uint32_t>(TC3_IRQn));
|
||||
|
||||
TC_Configure(TC1, 0,
|
||||
TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1);
|
||||
TC_Start(TC1, 0);
|
||||
|
||||
pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into
|
||||
port = g_APinDescription[pin].pPort; // declarations above. Want to
|
||||
portSet = &(port->PIO_SODR); // burn a few cycles after
|
||||
portClear = &(port->PIO_CODR); // starting timer to minimize
|
||||
timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'.
|
||||
timeReset = &(TC1->TC_CHANNEL[0].TC_CCR);
|
||||
p = pixels;
|
||||
end = p + sizePixels;
|
||||
pix = *p++;
|
||||
mask = 0x80;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (pix & mask)
|
||||
{
|
||||
t = T_SPEEDPROPS::CyclesT1h;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = T_SPEEDPROPS::CyclesT0h;
|
||||
}
|
||||
|
||||
// wait for the end of the previous cycle
|
||||
while (*timeValue < T_SPEEDPROPS::Cycles);
|
||||
|
||||
*portSet = pinMask;
|
||||
*timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG;
|
||||
|
||||
while (*timeValue < t);
|
||||
|
||||
*portClear = pinMask;
|
||||
if (!(mask >>= 1))
|
||||
{
|
||||
// This 'inside-out' loop logic utilizes
|
||||
if (p >= end)
|
||||
{
|
||||
break; // idle time to minimize inter-byte delays.
|
||||
}
|
||||
pix = *p++;
|
||||
mask = 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
// not really needed as the wait for latch does this and
|
||||
// while (*timeValue < T_SPEEDPROPS::Cycles); // Wait for last bit
|
||||
|
||||
TC_Stop(TC1, 0);
|
||||
}
|
||||
};
|
||||
|
||||
typedef NeoArmMethodBase<NeoArmOtherSpeedBase<NeoArmOtherSpeedPropsWs2812x>> NeoArmWs2812xMethod;
|
||||
typedef NeoArmMethodBase<NeoArmOtherSpeedBase<NeoArmOtherSpeedPropsSk6812>> NeoArmSk6812Method;
|
||||
typedef NeoArmMethodBase<NeoArmOtherSpeedBase<NeoArmOtherSpeedPropsTm1814>> NeoArmTm1814InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmOtherSpeedBase<NeoArmOtherSpeedPropsTm1829>> NeoArmTm1829InvertedMethod;
|
||||
typedef NeoArmMethodBase<NeoArmOtherSpeedBase<NeoArmOtherSpeedProps800Kbps>> NeoArm800KbpsMethod;
|
||||
typedef NeoArmMethodBase<NeoArmOtherSpeedBase<NeoArmOtherSpeedProps400Kbps>> NeoArm400KbpsMethod;
|
||||
typedef NeoArm400KbpsMethod NeoArmApa106Method;
|
||||
typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Arm doesn't have alternatives methods yet, so only one to make the default
|
||||
typedef NeoArmWs2812xMethod NeoWs2813Method;
|
||||
typedef NeoArmWs2812xMethod NeoWs2812xMethod;
|
||||
typedef NeoArmWs2812xMethod NeoWs2811Method;
|
||||
typedef NeoArmWs2812xMethod NeoWs2816Method;
|
||||
typedef NeoArmSk6812Method NeoSk6812Method;
|
||||
typedef NeoArmSk6812Method NeoLc8812Method;
|
||||
typedef NeoArm800KbpsMethod NeoWs2812Method;
|
||||
typedef NeoArmApa106Method NeoApa106Method;
|
||||
typedef NeoArmWs2812xMethod Neo800KbpsMethod;
|
||||
#ifdef NeoArm400KbpsMethod // this is needed due to missing 400Kbps for some platforms
|
||||
typedef NeoArm400KbpsMethod Neo400KbpsMethod;
|
||||
#endif
|
||||
// there is no non-invert methods for arm, but the norm for TM1814 is inverted, so
|
||||
typedef NeoArmTm1814InvertedMethod NeoTm1814InvertedMethod;
|
||||
typedef NeoArmTm1914InvertedMethod NeoTm1914InvertedMethod;
|
||||
typedef NeoArmTm1829InvertedMethod NeoTm1829InvertedMethod;
|
||||
|
||||
#endif // defined(__arm__)
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Atmel AVR.
|
||||
|
||||
Written by Michael C. Miller.
|
||||
Some work taken from the Adafruit NeoPixel library.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void send_data_8mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask);
|
||||
void send_data_8mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask);
|
||||
void send_data_8mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
||||
void send_data_12mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask);
|
||||
void send_data_12mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask);
|
||||
void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
||||
void send_data_16mhz_800(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
||||
void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
||||
void send_data_32mhz(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask, const uint8_t cycleTiming);
|
||||
}
|
||||
|
||||
class NeoAvrSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static void send_data(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
||||
{
|
||||
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
|
||||
#ifdef PORTD // PORTD isn't present on ATtiny85, etc.
|
||||
if (port == &PORTD)
|
||||
send_data_8mhz_800_PortD(data, sizeData, pinMask);
|
||||
else if (port == &PORTB)
|
||||
#endif // PORTD
|
||||
send_data_8mhz_800_PortB(data, sizeData, pinMask);
|
||||
|
||||
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
|
||||
#ifdef PORTD // PORTD
|
||||
if (port == &PORTD)
|
||||
send_data_12mhz_800_PortD(data, sizeData, pinMask);
|
||||
else if (port == &PORTB)
|
||||
#endif // PORTD
|
||||
send_data_12mhz_800_PortB(data, sizeData, pinMask);
|
||||
|
||||
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU
|
||||
send_data_16mhz_800(data, sizeData, port, pinMask);
|
||||
#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
|
||||
send_data_32mhz(data, sizeData, port, pinMask, 3);
|
||||
#else
|
||||
#error "CPU SPEED NOT SUPPORTED"
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class NeoAvrSpeedWs2812x : public NeoAvrSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoAvrSpeedSk6812 : public NeoAvrSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoAvrSpeedTm1814 : public NeoAvrSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoAvrSpeedTm1829 : public NeoAvrSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoAvrSpeed800Kbps: public NeoAvrSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoAvrSpeed400Kbps
|
||||
{
|
||||
public:
|
||||
static void send_data(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
||||
{
|
||||
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
|
||||
send_data_8mhz_400(data, sizeData, port, pinMask);
|
||||
|
||||
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
|
||||
send_data_12mhz_400(data, sizeData, port, pinMask);
|
||||
|
||||
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU
|
||||
send_data_16mhz_400(data, sizeData, port, pinMask);
|
||||
#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
|
||||
send_data_32mhz(data, sizeData, port, pinMask, 7);
|
||||
#else
|
||||
#error "CPU SPEED NOT SUPPORTED"
|
||||
#endif
|
||||
}
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
template<typename T_SPEED> class NeoAvrMethodBase
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoAvrMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin),
|
||||
_port(NULL),
|
||||
_pinMask(0)
|
||||
{
|
||||
pinMode(pin, OUTPUT);
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
|
||||
_port = portOutputRegister(digitalPinToPort(pin));
|
||||
_pinMask = digitalPinToBitMask(pin);
|
||||
}
|
||||
|
||||
~NeoAvrMethodBase()
|
||||
{
|
||||
pinMode(_pin, INPUT);
|
||||
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
uint32_t delta = micros() - _endTime;
|
||||
|
||||
return (delta >= T_SPEED::ResetTimeUs);
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
digitalWrite(_pin, LOW);
|
||||
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
// the function will simply hold off (if needed) on issuing the
|
||||
// subsequent round of data until the latch time has elapsed. This
|
||||
// allows the mainline code to start generating the next frame of data
|
||||
// rather than stalling for the latch.
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
#if !defined(ARDUINO_TEEONARDU_LEO) && !defined(ARDUINO_TEEONARDU_FLORA)
|
||||
yield(); // allows for system yield if needed
|
||||
#endif
|
||||
}
|
||||
|
||||
noInterrupts(); // Need 100% focus on instruction timing
|
||||
|
||||
T_SPEED::send_data(_data, _sizeData, _port, _pinMask);
|
||||
|
||||
interrupts();
|
||||
|
||||
// save EOD time for latch on next call
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // size of _data below
|
||||
const uint8_t _pin; // output pin number
|
||||
|
||||
uint32_t _endTime; // Latch timing reference
|
||||
uint8_t* _data; // Holds data stream which include LED color values and other settings as needed
|
||||
|
||||
volatile uint8_t* _port; // Output PORT register
|
||||
uint8_t _pinMask; // Output PORT bitmask
|
||||
};
|
||||
|
||||
|
||||
typedef NeoAvrMethodBase<NeoAvrSpeedWs2812x> NeoAvrWs2812xMethod;
|
||||
typedef NeoAvrMethodBase<NeoAvrSpeedSk6812> NeoAvrSk6812Method;
|
||||
typedef NeoAvrMethodBase<NeoAvrSpeedTm1814> NeoAvrTm1814InvertedMethod;
|
||||
typedef NeoAvrMethodBase<NeoAvrSpeedTm1829> NeoAvrTm1829InvertedMethod;
|
||||
typedef NeoAvrMethodBase<NeoAvrSpeed800Kbps> NeoAvr800KbpsMethod;
|
||||
typedef NeoAvrMethodBase<NeoAvrSpeed400Kbps> NeoAvr400KbpsMethod;
|
||||
typedef NeoAvrTm1814InvertedMethod NeoAvrTm1914InvertedMethod;
|
||||
|
||||
// AVR doesn't have alternatives yet, so there is just the default
|
||||
typedef NeoAvrWs2812xMethod NeoWs2813Method;
|
||||
typedef NeoAvrWs2812xMethod NeoWs2812xMethod;
|
||||
typedef NeoAvr800KbpsMethod NeoWs2812Method;
|
||||
typedef NeoAvrWs2812xMethod NeoWs2811Method;
|
||||
typedef NeoAvrWs2812xMethod NeoWs2816Method;
|
||||
typedef NeoAvrSk6812Method NeoSk6812Method;
|
||||
typedef NeoAvrSk6812Method NeoLc8812Method;
|
||||
typedef NeoAvr400KbpsMethod NeoApa106Method;
|
||||
typedef NeoAvrWs2812xMethod Neo800KbpsMethod;
|
||||
typedef NeoAvr400KbpsMethod Neo400KbpsMethod;
|
||||
|
||||
// there is no non-invert methods for avr, but the norm for TM1814 is inverted, so
|
||||
typedef NeoAvrTm1814InvertedMethod NeoTm1814InvertedMethod;
|
||||
typedef NeoAvrTm1914InvertedMethod NeoTm1914InvertedMethod;
|
||||
typedef NeoAvrTm1829InvertedMethod NeoTm1829InvertedMethod;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp32.
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// ESP32 C3 & S3 I2S is not supported yet due to significant changes to interface
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "Esp32_i2s.h"
|
||||
}
|
||||
|
||||
const uint16_t c_dmaBytesPerPixelBytes = 4;
|
||||
|
||||
class NeoEsp32I2sSpeedWs2812x
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sSampleRate = 100000;
|
||||
const static uint16_t ByteSendTimeUs = 10;
|
||||
const static uint16_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sSpeedSk6812
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sSampleRate = 100000;
|
||||
const static uint16_t ByteSendTimeUs = 10;
|
||||
const static uint16_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sSpeedTm1814
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sSampleRate = 100000;
|
||||
const static uint16_t ByteSendTimeUs = 10;
|
||||
const static uint16_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sSpeedTm1914
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sSampleRate = 100000;
|
||||
const static uint16_t ByteSendTimeUs = 10;
|
||||
const static uint16_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sSpeedTm1829
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sSampleRate = 100000;
|
||||
const static uint16_t ByteSendTimeUs = 10;
|
||||
const static uint16_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sSpeed800Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sSampleRate = 100000;
|
||||
const static uint16_t ByteSendTimeUs = 10;
|
||||
const static uint16_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sSpeed400Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sSampleRate = 50000;
|
||||
const static uint16_t ByteSendTimeUs = 20;
|
||||
const static uint16_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sSpeedApa106
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sSampleRate = 76000;
|
||||
const static uint16_t ByteSendTimeUs = 14;
|
||||
const static uint16_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sBusZero
|
||||
{
|
||||
public:
|
||||
NeoEsp32I2sBusZero() {};
|
||||
|
||||
const static uint8_t I2sBusNumber = 0;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sBusOne
|
||||
{
|
||||
public:
|
||||
NeoEsp32I2sBusOne() {};
|
||||
|
||||
const static uint8_t I2sBusNumber = 1;
|
||||
};
|
||||
|
||||
// dynamic channel support
|
||||
class NeoEsp32I2sBusN
|
||||
{
|
||||
public:
|
||||
NeoEsp32I2sBusN(NeoBusChannel channel) :
|
||||
I2sBusNumber(static_cast<uint8_t>(channel))
|
||||
{
|
||||
}
|
||||
NeoEsp32I2sBusN() = delete; // no default constructor
|
||||
|
||||
const uint8_t I2sBusNumber;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sNotInverted
|
||||
{
|
||||
public:
|
||||
const static bool Inverted = false;
|
||||
};
|
||||
|
||||
class NeoEsp32I2sInverted
|
||||
{
|
||||
public:
|
||||
const static bool Inverted = true;
|
||||
};
|
||||
|
||||
template<typename T_SPEED, typename T_BUS, typename T_INVERT> class NeoEsp32I2sMethodBase
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin)
|
||||
{
|
||||
construct(pixelCount, elementSize, settingsSize);
|
||||
}
|
||||
|
||||
NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin),
|
||||
_bus(channel)
|
||||
{
|
||||
construct(pixelCount, elementSize, settingsSize);
|
||||
}
|
||||
|
||||
~NeoEsp32I2sMethodBase()
|
||||
{
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
i2sSetPins(_bus.I2sBusNumber, -1, -1, -1, false);
|
||||
i2sDeinit(_bus.I2sBusNumber);
|
||||
free(_data);
|
||||
heap_caps_free(_i2sBuffer);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return (i2sWriteDone(_bus.I2sBusNumber));
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
size_t dmaBlockCount = (_i2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
|
||||
|
||||
i2sInit(_bus.I2sBusNumber,
|
||||
false,
|
||||
2, // bytes per sample
|
||||
T_SPEED::I2sSampleRate,
|
||||
I2S_CHAN_STEREO,
|
||||
I2S_FIFO_16BIT_DUAL,
|
||||
dmaBlockCount,
|
||||
_i2sBuffer,
|
||||
_i2sBufferSize);
|
||||
i2sSetPins(_bus.I2sBusNumber, _pin, -1, -1, T_INVERT::Inverted);
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
// wait for not actively sending data
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
FillBuffers();
|
||||
|
||||
i2sWrite(_bus.I2sBusNumber);
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer
|
||||
const uint8_t _pin; // output pin number
|
||||
const T_BUS _bus; // holds instance for multi bus support
|
||||
|
||||
uint8_t* _data; // Holds LED color values
|
||||
|
||||
size_t _i2sBufferSize; // total size of _i2sBuffer
|
||||
uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc
|
||||
|
||||
void construct(uint16_t pixelCount, size_t elementSize, size_t settingsSize)
|
||||
{
|
||||
// DMA is too fast to support a single pixel and maintain consistency
|
||||
if (pixelCount < 2)
|
||||
{
|
||||
pixelCount = 2;
|
||||
}
|
||||
|
||||
uint16_t dmaSettingsSize = c_dmaBytesPerPixelBytes * settingsSize;
|
||||
uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize;
|
||||
uint16_t resetSize = c_dmaBytesPerPixelBytes * T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs;
|
||||
|
||||
_i2sBufferSize = pixelCount * dmaPixelSize + dmaSettingsSize + resetSize;
|
||||
|
||||
// must have a 4 byte aligned buffer for i2s
|
||||
uint32_t alignment = _i2sBufferSize % 4;
|
||||
if (alignment)
|
||||
{
|
||||
_i2sBufferSize += 4 - alignment;
|
||||
}
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
|
||||
_i2sBuffer = static_cast<uint8_t*>(heap_caps_malloc(_i2sBufferSize, MALLOC_CAP_DMA));
|
||||
// no need to initialize all of it, but since it contains
|
||||
// "reset" bits that don't latter get overwritten we just clear it all
|
||||
memset(_i2sBuffer, 0x00, _i2sBufferSize);
|
||||
}
|
||||
|
||||
void FillBuffers()
|
||||
{
|
||||
const uint16_t bitpatterns[16] =
|
||||
{
|
||||
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
|
||||
0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
|
||||
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110,
|
||||
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
|
||||
};
|
||||
|
||||
uint16_t* pDma = reinterpret_cast<uint16_t*>(_i2sBuffer);
|
||||
uint8_t* pEnd = _data + _sizeData;
|
||||
for (uint8_t* pPixel = _data; pPixel < pEnd; pPixel++)
|
||||
{
|
||||
*(pDma++) = bitpatterns[((*pPixel) & 0x0f)];
|
||||
*(pDma++) = bitpatterns[((*pPixel) >> 4) & 0x0f];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2sBusZero, NeoEsp32I2sNotInverted> NeoEsp32I2s0Ws2812xMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2sBusZero, NeoEsp32I2sNotInverted> NeoEsp32I2s0Sk6812Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2sBusZero, NeoEsp32I2sInverted> NeoEsp32I2s0Tm1814Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2sBusZero, NeoEsp32I2sInverted> NeoEsp32I2s0Tm1829Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2sBusZero, NeoEsp32I2sInverted> NeoEsp32I2s0Tm1914Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusZero, NeoEsp32I2sNotInverted> NeoEsp32I2s0800KbpsMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusZero, NeoEsp32I2sNotInverted> NeoEsp32I2s0400KbpsMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusZero, NeoEsp32I2sNotInverted> NeoEsp32I2s0Apa106Method;
|
||||
|
||||
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2sBusZero, NeoEsp32I2sInverted> NeoEsp32I2s0Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2sBusZero, NeoEsp32I2sInverted> NeoEsp32I2s0Sk6812InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2sBusZero, NeoEsp32I2sNotInverted> NeoEsp32I2s0Tm1814InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2sBusZero, NeoEsp32I2sNotInverted> NeoEsp32I2s0Tm1914InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2sBusZero, NeoEsp32I2sNotInverted> NeoEsp32I2s0Tm1829InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusZero, NeoEsp32I2sInverted> NeoEsp32I2s0800KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusZero, NeoEsp32I2sInverted> NeoEsp32I2s0400KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusZero, NeoEsp32I2sInverted> NeoEsp32I2s0Apa106InvertedMethod;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// (I2S_NUM_MAX == 2)
|
||||
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2sBusOne, NeoEsp32I2sNotInverted> NeoEsp32I2s1Ws2812xMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2sBusOne, NeoEsp32I2sNotInverted> NeoEsp32I2s1Sk6812Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1Tm1814Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1Tm1829Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1Tm1914Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusOne, NeoEsp32I2sNotInverted> NeoEsp32I2s1800KbpsMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusOne, NeoEsp32I2sNotInverted> NeoEsp32I2s1400KbpsMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusOne, NeoEsp32I2sNotInverted> NeoEsp32I2s1Apa106Method;
|
||||
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1Sk6812InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2sBusOne, NeoEsp32I2sNotInverted> NeoEsp32I2s1Tm1814InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2sBusOne, NeoEsp32I2sNotInverted> NeoEsp32I2s1Tm1829InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2sBusOne, NeoEsp32I2sNotInverted> NeoEsp32I2s1Tm1914InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1800KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1400KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusOne, NeoEsp32I2sInverted> NeoEsp32I2s1Apa106InvertedMethod;
|
||||
|
||||
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNWs2812xMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNSk6812Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNTm1814Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNTm1829Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNTm1914Method;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sN800KbpsMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sN400KbpsMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNApa106Method;
|
||||
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNWs2812xInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNSk6812InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNTm1814InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNTm1829InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2sBusN, NeoEsp32I2sNotInverted> NeoEsp32I2sNTm1914InvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sN800KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sN400KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2sBusN, NeoEsp32I2sInverted> NeoEsp32I2sNApa106InvertedMethod;
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(NEOPIXEL_ESP32_RMT_DEFAULT) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
// I2s Bus 1 method is the default method for Esp32
|
||||
// Esp32 S2 & C3 & S3 will use RMT as the default allways
|
||||
typedef NeoEsp32I2s1Ws2812xMethod NeoWs2813Method;
|
||||
typedef NeoEsp32I2s1Ws2812xMethod NeoWs2812xMethod;
|
||||
typedef NeoEsp32I2s1800KbpsMethod NeoWs2812Method;
|
||||
typedef NeoEsp32I2s1Ws2812xMethod NeoWs2811Method;
|
||||
typedef NeoEsp32I2s1Ws2812xMethod NeoWs2816Method;
|
||||
typedef NeoEsp32I2s1Sk6812Method NeoSk6812Method;
|
||||
typedef NeoEsp32I2s1Tm1814Method NeoTm1814Method;
|
||||
typedef NeoEsp32I2s1Tm1829Method NeoTm1829Method;
|
||||
typedef NeoEsp32I2s1Tm1914Method NeoTm1914Method;
|
||||
typedef NeoEsp32I2s1Sk6812Method NeoLc8812Method;
|
||||
typedef NeoEsp32I2s1Apa106Method NeoApa106Method;
|
||||
|
||||
typedef NeoEsp32I2s1Ws2812xMethod Neo800KbpsMethod;
|
||||
typedef NeoEsp32I2s1400KbpsMethod Neo400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32I2s1Ws2812xInvertedMethod NeoWs2813InvertedMethod;
|
||||
typedef NeoEsp32I2s1Ws2812xInvertedMethod NeoWs2812xInvertedMethod;
|
||||
typedef NeoEsp32I2s1Ws2812xInvertedMethod NeoWs2811InvertedMethod;
|
||||
typedef NeoEsp32I2s1Ws2812xInvertedMethod NeoWs2816InvertedMethod;
|
||||
typedef NeoEsp32I2s1800KbpsInvertedMethod NeoWs2812InvertedMethod;
|
||||
typedef NeoEsp32I2s1Sk6812InvertedMethod NeoSk6812InvertedMethod;
|
||||
typedef NeoEsp32I2s1Tm1814InvertedMethod NeoTm1814InvertedMethod;
|
||||
typedef NeoEsp32I2s1Tm1829InvertedMethod NeoTm1829InvertedMethod;
|
||||
typedef NeoEsp32I2s1Tm1914InvertedMethod NeoTm1914InvertedMethod;
|
||||
typedef NeoEsp32I2s1Sk6812InvertedMethod NeoLc8812InvertedMethod;
|
||||
typedef NeoEsp32I2s1Apa106InvertedMethod NeoApa106InvertedMethod;
|
||||
|
||||
typedef NeoEsp32I2s1Ws2812xInvertedMethod Neo800KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2s1400KbpsInvertedMethod Neo400KbpsInvertedMethod;
|
||||
|
||||
#endif // !defined(NEOPIXEL_ESP32_RMT_DEFAULT) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,757 @@
|
||||
#pragma once
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp32.
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
// ESP32C3/S3 I2S is not supported yet due to significant changes to interface
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "Esp32_i2s.h"
|
||||
}
|
||||
|
||||
#pragma once
|
||||
|
||||
// ESP32 Endian Map
|
||||
// uint16_t
|
||||
// 1234
|
||||
// 3412
|
||||
// uint32_t
|
||||
// 12345678
|
||||
// 78563412
|
||||
// uint64_t
|
||||
// 0123456789abcdef
|
||||
// efcdab8967452301
|
||||
|
||||
//
|
||||
// true size of mux channel, 8 bit
|
||||
//
|
||||
class NeoEspI2sMuxBusSize8Bit
|
||||
{
|
||||
public:
|
||||
NeoEspI2sMuxBusSize8Bit() {};
|
||||
|
||||
const static size_t MuxBusDataSize = 1;
|
||||
|
||||
static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
// 1234 - order
|
||||
// 3412 = actual due to endianness
|
||||
// 00000001
|
||||
const uint32_t EncodedZeroBit = 0x00000100;
|
||||
// 00010101
|
||||
const uint32_t EncodedOneBit = 0x01000101;
|
||||
#else
|
||||
// 8 channel bits layout for DMA 32bit value
|
||||
// note, right to left
|
||||
// mux bus bit/id 76543210 76543210 76543210 76543210
|
||||
// encode bit # 3 2 1 0
|
||||
// value zero 0 0 0 1
|
||||
// value one 0 1 1 1
|
||||
//
|
||||
// due to indianness between peripheral and cpu, bytes within the words are swapped in the const
|
||||
// 1234 - order
|
||||
// 3412 = actual due to endianness
|
||||
// 00000001
|
||||
const uint32_t EncodedZeroBit = 0x00010000;
|
||||
// 00010101
|
||||
const uint32_t EncodedOneBit = 0x01010001;
|
||||
#endif
|
||||
|
||||
uint32_t* pDma = reinterpret_cast<uint32_t*>(dmaBuffer);
|
||||
const uint8_t* pEnd = data + sizeData;
|
||||
|
||||
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
|
||||
{
|
||||
uint8_t value = *pPixel;
|
||||
|
||||
for (uint8_t bit = 0; bit < 8; bit++)
|
||||
{
|
||||
uint32_t dma = *(pDma);
|
||||
|
||||
dma |= (((value & 0x80) ? EncodedOneBit : EncodedZeroBit) << (muxId));
|
||||
*(pDma++) = dma;
|
||||
value <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// true size of mux channel, 16 bit
|
||||
//
|
||||
class NeoEspI2sMuxBusSize16Bit
|
||||
{
|
||||
public:
|
||||
NeoEspI2sMuxBusSize16Bit() {};
|
||||
|
||||
const static size_t MuxBusDataSize = 2;
|
||||
|
||||
static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId)
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
// 1234 5678 - order
|
||||
// 3412 7856 = actual due to endianness
|
||||
// not swap 0000000000000001
|
||||
const uint64_t EncodedZeroBit64 = 0x0000000000010000;
|
||||
// no swap 0000000100010001
|
||||
const uint64_t EncodedOneBit64 = 0x0001000000010001;
|
||||
// can be shifted by 8!
|
||||
Fillx16(dmaBuffer,
|
||||
data,
|
||||
sizeData,
|
||||
muxId,
|
||||
EncodedZeroBit64,
|
||||
EncodedOneBit64);
|
||||
#else
|
||||
|
||||
// 16 channel bits layout for DMA 64bit value
|
||||
// note, right to left, destination is 32bit chunks
|
||||
// due to indianness between peripheral and cpu,
|
||||
// bytes within the words are swapped and words within dwords
|
||||
// in the literal constants
|
||||
// { } { }
|
||||
// 0123 4567 89ab cdef - order of bytes in literal constant
|
||||
// efcd ab89 6745 2301 - order of memory on ESP32 due to Endianness
|
||||
// 6745 2301 efcd ab89 - 32bit dest means only map using 32bits so swap upper and lower
|
||||
//
|
||||
// Due to final bit locations, can't shift encoded one bit
|
||||
// either left more than 7 or right more than 7 so we have to
|
||||
// split the updates and use different encodings
|
||||
if (muxId < 8)
|
||||
{
|
||||
// endian + dest swap 0000000000000001
|
||||
const uint64_t EncodedZeroBit64 = 0x0000000001000000;
|
||||
// endian + dest swap 0000000100010001
|
||||
const uint64_t EncodedOneBit64 = 0x0100000001000100;
|
||||
// cant be shifted by 8!
|
||||
Fillx16(dmaBuffer,
|
||||
data,
|
||||
sizeData,
|
||||
muxId,
|
||||
EncodedZeroBit64,
|
||||
EncodedOneBit64);
|
||||
}
|
||||
else
|
||||
{
|
||||
// endian + dest swap 0000000000000001
|
||||
// then pre shift by 8 0000000000000100
|
||||
const uint64_t EncodedZeroBit64 = 0x0000000000010000;
|
||||
// endian + dest swap 0000000100010001
|
||||
// then pre shift by 8 0000010001000100
|
||||
const uint64_t EncodedOneBit64 = 0x0001000000010001;
|
||||
Fillx16(dmaBuffer,
|
||||
data,
|
||||
sizeData,
|
||||
muxId - 8, // preshifted
|
||||
EncodedZeroBit64,
|
||||
EncodedOneBit64);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
static void Fillx16(uint8_t* dmaBuffer,
|
||||
const uint8_t* data,
|
||||
size_t sizeData,
|
||||
uint8_t muxShift,
|
||||
const uint64_t EncodedZeroBit64,
|
||||
const uint64_t EncodedOneBit64)
|
||||
{
|
||||
uint64_t* pDma64 = reinterpret_cast<uint64_t*>(dmaBuffer);
|
||||
const uint8_t* pEnd = data + sizeData;
|
||||
|
||||
for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++)
|
||||
{
|
||||
uint8_t value = *pPixel;
|
||||
|
||||
for (uint8_t bit = 0; bit < 8; bit++)
|
||||
{
|
||||
uint64_t dma64 = *(pDma64);
|
||||
|
||||
dma64 |= (((value & 0x80) ? EncodedOneBit64 : EncodedZeroBit64) << (muxShift));
|
||||
*(pDma64++) = dma64;
|
||||
value <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// tracks mux channels used and if updated
|
||||
//
|
||||
// T_FLAG - type used to store bit flags, UINT8_t for 8 channels, UINT16_t for 16
|
||||
// T_MUXSIZE - true size of mux channel = NeoEspI2sMuxBusSize8Bit or NeoEspI2sMuxBusSize16Bit
|
||||
//
|
||||
template<typename T_FLAG, typename T_MUXSIZE> class NeoEspI2sMuxMap : public T_MUXSIZE
|
||||
{
|
||||
public:
|
||||
const static uint8_t InvalidMuxId = -1;
|
||||
const static size_t BusMaxCount = sizeof(T_FLAG) * 8;
|
||||
|
||||
size_t MaxBusDataSize; // max size of stream data from any single mux bus
|
||||
T_FLAG UpdateMap; // bitmap flags of mux buses to track update state
|
||||
T_FLAG UpdateMapMask; // mask to used bits in s_UpdateMap
|
||||
T_FLAG BusCount; // count of mux buses
|
||||
|
||||
// as a static instance, all members get initialized to zero
|
||||
// and the constructor is called at inconsistent time to other globals
|
||||
// so its not useful to have or rely on,
|
||||
// but without it presence they get zeroed far too late
|
||||
NeoEspI2sMuxMap()
|
||||
// //:
|
||||
// //MaxBusDataSize(0),
|
||||
// //UpdateMap(0),
|
||||
// //UpdateMapMask(0),
|
||||
// //BusCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t RegisterNewMuxBus(const size_t dataSize)
|
||||
{
|
||||
// find first available bus id
|
||||
uint8_t muxId = 0;
|
||||
while (muxId < BusMaxCount)
|
||||
{
|
||||
T_FLAG muxIdField = (1 << muxId);
|
||||
if ((UpdateMapMask & muxIdField) == 0)
|
||||
{
|
||||
// complete registration
|
||||
BusCount++;
|
||||
UpdateMapMask |= muxIdField;
|
||||
if (dataSize > MaxBusDataSize)
|
||||
{
|
||||
MaxBusDataSize = dataSize;
|
||||
}
|
||||
break;
|
||||
}
|
||||
muxId++;
|
||||
}
|
||||
if (muxId == BusMaxCount)
|
||||
{
|
||||
log_e("exceded channel limit of %u on bus", BusMaxCount);
|
||||
}
|
||||
return muxId;
|
||||
}
|
||||
|
||||
|
||||
bool DeregisterMuxBus(uint8_t muxId)
|
||||
{
|
||||
T_FLAG muxIdField = (1 << muxId);
|
||||
if (UpdateMapMask & muxIdField)
|
||||
{
|
||||
// complete deregistration
|
||||
BusCount--;
|
||||
UpdateMapMask &= ~muxIdField;
|
||||
if (UpdateMapMask == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAllMuxBusesUpdated()
|
||||
{
|
||||
return (UpdateMap == UpdateMapMask);
|
||||
}
|
||||
|
||||
bool IsNoMuxBusesUpdate()
|
||||
{
|
||||
return (UpdateMap == 0);
|
||||
}
|
||||
|
||||
void MarkMuxBusUpdated(uint8_t muxId)
|
||||
{
|
||||
UpdateMap |= (1 << muxId);
|
||||
}
|
||||
|
||||
void ResetMuxBusesUpdated()
|
||||
{
|
||||
UpdateMap = 0;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
MaxBusDataSize = 0;
|
||||
UpdateMap = 0;
|
||||
UpdateMapMask = 0;
|
||||
BusCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Implementation of a Double Buffered version of a I2sContext
|
||||
// Manages the underlying I2S details including the buffer(s)
|
||||
// This creates a front buffer that can be filled while actively sending
|
||||
// the back buffer, thus improving async operation of the i2s DMA.
|
||||
// Note that the back buffer must be DMA memory, a limited resource, so
|
||||
// the front buffer uses normal memory and copies rather than swap pointers
|
||||
//
|
||||
// T_MUXMAP - NeoEspI2sMuxMap - tracking class for mux state
|
||||
//
|
||||
template<typename T_MUXMAP> class NeoEspI2sDblBuffContext
|
||||
{
|
||||
public:
|
||||
const static size_t DmaBitsPerPixelBit = 4;
|
||||
|
||||
size_t I2sBufferSize; // total size of I2sBuffer
|
||||
uint8_t* I2sBuffer; // holds the DMA buffer that is referenced by I2sBufDesc
|
||||
uint8_t* I2sEditBuffer; // hold a editable buffer that is copied to I2sBuffer
|
||||
T_MUXMAP MuxMap;
|
||||
|
||||
// as a static instance, all members get initialized to zero
|
||||
// and the constructor is called at inconsistent time to other globals
|
||||
// so its not useful to have or rely on,
|
||||
// but without it presence they get zeroed far too late
|
||||
NeoEspI2sDblBuffContext()
|
||||
// //:
|
||||
// //I2sBufferSize(0),
|
||||
// //I2sBuffer(nullptr),
|
||||
// //I2sEditBuffer(nullptr),
|
||||
// //MuxMap()
|
||||
{
|
||||
}
|
||||
|
||||
void Construct(const uint8_t busNumber, uint32_t i2sSampleRate)
|
||||
{
|
||||
// construct only once on first time called
|
||||
if (I2sBuffer == nullptr)
|
||||
{
|
||||
// MuxMap.MaxBusDataSize = max size in bytes of a single channel
|
||||
// DmaBitsPerPixelBit = how many dma bits/byte are needed for each source (pixel) bit/byte
|
||||
// T_MUXMAP::MuxBusDataSize = the true size of data for selected mux mode (not exposed size as i2s0 only supports 16bit mode)
|
||||
I2sBufferSize = MuxMap.MaxBusDataSize * 8 * DmaBitsPerPixelBit * T_MUXMAP::MuxBusDataSize;
|
||||
|
||||
// must have a 4 byte aligned buffer for i2s
|
||||
uint32_t alignment = I2sBufferSize % 4;
|
||||
if (alignment)
|
||||
{
|
||||
I2sBufferSize += 4 - alignment;
|
||||
}
|
||||
|
||||
size_t dmaBlockCount = (I2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN;
|
||||
|
||||
I2sBuffer = static_cast<uint8_t*>(heap_caps_malloc(I2sBufferSize, MALLOC_CAP_DMA));
|
||||
if (I2sBuffer == nullptr)
|
||||
{
|
||||
log_e("send buffer memory allocation failure (size %u)",
|
||||
I2sBufferSize);
|
||||
}
|
||||
memset(I2sBuffer, 0x00, I2sBufferSize);
|
||||
|
||||
I2sEditBuffer = static_cast<uint8_t*>(malloc(I2sBufferSize));
|
||||
if (I2sEditBuffer == nullptr)
|
||||
{
|
||||
log_e("edit buffer memory allocation failure (size %u)",
|
||||
I2sBufferSize);
|
||||
}
|
||||
memset(I2sEditBuffer, 0x00, I2sBufferSize);
|
||||
|
||||
i2sInit(busNumber,
|
||||
true,
|
||||
T_MUXMAP::MuxBusDataSize,
|
||||
i2sSampleRate,
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
// using these modes on ESP32S2 actually allows it to function
|
||||
// in both x8 and x16
|
||||
I2S_CHAN_STEREO,
|
||||
I2S_FIFO_16BIT_DUAL,
|
||||
#else
|
||||
// but they won't work on ESP32 in parallel mode, but these will
|
||||
I2S_CHAN_RIGHT_TO_LEFT,
|
||||
I2S_FIFO_16BIT_SINGLE,
|
||||
#endif
|
||||
dmaBlockCount,
|
||||
I2sBuffer,
|
||||
I2sBufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Destruct(const uint8_t busNumber)
|
||||
{
|
||||
if (I2sBuffer == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
i2sSetPins(busNumber, -1, -1, -1, false);
|
||||
i2sDeinit(busNumber);
|
||||
|
||||
free(I2sEditBuffer);
|
||||
heap_caps_free(I2sBuffer);
|
||||
|
||||
I2sBufferSize = 0;
|
||||
I2sBuffer = nullptr;
|
||||
I2sEditBuffer = nullptr;
|
||||
|
||||
MuxMap.Reset();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Implementation of the low level interface into i2s mux bus
|
||||
//
|
||||
// T_BUSCONTEXT - the context to use, currently only NeoEspI2sDblBuffContext but there is
|
||||
// a plan to provide one that doesn't implement the front buffer but would be less
|
||||
// async as it would have to wait until the last frame was completely sent before
|
||||
// updating and new data
|
||||
// T_BUS - the bus id, NeoEsp32I2sBusZero, NeoEsp32I2sBusOne
|
||||
//
|
||||
template<typename T_BUSCONTEXT, typename T_BUS> class NeoEsp32I2sMuxBus
|
||||
{
|
||||
public:
|
||||
NeoEsp32I2sMuxBus() :
|
||||
_muxId(s_context.MuxMap.InvalidMuxId)
|
||||
{
|
||||
}
|
||||
|
||||
void RegisterNewMuxBus(size_t dataSize)
|
||||
{
|
||||
_muxId = s_context.MuxMap.RegisterNewMuxBus(dataSize);
|
||||
}
|
||||
|
||||
void Initialize(uint8_t pin, uint32_t i2sSampleRate, bool invert)
|
||||
{
|
||||
s_context.Construct(T_BUS::I2sBusNumber, i2sSampleRate);
|
||||
i2sSetPins(T_BUS::I2sBusNumber, pin, _muxId, s_context.MuxMap.MuxBusDataSize, invert);
|
||||
}
|
||||
|
||||
void DeregisterMuxBus()
|
||||
{
|
||||
if (s_context.MuxMap.DeregisterMuxBus(_muxId))
|
||||
{
|
||||
s_context.Destruct(T_BUS::I2sBusNumber);
|
||||
}
|
||||
// disconnect muxed pin?
|
||||
_muxId = s_context.MuxMap.InvalidMuxId;
|
||||
}
|
||||
|
||||
void StartWrite()
|
||||
{
|
||||
if (s_context.MuxMap.IsAllMuxBusesUpdated())
|
||||
{
|
||||
s_context.MuxMap.ResetMuxBusesUpdated();
|
||||
|
||||
// wait for not actively sending data
|
||||
while (!IsWriteDone())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
// copy edit buffer to sending buffer
|
||||
memcpy(s_context.I2sBuffer, s_context.I2sEditBuffer, s_context.I2sBufferSize);
|
||||
i2sWrite(T_BUS::I2sBusNumber);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsWriteDone()
|
||||
{
|
||||
return i2sWriteDone(T_BUS::I2sBusNumber);
|
||||
}
|
||||
|
||||
void FillBuffers(const uint8_t* data, size_t sizeData)
|
||||
{
|
||||
if (s_context.MuxMap.IsNoMuxBusesUpdate())
|
||||
{
|
||||
// clear all the data in preperation for each mux channel to add
|
||||
memset(s_context.I2sEditBuffer, 0x00, s_context.I2sBufferSize);
|
||||
}
|
||||
|
||||
s_context.MuxMap.EncodeIntoDma(s_context.I2sEditBuffer,
|
||||
data,
|
||||
sizeData,
|
||||
_muxId );
|
||||
|
||||
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
|
||||
}
|
||||
|
||||
void MarkUpdated()
|
||||
{
|
||||
s_context.MuxMap.MarkMuxBusUpdated(_muxId);
|
||||
}
|
||||
|
||||
private:
|
||||
static T_BUSCONTEXT s_context;
|
||||
uint8_t _muxId;
|
||||
};
|
||||
|
||||
template<typename T_BUSCONTEXT, typename T_BUS> T_BUSCONTEXT NeoEsp32I2sMuxBus<T_BUSCONTEXT, T_BUS>::s_context = T_BUSCONTEXT();
|
||||
|
||||
//
|
||||
// wrapping layer of the i2s mux bus as a NeoMethod
|
||||
//
|
||||
// T_SPEED - NeoEsp32I2sSpeed* (ex NeoEsp32I2sSpeedWs2812x) used to define output signal form
|
||||
// T_BUS - NeoEsp32I2sMuxBus, the bus to use
|
||||
// T_INVERT - NeoEsp32I2sNotInverted or NeoEsp32I2sInverted, will invert output signal
|
||||
//
|
||||
template<typename T_SPEED, typename T_BUS, typename T_INVERT> class NeoEsp32I2sXMethodBase
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoEsp32I2sXMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin),
|
||||
_bus()
|
||||
{
|
||||
_bus.RegisterNewMuxBus(_sizeData + T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs);
|
||||
}
|
||||
|
||||
~NeoEsp32I2sXMethodBase()
|
||||
{
|
||||
while (!_bus.IsWriteDone())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
_bus.DeregisterMuxBus();
|
||||
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return _bus.IsWriteDone();
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
_bus.Initialize(_pin, T_SPEED::I2sSampleRate, T_INVERT::Inverted);
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
if (_data == nullptr)
|
||||
{
|
||||
log_e("front buffer memory allocation failure");
|
||||
}
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
_bus.FillBuffers(_data, _sizeData);
|
||||
_bus.StartWrite(); // only triggers actual write after all mux busses have updated
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called even if no changes to method buffer
|
||||
// as edit buffer is always cleared and then copied to send buffer and all
|
||||
// mux bus needs to included
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer
|
||||
const uint8_t _pin; // output pin number
|
||||
|
||||
T_BUS _bus; // holds instance for mux bus support
|
||||
uint8_t* _data; // Holds LED color values
|
||||
};
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
||||
|
||||
#else
|
||||
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus;
|
||||
|
||||
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint8_t, NeoEspI2sMuxBusSize8Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus;
|
||||
typedef NeoEsp32I2sMuxBus<NeoEspI2sDblBuffContext<NeoEspI2sMuxMap<uint16_t, NeoEspI2sMuxBusSize16Bit>>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus;
|
||||
|
||||
#endif
|
||||
|
||||
// NORMAL
|
||||
//
|
||||
|
||||
// I2s0x8
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Ws2812xMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Sk6812Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1814Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1829Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Tm1914Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8800KbpsMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8400KbpsMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Apa106Method;
|
||||
|
||||
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2813Method;
|
||||
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2812dMethod;
|
||||
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2811Method;
|
||||
typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2816Method;
|
||||
typedef NeoEsp32I2s0X8800KbpsMethod NeoEsp32I2s0X8Ws2812Method;
|
||||
typedef NeoEsp32I2s0X8Sk6812Method NeoEsp32I2s0X8Lc8812Method;
|
||||
|
||||
// I2s0x16
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Ws2812xMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Sk6812Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1814Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1829Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Tm1914Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16800KbpsMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16400KbpsMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Apa106Method;
|
||||
|
||||
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2813Method;
|
||||
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2812dMethod;
|
||||
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2811Method;
|
||||
typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2816Method;
|
||||
typedef NeoEsp32I2s0X16800KbpsMethod NeoEsp32I2s0X16Ws2812Method;
|
||||
typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Lc8812Method;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
// I2s1x8
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Ws2812xMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Sk6812Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1814Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1829Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Tm1914Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8800KbpsMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8400KbpsMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Apa106Method;
|
||||
|
||||
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2813Method;
|
||||
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2812dMethod;
|
||||
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2811Method;
|
||||
typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2816Method;
|
||||
typedef NeoEsp32I2s1X8800KbpsMethod NeoEsp32I2s1X8Ws2812Method;
|
||||
typedef NeoEsp32I2s1X8Sk6812Method NeoEsp32I2s1X8Lc8812Method;
|
||||
|
||||
// I2s1x16
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Ws2812xMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Sk6812Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1814Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1829Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Tm1914Method;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16800KbpsMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16400KbpsMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Apa106Method;
|
||||
|
||||
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2813Method;
|
||||
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2812dMethod;
|
||||
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2811Method;
|
||||
typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2816Method;
|
||||
typedef NeoEsp32I2s1X16800KbpsMethod NeoEsp32I2s1X16Ws2812Method;
|
||||
typedef NeoEsp32I2s1X16Sk6812Method NeoEsp32I2s1X16Lc8812Method;
|
||||
|
||||
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
// INVERTED
|
||||
//
|
||||
// I2s0x8 INVERTED
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Sk6812InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1814InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1829InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X8Tm1914InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8800KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8400KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X8Apa106InvertedMethod;
|
||||
|
||||
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2813InvertedMethod;
|
||||
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2811InvertedMethod;
|
||||
typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2816InvertedMethod;
|
||||
typedef NeoEsp32I2s0X8800KbpsInvertedMethod NeoEsp32I2s0X8Ws2812InvertedMethod;
|
||||
typedef NeoEsp32I2s0X8Sk6812InvertedMethod NeoEsp32I2s0X8Lc8812InvertedMethod;
|
||||
|
||||
|
||||
// I2s0x16 INVERTED
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Sk6812InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1814InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1829InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s0X16Tm1914InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16800KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16400KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s0Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s0X16Apa106InvertedMethod;
|
||||
|
||||
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2813InvertedMethod;
|
||||
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2811InvertedMethod;
|
||||
typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2816InvertedMethod;
|
||||
typedef NeoEsp32I2s0X16800KbpsInvertedMethod NeoEsp32I2s0X16Ws2812InvertedMethod;
|
||||
typedef NeoEsp32I2s0X16Sk6812InvertedMethod NeoEsp32I2s0X16Lc8812InvertedMethod;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
// I2s1x8 INVERTED
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Sk6812InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1814InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1829InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X8Tm1914InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8800KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8400KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux8Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X8Apa106InvertedMethod;
|
||||
|
||||
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2813InvertedMethod;
|
||||
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2811InvertedMethod;
|
||||
typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2816InvertedMethod;
|
||||
typedef NeoEsp32I2s1X8800KbpsInvertedMethod NeoEsp32I2s1X8Ws2812InvertedMethod;
|
||||
typedef NeoEsp32I2s1X8Sk6812InvertedMethod NeoEsp32I2s1X8Lc8812InvertedMethod;
|
||||
|
||||
// I2s1x16 INVERTED
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedWs2812x, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedSk6812, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Sk6812InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1814, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1814InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1829, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1829InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedTm1914, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sNotInverted> NeoEsp32I2s1X16Tm1914InvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed800Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16800KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeed400Kbps, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16400KbpsInvertedMethod;
|
||||
typedef NeoEsp32I2sXMethodBase<NeoEsp32I2sSpeedApa106, NeoEsp32I2s1Mux16Bus, NeoEsp32I2sInverted> NeoEsp32I2s1X16Apa106InvertedMethod;
|
||||
|
||||
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2813InvertedMethod;
|
||||
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2811InvertedMethod;
|
||||
typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2816InvertedMethod;
|
||||
typedef NeoEsp32I2s1X16800KbpsInvertedMethod NeoEsp32I2s1X16Ws2812InvertedMethod;
|
||||
typedef NeoEsp32I2s1X16Sk6812InvertedMethod NeoEsp32I2s1X16Lc8812InvertedMethod;
|
||||
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
#endif // defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
@@ -0,0 +1,331 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp32.
|
||||
|
||||
A BIG thanks to Andreas Merkle for the investigation and implementation of
|
||||
a workaround to the GCC bug that drops method attributes from template methods
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "../NeoSettings.h"
|
||||
#include "../NeoBusChannel.h"
|
||||
#include "NeoEsp32RmtMethod.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
|
||||
// translate NeoPixelBuffer into RMT buffer
|
||||
// this is done on the fly so we don't require a send buffer in raw RMT format
|
||||
// which would be 32x larger than the primary buffer
|
||||
//
|
||||
// NOTE: This was moved from the template below to here to workaround a GCC bug
|
||||
// That bug is that the IRAM_ATTR attribute (any attributes) is lost on template classes.
|
||||
//
|
||||
// Further, it was removed from the header to support current Esp32 release
|
||||
// which will need to be removed when the latest GitHub branchis released
|
||||
// due to this method will not get inlined this way
|
||||
//
|
||||
void NeoEsp32RmtSpeed::_translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num,
|
||||
const uint32_t rmtBit0,
|
||||
const uint32_t rmtBit1,
|
||||
const uint16_t rmtDurationReset)
|
||||
{
|
||||
if (src == NULL || dest == NULL)
|
||||
{
|
||||
*translated_size = 0;
|
||||
*item_num = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = 0;
|
||||
size_t num = 0;
|
||||
const uint8_t* psrc = static_cast<const uint8_t*>(src);
|
||||
rmt_item32_t* pdest = dest;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint8_t data = *psrc;
|
||||
|
||||
for (uint8_t bit = 0; bit < 8; bit++)
|
||||
{
|
||||
pdest->val = (data & 0x80) ? rmtBit1 : rmtBit0;
|
||||
pdest++;
|
||||
data <<= 1;
|
||||
}
|
||||
num += 8;
|
||||
size++;
|
||||
|
||||
// if this is the last byte we need to adjust the length of the last pulse
|
||||
if (size >= src_size)
|
||||
{
|
||||
// extend the last bits LOW value to include the full reset signal length
|
||||
pdest--;
|
||||
pdest->duration1 = rmtDurationReset;
|
||||
// and stop updating data to send
|
||||
break;
|
||||
}
|
||||
|
||||
if (num >= wanted_num)
|
||||
{
|
||||
// stop updating data to send
|
||||
break;
|
||||
}
|
||||
|
||||
psrc++;
|
||||
}
|
||||
|
||||
*translated_size = size;
|
||||
*item_num = num;
|
||||
}
|
||||
|
||||
|
||||
// these are required due to the linker error with ISRs
|
||||
// dangerous relocation: l32r: literal placed after use
|
||||
// https://stackoverflow.com/questions/19532826/what-does-a-dangerous-relocation-error-mean
|
||||
//
|
||||
void NeoEsp32RmtSpeedWs2811::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeedWs2812x::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeedSk6812::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeedTm1814::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeedTm1829::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeedTm1914::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeed800Kbps::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeed400Kbps::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeedApa106::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtSpeedTx1812::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeedWs2811::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeedWs2812x::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeedSk6812::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeedTm1814::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeedTm1829::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeedTm1914::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeed800Kbps::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeed400Kbps::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeedApa106::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
|
||||
void NeoEsp32RmtInvertedSpeedTx1812::Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num)
|
||||
{
|
||||
_translate(src, dest, src_size, wanted_num, translated_size, item_num,
|
||||
RmtBit0, RmtBit1, RmtDurationReset);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,941 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp32.
|
||||
|
||||
A BIG thanks to Andreas Merkle for the investigation and implementation of
|
||||
a workaround to the GCC bug that drops method attributes from template methods
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
/* General Reference documentation for the APIs used in this implementation
|
||||
LOW LEVEL: (what is actually used)
|
||||
DOCS: https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/rmt.html
|
||||
EXAMPLE: https://github.com/espressif/esp-idf/blob/826ff7186ae07dc81e960a8ea09ebfc5304bfb3b/examples/peripherals/rmt_tx/main/rmt_tx_main.c
|
||||
|
||||
HIGHER LEVEL:
|
||||
NO TRANSLATE SUPPORT so this was not used
|
||||
NOTE: https://github.com/espressif/arduino-esp32/commit/50d142950d229b8fabca9b749dc4a5f2533bc426
|
||||
Esp32-hal-rmt.h
|
||||
Esp32-hal-rmt.c
|
||||
*/
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <driver/rmt.h>
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
#define NEOPIXELBUS_RMT_INT_FLAGS (ESP_INTR_FLAG_LOWMED)
|
||||
#else
|
||||
#define NEOPIXELBUS_RMT_INT_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1)
|
||||
#endif
|
||||
|
||||
class NeoEsp32RmtSpeed
|
||||
{
|
||||
public:
|
||||
// ClkDiv of 2 provides for good resolution and plenty of reset resolution; but
|
||||
// a ClkDiv of 1 will provide enough space for the longest reset and does show
|
||||
// little better pulse accuracy
|
||||
const static uint8_t RmtClockDivider = 2;
|
||||
|
||||
inline constexpr static uint32_t FromNs(uint32_t ns)
|
||||
{
|
||||
return ns / NsPerRmtTick;
|
||||
}
|
||||
|
||||
protected:
|
||||
const static uint32_t RmtCpu = 80000000L; // 80 mhz RMT clock
|
||||
const static uint32_t NsPerSecond = 1000000000L;
|
||||
const static uint32_t RmtTicksPerSecond = (RmtCpu / RmtClockDivider);
|
||||
const static uint32_t NsPerRmtTick = (NsPerSecond / RmtTicksPerSecond); // about 25
|
||||
|
||||
static void IRAM_ATTR _translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num,
|
||||
const uint32_t rmtBit0,
|
||||
const uint32_t rmtBit1,
|
||||
const uint16_t rmtDurationReset);
|
||||
|
||||
};
|
||||
|
||||
class NeoEsp32RmtSpeedBase : public NeoEsp32RmtSpeed
|
||||
{
|
||||
public:
|
||||
// this is used rather than the rmt_item32_t as you can't correctly initialize
|
||||
// it as a static constexpr within the template
|
||||
inline constexpr static uint32_t Item32Val(uint16_t nsHigh, uint16_t nsLow)
|
||||
{
|
||||
return (FromNs(nsLow) << 16) | (1 << 15) | (FromNs(nsHigh));
|
||||
}
|
||||
|
||||
const static rmt_idle_level_t IdleLevel = RMT_IDLE_LEVEL_LOW;
|
||||
};
|
||||
|
||||
class NeoEsp32RmtInvertedSpeedBase : public NeoEsp32RmtSpeed
|
||||
{
|
||||
public:
|
||||
// this is used rather than the rmt_item32_t as you can't correctly initialize
|
||||
// it as a static constexpr within the template
|
||||
inline constexpr static uint32_t Item32Val(uint16_t nsHigh, uint16_t nsLow)
|
||||
{
|
||||
return (FromNs(nsLow) << 16) | (1 << 31) | (FromNs(nsHigh));
|
||||
}
|
||||
|
||||
const static rmt_idle_level_t IdleLevel = RMT_IDLE_LEVEL_HIGH;
|
||||
};
|
||||
|
||||
class NeoEsp32RmtSpeedWs2811 : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 950);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(900, 350);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtSpeedWs2812x : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtSpeedSk6812 : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(80000); // 80us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
// normal is inverted signal
|
||||
class NeoEsp32RmtSpeedTm1814 : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
// normal is inverted signal
|
||||
class NeoEsp32RmtSpeedTm1829 : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 900);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 400);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
// normal is inverted signal
|
||||
class NeoEsp32RmtSpeedTm1914 : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtSpeed800Kbps : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtSpeed400Kbps : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(800, 1700);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1600, 900);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtSpeedApa106 : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(350, 1350);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1350, 350);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtSpeedTx1812 : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 600);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(600, 300);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(80000); // 80us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtInvertedSpeedWs2811 : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 950);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(900, 350);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtInvertedSpeedWs2812x : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtInvertedSpeedSk6812 : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(80000); // 80us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
// normal is inverted signal
|
||||
class NeoEsp32RmtInvertedSpeedTm1814 : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
// normal is inverted signal
|
||||
class NeoEsp32RmtInvertedSpeedTm1829 : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 900);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 400);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
// normal is inverted signal
|
||||
class NeoEsp32RmtInvertedSpeedTm1914 : public NeoEsp32RmtSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtInvertedSpeed800Kbps : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtInvertedSpeed400Kbps : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(800, 1700);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1600, 900);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtInvertedSpeedApa106 : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(350, 1350);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1350, 350);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtInvertedSpeedTx1812 : public NeoEsp32RmtInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 600);
|
||||
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(600, 300);
|
||||
const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(80000); // 80us
|
||||
|
||||
static void IRAM_ATTR Translate(const void* src,
|
||||
rmt_item32_t* dest,
|
||||
size_t src_size,
|
||||
size_t wanted_num,
|
||||
size_t* translated_size,
|
||||
size_t* item_num);
|
||||
};
|
||||
|
||||
class NeoEsp32RmtChannel0
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannel0() {};
|
||||
|
||||
const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_0;
|
||||
};
|
||||
|
||||
class NeoEsp32RmtChannel1
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannel1() {};
|
||||
|
||||
const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_1;
|
||||
};
|
||||
|
||||
class NeoEsp32RmtChannel2
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannel2() {};
|
||||
|
||||
const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_2;
|
||||
};
|
||||
|
||||
class NeoEsp32RmtChannel3
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannel3() {};
|
||||
|
||||
const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_3;
|
||||
};
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
class NeoEsp32RmtChannel4
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannel4() {};
|
||||
|
||||
const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_4;
|
||||
};
|
||||
|
||||
class NeoEsp32RmtChannel5
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannel5() {};
|
||||
|
||||
const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_5;
|
||||
};
|
||||
|
||||
class NeoEsp32RmtChannel6
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannel6() {};
|
||||
|
||||
const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_6;
|
||||
};
|
||||
|
||||
class NeoEsp32RmtChannel7
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannel7() {};
|
||||
|
||||
const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_7;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// dynamic channel support
|
||||
class NeoEsp32RmtChannelN
|
||||
{
|
||||
public:
|
||||
NeoEsp32RmtChannelN(NeoBusChannel channel) :
|
||||
RmtChannelNumber(static_cast<rmt_channel_t>(channel))
|
||||
{
|
||||
}
|
||||
NeoEsp32RmtChannelN() = delete; // no default constructor
|
||||
|
||||
const rmt_channel_t RmtChannelNumber;
|
||||
};
|
||||
|
||||
template<typename T_SPEED, typename T_CHANNEL> class NeoEsp32RmtMethodBase
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoEsp32RmtMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin)
|
||||
{
|
||||
construct();
|
||||
}
|
||||
|
||||
NeoEsp32RmtMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
|
||||
_sizeData(pixelCount* elementSize + settingsSize),
|
||||
_pin(pin),
|
||||
_channel(channel)
|
||||
{
|
||||
construct();
|
||||
}
|
||||
|
||||
~NeoEsp32RmtMethodBase()
|
||||
{
|
||||
// wait until the last send finishes before destructing everything
|
||||
// arbitrary time out of 10 seconds
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(rmt_wait_tx_done(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS));
|
||||
|
||||
ESP_ERROR_CHECK(rmt_driver_uninstall(_channel.RmtChannelNumber));
|
||||
|
||||
gpio_matrix_out(_pin, 0x100, false, false);
|
||||
pinMode(_pin, INPUT);
|
||||
|
||||
free(_dataEditing);
|
||||
free(_dataSending);
|
||||
}
|
||||
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return (ESP_OK == rmt_wait_tx_done(_channel.RmtChannelNumber, 0));
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
rmt_config_t config = {};
|
||||
|
||||
config.rmt_mode = RMT_MODE_TX;
|
||||
config.channel = _channel.RmtChannelNumber;
|
||||
config.gpio_num = static_cast<gpio_num_t>(_pin);
|
||||
config.mem_block_num = 1;
|
||||
config.tx_config.loop_en = false;
|
||||
|
||||
config.tx_config.idle_output_en = true;
|
||||
config.tx_config.idle_level = T_SPEED::IdleLevel;
|
||||
|
||||
config.tx_config.carrier_en = false;
|
||||
config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
|
||||
|
||||
config.clk_div = T_SPEED::RmtClockDivider;
|
||||
|
||||
ESP_ERROR_CHECK(rmt_config(&config));
|
||||
ESP_ERROR_CHECK(rmt_driver_install(_channel.RmtChannelNumber, 0, NEOPIXELBUS_RMT_INT_FLAGS));
|
||||
ESP_ERROR_CHECK(rmt_translator_init(_channel.RmtChannelNumber, T_SPEED::Translate));
|
||||
}
|
||||
|
||||
void Update(bool maintainBufferConsistency)
|
||||
{
|
||||
// wait for not actively sending data
|
||||
// this will time out at 10 seconds, an arbitrarily long period of time
|
||||
// and do nothing if this happens
|
||||
if (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT(rmt_wait_tx_done(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS)))
|
||||
{
|
||||
// now start the RMT transmit with the editing buffer before we swap
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(rmt_write_sample(_channel.RmtChannelNumber, _dataEditing, _sizeData, false));
|
||||
|
||||
if (maintainBufferConsistency)
|
||||
{
|
||||
// copy editing to sending,
|
||||
// this maintains the contract that "colors present before will
|
||||
// be the same after", otherwise GetPixelColor will be inconsistent
|
||||
memcpy(_dataSending, _dataEditing, _sizeData);
|
||||
}
|
||||
|
||||
// swap so the user can modify without affecting the async operation
|
||||
std::swap(_dataSending, _dataEditing);
|
||||
}
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _dataEditing;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data*' buffers
|
||||
const uint8_t _pin; // output pin number
|
||||
const T_CHANNEL _channel; // holds instance for multi channel support
|
||||
|
||||
// Holds data stream which include LED color values and other settings as needed
|
||||
uint8_t* _dataEditing; // exposed for get and set
|
||||
uint8_t* _dataSending; // used for async send using RMT
|
||||
|
||||
|
||||
void construct()
|
||||
{
|
||||
_dataEditing = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
|
||||
_dataSending = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// no need to initialize it, it gets overwritten on every send
|
||||
}
|
||||
};
|
||||
|
||||
// normal
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtNWs2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtNWs2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtNWs2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtNSk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtNApa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannelN> NeoEsp32RmtNTx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtN800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtN400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32Rmt0Sk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel0> NeoEsp32Rmt0Tm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel0> NeoEsp32Rmt0Tm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel0> NeoEsp32Rmt0Tm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel0> NeoEsp32Rmt0Apa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel0> NeoEsp32Rmt0Tx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel0> NeoEsp32Rmt0800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel0> NeoEsp32Rmt0400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel1> NeoEsp32Rmt1Ws2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32Rmt1Ws2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32Rmt1Ws2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel1> NeoEsp32Rmt1Sk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel1> NeoEsp32Rmt1Tm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel1> NeoEsp32Rmt1Tm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel1> NeoEsp32Rmt1Tm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel1> NeoEsp32Rmt1Apa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel1> NeoEsp32Rmt1Tx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel1> NeoEsp32Rmt1800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel1> NeoEsp32Rmt1400KbpsMethod;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel2> NeoEsp32Rmt2Ws2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32Rmt2Ws2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32Rmt2Ws2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel2> NeoEsp32Rmt2Sk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel2> NeoEsp32Rmt2Tm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel2> NeoEsp32Rmt2Tm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel2> NeoEsp32Rmt2Tm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel2> NeoEsp32Rmt2Apa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel2> NeoEsp32Rmt2Tx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel2> NeoEsp32Rmt2800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel2> NeoEsp32Rmt2400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel3> NeoEsp32Rmt3Ws2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32Rmt3Ws2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32Rmt3Ws2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel3> NeoEsp32Rmt3Sk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel3> NeoEsp32Rmt3Tm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel3> NeoEsp32Rmt3Tm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel3> NeoEsp32Rmt3Tm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel3> NeoEsp32Rmt3Apa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel3> NeoEsp32Rmt3Tx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel3> NeoEsp32Rmt3800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel3> NeoEsp32Rmt3400KbpsMethod;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel4> NeoEsp32Rmt4Ws2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32Rmt4Ws2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32Rmt4Ws2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel4> NeoEsp32Rmt4Sk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel4> NeoEsp32Rmt4Tm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel4> NeoEsp32Rmt4Tm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel4> NeoEsp32Rmt4Tm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel4> NeoEsp32Rmt4Apa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel4> NeoEsp32Rmt4Tx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel4> NeoEsp32Rmt4800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel4> NeoEsp32Rmt4400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel5> NeoEsp32Rmt5Ws2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32Rmt5Ws2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32Rmt5Ws2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel5> NeoEsp32Rmt5Sk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel5> NeoEsp32Rmt5Tm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel5> NeoEsp32Rmt5Tm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel5> NeoEsp32Rmt5Tm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel5> NeoEsp32Rmt5Apa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel5> NeoEsp32Rmt5Tx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel5> NeoEsp32Rmt5800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel5> NeoEsp32Rmt5400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel6> NeoEsp32Rmt6Ws2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32Rmt6Ws2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32Rmt6Ws2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel6> NeoEsp32Rmt6Sk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel6> NeoEsp32Rmt6Tm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel6> NeoEsp32Rmt6Tm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel6> NeoEsp32Rmt6Tm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel6> NeoEsp32Rmt6Apa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel6> NeoEsp32Rmt6Tx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel6> NeoEsp32Rmt6800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel6> NeoEsp32Rmt6400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel7> NeoEsp32Rmt7Ws2811Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32Rmt7Ws2812xMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32Rmt7Ws2816Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel7> NeoEsp32Rmt7Sk6812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel7> NeoEsp32Rmt7Tm1814Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel7> NeoEsp32Rmt7Tm1829Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel7> NeoEsp32Rmt7Tm1914Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel7> NeoEsp32Rmt7Apa106Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel7> NeoEsp32Rmt7Tx1812Method;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel7> NeoEsp32Rmt7800KbpsMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel7> NeoEsp32Rmt7400KbpsMethod;
|
||||
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
// inverted
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtNWs2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtNWs2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtNWs2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtNSk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtNApa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannelN> NeoEsp32RmtNTx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtN800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtN400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32Rmt0Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel0> NeoEsp32Rmt0Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel0> NeoEsp32Rmt0Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel0> NeoEsp32Rmt0Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel0> NeoEsp32Rmt0Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel0> NeoEsp32Rmt0Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel0> NeoEsp32Rmt0800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel0> NeoEsp32Rmt0400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel1> NeoEsp32Rmt1Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32Rmt1Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32Rmt1Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel1> NeoEsp32Rmt1Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel1> NeoEsp32Rmt1Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel1> NeoEsp32Rmt1Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel1> NeoEsp32Rmt1Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel1> NeoEsp32Rmt1Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel1> NeoEsp32Rmt1Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel1> NeoEsp32Rmt1800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel1> NeoEsp32Rmt1400KbpsInvertedMethod;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel2> NeoEsp32Rmt2Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32Rmt2Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32Rmt2Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel2> NeoEsp32Rmt2Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel2> NeoEsp32Rmt2Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel2> NeoEsp32Rmt2Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel2> NeoEsp32Rmt2Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel2> NeoEsp32Rmt2Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel2> NeoEsp32Rmt2Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel2> NeoEsp32Rmt2800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel2> NeoEsp32Rmt2400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel3> NeoEsp32Rmt3Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32Rmt3Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32Rmt3Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel3> NeoEsp32Rmt3Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel3> NeoEsp32Rmt3Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel3> NeoEsp32Rmt3Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel3> NeoEsp32Rmt3Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel3> NeoEsp32Rmt3Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel3> NeoEsp32Rmt3Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel3> NeoEsp32Rmt3800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel3> NeoEsp32Rmt3400KbpsInvertedMethod;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel4> NeoEsp32Rmt4Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32Rmt4Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32Rmt4Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel4> NeoEsp32Rmt4Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel4> NeoEsp32Rmt4Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel4> NeoEsp32Rmt4Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel4> NeoEsp32Rmt4Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel4> NeoEsp32Rmt4Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel4> NeoEsp32Rmt4Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel4> NeoEsp32Rmt4800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel4> NeoEsp32Rmt4400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel5> NeoEsp32Rmt5Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32Rmt5Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32Rmt5Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel5> NeoEsp32Rmt5Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel5> NeoEsp32Rmt5Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel5> NeoEsp32Rmt5Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel5> NeoEsp32Rmt5Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel5> NeoEsp32Rmt5Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel5> NeoEsp32Rmt5Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel5> NeoEsp32Rmt5800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel5> NeoEsp32Rmt5400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel6> NeoEsp32Rmt6Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32Rmt6Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32Rmt6Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel6> NeoEsp32Rmt6Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel6> NeoEsp32Rmt6Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel6> NeoEsp32Rmt6Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel6> NeoEsp32Rmt6Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel6> NeoEsp32Rmt6Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel6> NeoEsp32Rmt6Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel6> NeoEsp32Rmt6800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel6> NeoEsp32Rmt6400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel7> NeoEsp32Rmt7Ws2811InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32Rmt7Ws2812xInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32Rmt7Ws2816InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel7> NeoEsp32Rmt7Sk6812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel7> NeoEsp32Rmt7Tm1814InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel7> NeoEsp32Rmt7Tm1829InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel7> NeoEsp32Rmt7Tm1914InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel7> NeoEsp32Rmt7Apa106InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel7> NeoEsp32Rmt7Tx1812InvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel7> NeoEsp32Rmt7800KbpsInvertedMethod;
|
||||
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel7> NeoEsp32Rmt7400KbpsInvertedMethod;
|
||||
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
|
||||
#if defined(NEOPIXEL_ESP32_RMT_DEFAULT) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
// Normally I2s method is the default, defining NEOPIXEL_ESP32_RMT_DEFAULT
|
||||
// will switch to use RMT as the default method
|
||||
// The ESP32S2 & ESP32C3 will always defualt to RMT
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
// RMT channel 1 method is the default method for Esp32S2 & Esp32C3
|
||||
typedef NeoEsp32Rmt1Ws2812xMethod NeoWs2813Method;
|
||||
typedef NeoEsp32Rmt1Ws2812xMethod NeoWs2812xMethod;
|
||||
typedef NeoEsp32Rmt1800KbpsMethod NeoWs2812Method;
|
||||
typedef NeoEsp32Rmt1Ws2812xMethod NeoWs2811Method;
|
||||
typedef NeoEsp32Rmt1Ws2812xMethod NeoWs2816Method;
|
||||
typedef NeoEsp32Rmt1Sk6812Method NeoSk6812Method;
|
||||
typedef NeoEsp32Rmt1Tm1814Method NeoTm1814Method;
|
||||
typedef NeoEsp32Rmt1Tm1829Method NeoTm1829Method;
|
||||
typedef NeoEsp32Rmt1Tm1914Method NeoTm1914Method;
|
||||
typedef NeoEsp32Rmt1Sk6812Method NeoLc8812Method;
|
||||
typedef NeoEsp32Rmt1Apa106Method NeoApa106Method;
|
||||
typedef NeoEsp32Rmt1Tx1812Method NeoTx1812Method;
|
||||
|
||||
typedef NeoEsp32Rmt1Ws2812xMethod Neo800KbpsMethod;
|
||||
typedef NeoEsp32Rmt1400KbpsMethod Neo400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32Rmt1Ws2812xInvertedMethod NeoWs2813InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Ws2812xInvertedMethod NeoWs2812xInvertedMethod;
|
||||
typedef NeoEsp32Rmt1Ws2812xInvertedMethod NeoWs2811InvertedMethod;
|
||||
typedef NeoEsp32Rmt1800KbpsInvertedMethod NeoWs2812InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Ws2812xInvertedMethod NeoWs2816InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Sk6812InvertedMethod NeoSk6812InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Tm1814InvertedMethod NeoTm1814InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Tm1829InvertedMethod NeoTm1829InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Tm1914InvertedMethod NeoTm1914InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Sk6812InvertedMethod NeoLc8812InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Apa106InvertedMethod NeoApa106InvertedMethod;
|
||||
typedef NeoEsp32Rmt1Tx1812InvertedMethod NeoTx1812InvertedMethod;
|
||||
|
||||
typedef NeoEsp32Rmt1Ws2812xInvertedMethod Neo800KbpsInvertedMethod;
|
||||
typedef NeoEsp32Rmt1400KbpsInvertedMethod Neo400KbpsInvertedMethod;
|
||||
|
||||
#else // defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
// RMT channel 6 method is the default method for Esp32
|
||||
typedef NeoEsp32Rmt6Ws2812xMethod NeoWs2813Method;
|
||||
typedef NeoEsp32Rmt6Ws2812xMethod NeoWs2812xMethod;
|
||||
typedef NeoEsp32Rmt6800KbpsMethod NeoWs2812Method;
|
||||
typedef NeoEsp32Rmt6Ws2812xMethod NeoWs2811Method;
|
||||
typedef NeoEsp32Rmt6Ws2812xMethod NeoWs2816Method;
|
||||
typedef NeoEsp32Rmt6Sk6812Method NeoSk6812Method;
|
||||
typedef NeoEsp32Rmt6Tm1814Method NeoTm1814Method;
|
||||
typedef NeoEsp32Rmt6Tm1829Method NeoTm1829Method;
|
||||
typedef NeoEsp32Rmt6Tm1914Method NeoTm1914Method;
|
||||
typedef NeoEsp32Rmt6Sk6812Method NeoLc8812Method;
|
||||
typedef NeoEsp32Rmt6Apa106Method NeoApa106Method;
|
||||
typedef NeoEsp32Rmt6Tx1812Method NeoTx1812Method;
|
||||
|
||||
typedef NeoEsp32Rmt6Ws2812xMethod Neo800KbpsMethod;
|
||||
typedef NeoEsp32Rmt6400KbpsMethod Neo400KbpsMethod;
|
||||
|
||||
typedef NeoEsp32Rmt6Ws2812xInvertedMethod NeoWs2813InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Ws2812xInvertedMethod NeoWs2812xInvertedMethod;
|
||||
typedef NeoEsp32Rmt6Ws2812xInvertedMethod NeoWs2811InvertedMethod;
|
||||
typedef NeoEsp32Rmt6800KbpsInvertedMethod NeoWs2812InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Ws2812xInvertedMethod NeoWs2816InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Sk6812InvertedMethod NeoSk6812InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Tm1814InvertedMethod NeoTm1814InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Tm1829InvertedMethod NeoTm1829InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Tm1914InvertedMethod NeoTm1914InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Sk6812InvertedMethod NeoLc8812InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Apa106InvertedMethod NeoApa106InvertedMethod;
|
||||
typedef NeoEsp32Rmt6Tx1812InvertedMethod NeoTx1812InvertedMethod;
|
||||
|
||||
typedef NeoEsp32Rmt6Ws2812xInvertedMethod Neo800KbpsInvertedMethod;
|
||||
typedef NeoEsp32Rmt6400KbpsInvertedMethod Neo400KbpsInvertedMethod;
|
||||
|
||||
#endif // defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
#endif // defined(NEOPIXEL_ESP32_RMT_DEFAULT) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,435 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp8266.
|
||||
|
||||
|
||||
Written by Michael C. Miller.
|
||||
Thanks to g3gg0.de for porting the initial DMA support which lead to this.
|
||||
Thanks to github/cnlohr for the original work on DMA support, which opend
|
||||
all our minds to a better way (located at https://github.com/cnlohr/esp8266ws2812i2s).
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include "NeoEsp8266I2sMethodCore.h"
|
||||
|
||||
class NeoEsp8266DmaSpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint8_t IdleLevel = 0;
|
||||
static uint16_t Convert(uint8_t value)
|
||||
{
|
||||
const uint16_t bitpatterns[16] =
|
||||
{
|
||||
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
|
||||
0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
|
||||
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110,
|
||||
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
|
||||
};
|
||||
|
||||
return bitpatterns[value];
|
||||
}
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint8_t IdleLevel = 1;
|
||||
static uint16_t Convert(uint8_t value)
|
||||
{
|
||||
const uint16_t bitpatterns[16] =
|
||||
{
|
||||
0b0111011101110111, 0b0111011101110001, 0b0111011100010111, 0b0111011100010001,
|
||||
0b0111000101110111, 0b0111000101110001, 0b0111000100010111, 0b0111000100010001,
|
||||
0b0001011101110111, 0b0001011101110001, 0b0001011100010111, 0b0001011100010001,
|
||||
0b0001000101110111, 0b0001000101110001, 0b0001000100010111, 0b0001000100010001,
|
||||
};
|
||||
|
||||
return bitpatterns[value];
|
||||
}
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeed800KbpsBase : public NeoEsp8266DmaSpeedBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 5; // 0-63
|
||||
const static uint32_t I2sBaseClockDivisor = 10; // 0-63
|
||||
const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeedWs2812x : public NeoEsp8266DmaSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeedSk6812 : public NeoEsp8266DmaSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeedTm1814 : public NeoEsp8266DmaSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeedTm1829 : public NeoEsp8266DmaSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeed800Kbps : public NeoEsp8266DmaSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeed400Kbps : public NeoEsp8266DmaSpeedBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 10; // 0-63
|
||||
const static uint32_t I2sBaseClockDivisor = 10; // 0-63
|
||||
const static uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed
|
||||
const static uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeedApa106 : public NeoEsp8266DmaSpeedBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 4; // 0-63
|
||||
const static uint32_t I2sBaseClockDivisor = 17; // 0-63
|
||||
const static uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element
|
||||
const static uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeedIntertek : public NeoEsp8266DmaSpeedBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 5; // 0-63
|
||||
const static uint32_t I2sBaseClockDivisor = 10; // 0-63
|
||||
const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element
|
||||
const static uint32_t ResetTimeUs = 12470;
|
||||
const static uint32_t InterPixelTimeUs = 20;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeed800KbpsBase : public NeoEsp8266DmaInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 5; // 0-63
|
||||
const static uint32_t I2sBaseClockDivisor = 10; // 0-63
|
||||
const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeedWs2812x : public NeoEsp8266DmaInvertedSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 300;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeedSk6812 : public NeoEsp8266DmaInvertedSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 80;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeedTm1814 : public NeoEsp8266DmaInvertedSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeedTm1829 : public NeoEsp8266DmaInvertedSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 200;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeed800Kbps : public NeoEsp8266DmaInvertedSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeed400Kbps : public NeoEsp8266DmaInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 10; // 0-63
|
||||
const static uint32_t I2sBaseClockDivisor = 10; // 0-63
|
||||
const static uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed
|
||||
const static uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeedApa106 : public NeoEsp8266DmaInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 4; // 0-63
|
||||
const static uint32_t I2sBaseClockDivisor = 17; // 0-63
|
||||
const static uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element
|
||||
const static uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaInvertedSpeedIntertek : public NeoEsp8266DmaInvertedSpeedBase
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 5; // 0-63
|
||||
const static uint32_t I2sBaseClockDivisor = 10; // 0-63
|
||||
const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
|
||||
const static uint32_t ResetTimeUs = 12470;
|
||||
const static uint32_t InterPixelTimeUs = 20;
|
||||
};
|
||||
|
||||
template<typename T_SPEED> class NeoEsp8266DmaEncode : public T_SPEED
|
||||
{
|
||||
public:
|
||||
static size_t SpacingPixelSize(size_t sizePixel)
|
||||
{
|
||||
return sizePixel;
|
||||
}
|
||||
|
||||
static void FillBuffers(uint8_t* i2sBuffer,
|
||||
const uint8_t* data,
|
||||
size_t sizeData,
|
||||
[[maybe_unused]] size_t sizePixel)
|
||||
{
|
||||
uint16_t* pDma = (uint16_t*)i2sBuffer;
|
||||
const uint8_t* pEnd = data + sizeData;
|
||||
for (const uint8_t* pData = data; pData < pEnd; pData++)
|
||||
{
|
||||
*(pDma++) = T_SPEED::Convert(((*pData) & 0x0f));
|
||||
*(pDma++) = T_SPEED::Convert(((*pData) >> 4) & 0x0f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T_SPEED> class NeoEsp8266DmaPixelSpacingEncode : public T_SPEED
|
||||
{
|
||||
public:
|
||||
static size_t SpacingPixelSize(size_t sizePixel)
|
||||
{
|
||||
return sizePixel + T_SPEED::InterPixelTimeUs / T_SPEED::ByteSendTimeUs;
|
||||
}
|
||||
|
||||
static void FillBuffers(uint8_t* i2sBuffer,
|
||||
const uint8_t* data,
|
||||
size_t sizeData,
|
||||
size_t sizePixel)
|
||||
{
|
||||
uint16_t* pDma = (uint16_t*)i2sBuffer;
|
||||
const uint8_t* pEnd = data + sizeData;
|
||||
uint8_t element = 0;
|
||||
for (const uint8_t* pData = data; pData < pEnd; pData++)
|
||||
{
|
||||
*(pDma++) = T_SPEED::Convert(((*pData) & 0x0f));
|
||||
*(pDma++) = T_SPEED::Convert(((*pData) >> 4) & 0x0f);
|
||||
|
||||
element++;
|
||||
if (element == sizePixel)
|
||||
{
|
||||
element = 0;
|
||||
|
||||
for (uint8_t padding = 0;
|
||||
padding < (T_SPEED::InterPixelTimeUs / T_SPEED::ByteSendTimeUs);
|
||||
padding++)
|
||||
{
|
||||
*(pDma++) = T_SPEED::IdleLevel * 0xffff;
|
||||
*(pDma++) = T_SPEED::IdleLevel * 0xffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T_ENCODER> class NeoEsp8266DmaMethodBase : NeoEsp8266I2sMethodCore
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizePixel(elementSize),
|
||||
_sizeData(pixelCount * elementSize + settingsSize)
|
||||
{
|
||||
size_t dmaPixelSize = DmaBytesPerPixelBytes * T_ENCODER::SpacingPixelSize(_sizePixel);
|
||||
size_t dmaSettingsSize = DmaBytesPerPixelBytes * settingsSize;
|
||||
|
||||
size_t i2sBufferSize = pixelCount * dmaPixelSize + dmaSettingsSize;
|
||||
// size is rounded up to nearest c_I2sByteBoundarySize
|
||||
i2sBufferSize = NeoUtil::RoundUp(i2sBufferSize, c_I2sByteBoundarySize);
|
||||
|
||||
// calculate a buffer size that takes reset amount of time
|
||||
size_t i2sResetSize = T_ENCODER::ResetTimeUs * DmaBytesPerPixelBytes / T_ENCODER::ByteSendTimeUs;
|
||||
// size is rounded up to nearest c_I2sByteBoundarySize
|
||||
i2sResetSize = NeoUtil::RoundUp(i2sResetSize, c_I2sByteBoundarySize);
|
||||
size_t is2BufMaxBlockSize = (c_maxDmaBlockSize / dmaPixelSize) * dmaPixelSize;
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
|
||||
AllocateI2s(i2sBufferSize, i2sResetSize, is2BufMaxBlockSize, T_ENCODER::IdleLevel);
|
||||
}
|
||||
|
||||
NeoEsp8266DmaMethodBase([[maybe_unused]] uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
NeoEsp8266DmaMethodBase(pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
|
||||
~NeoEsp8266DmaMethodBase()
|
||||
{
|
||||
uint8_t waits = 1;
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
waits = 2;
|
||||
yield();
|
||||
}
|
||||
|
||||
// wait for any pending sends to complete
|
||||
// due to internal i2s caching/send delays, this can more that once the data size
|
||||
uint32_t time = micros();
|
||||
while ((micros() - time) < ((getPixelTime() + T_ENCODER::ResetTimeUs) * waits))
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
FreeI2s();
|
||||
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return IsIdle();
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
InitializeI2s(T_ENCODER::I2sClockDivisor, T_ENCODER::I2sBaseClockDivisor);
|
||||
}
|
||||
|
||||
void IRAM_ATTR Update(bool)
|
||||
{
|
||||
// wait for not actively sending data
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
T_ENCODER::FillBuffers(_i2sBuffer, _data, _sizeData, _sizePixel);
|
||||
|
||||
WriteI2s();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
// due to encoding required for i2s, we need 4 bytes to encode the pulses
|
||||
static const uint16_t DmaBytesPerPixelBytes = 4;
|
||||
|
||||
const size_t _sizePixel; // size of a pixel in _data
|
||||
const size_t _sizeData; // Size of '_data' buffer
|
||||
uint8_t* _data; // Holds LED color values
|
||||
|
||||
uint32_t getPixelTime() const
|
||||
{
|
||||
return (T_ENCODER::ByteSendTimeUs * GetSendSize() / DmaBytesPerPixelBytes);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// normal
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedWs2812x>> NeoEsp8266DmaWs2812xMethod;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedSk6812>> NeoEsp8266DmaSk6812Method;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedTm1814>> NeoEsp8266DmaTm1814Method;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedTm1829>> NeoEsp8266DmaTm1829Method;
|
||||
typedef NeoEsp8266DmaTm1814Method NeoEsp8266DmaTm1914Method;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeed800Kbps>> NeoEsp8266Dma800KbpsMethod;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeed400Kbps>> NeoEsp8266Dma400KbpsMethod;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedApa106>> NeoEsp8266DmaApa106Method;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaPixelSpacingEncode<NeoEsp8266DmaSpeedIntertek>> NeoEsp8266DmaIntertekMethod;
|
||||
|
||||
|
||||
// inverted
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedWs2812x>> NeoEsp8266DmaInvertedWs2812xMethod;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedSk6812>> NeoEsp8266DmaInvertedSk6812Method;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedTm1814>> NeoEsp8266DmaInvertedTm1814Method;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedTm1829>> NeoEsp8266DmaInvertedTm1829Method;
|
||||
typedef NeoEsp8266DmaInvertedTm1814Method NeoEsp8266DmaInvertedTm1914Method;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeed800Kbps>> NeoEsp8266DmaInverted800KbpsMethod;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeed400Kbps>> NeoEsp8266DmaInverted400KbpsMethod;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedApa106>> NeoEsp8266DmaInvertedApa106Method;
|
||||
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaPixelSpacingEncode<NeoEsp8266DmaInvertedSpeedIntertek>> NeoEsp8266DmaInvertedIntertekMethod;
|
||||
|
||||
// Dma method is the default method for Esp8266
|
||||
typedef NeoEsp8266DmaWs2812xMethod NeoWs2813Method;
|
||||
typedef NeoEsp8266DmaWs2812xMethod NeoWs2812xMethod;
|
||||
typedef NeoEsp8266Dma800KbpsMethod NeoWs2812Method;
|
||||
typedef NeoEsp8266DmaWs2812xMethod NeoWs2811Method;
|
||||
typedef NeoEsp8266DmaWs2812xMethod NeoWs2816Method;
|
||||
typedef NeoEsp8266DmaSk6812Method NeoSk6812Method;
|
||||
typedef NeoEsp8266DmaTm1814Method NeoTm1814Method;
|
||||
typedef NeoEsp8266DmaTm1829Method NeoTm1829Method;
|
||||
typedef NeoEsp8266DmaTm1914Method NeoTm1914Method;
|
||||
typedef NeoEsp8266DmaSk6812Method NeoLc8812Method;
|
||||
typedef NeoEsp8266DmaApa106Method NeoApa106Method;
|
||||
typedef NeoEsp8266DmaIntertekMethod NeoIntertekMethod;
|
||||
|
||||
typedef NeoEsp8266DmaWs2812xMethod Neo800KbpsMethod;
|
||||
typedef NeoEsp8266Dma400KbpsMethod Neo400KbpsMethod;
|
||||
|
||||
// inverted
|
||||
typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2813InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2812xInvertedMethod;
|
||||
typedef NeoEsp8266DmaInverted800KbpsMethod NeoWs2812InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2811InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2816InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedSk6812Method NeoSk6812InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedTm1814Method NeoTm1814InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedTm1829Method NeoTm1829InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedTm1914Method NeoTm1914InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedSk6812Method NeoLc8812InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedApa106Method NeoApa106InvertedMethod;
|
||||
typedef NeoEsp8266DmaInvertedIntertekMethod NeoInvertedIntertekMethod;
|
||||
|
||||
typedef NeoEsp8266DmaInvertedWs2812xMethod Neo800KbpsInvertedMethod;
|
||||
typedef NeoEsp8266DmaInverted400KbpsMethod Neo400KbpsInvertedMethod;
|
||||
#endif
|
||||
@@ -0,0 +1,352 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp8266.
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include "NeoEsp8266I2sMethodCore.h"
|
||||
|
||||
|
||||
class NeoEsp8266I2sDmx512SpeedBase
|
||||
{
|
||||
public:
|
||||
// 4 us bit send, 250Kbps
|
||||
static const uint32_t I2sClockDivisor = 20; // 0-63
|
||||
static const uint32_t I2sBaseClockDivisor = 32; // 0-63
|
||||
static const uint32_t ByteSendTimeUs = 44; // us it takes to send a single pixel element of 11 bits
|
||||
static const uint32_t BreakMabUs = 96; // Break min 92, Mab min 12
|
||||
static const size_t BreakMabSize = 4; // roundupby((BreakMabUs/4us)/8,4) count of bytes needed for the Break+Mab timing
|
||||
static const uint32_t MtbpUs = 11; // Mtbp, min 0, buy we use at least one byte of space (8*1.35)
|
||||
static const size_t MtbpSize = 1; // (MtbpUs/1.35)/8 count of bytes needed for the Mtbp timing
|
||||
// DMX requires the first slot to be zero
|
||||
static const size_t HeaderSize = 1;
|
||||
};
|
||||
|
||||
class NeoEsp8266I2sDmx512Speed : public NeoEsp8266I2sDmx512SpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint8_t MtbpLevel = 0x1; // high
|
||||
static const uint8_t StartBit = 0b00000000;
|
||||
static const uint8_t StopBits = 0b00000011;
|
||||
static const uint32_t Break = 0x00000000; // Break
|
||||
static const uint32_t BreakMab = 0x00000007; // Break + Mab
|
||||
|
||||
static uint8_t Convert(uint8_t value)
|
||||
{
|
||||
// DMX requires LSB order
|
||||
return NeoUtil::Reverse8Bits( value );
|
||||
}
|
||||
};
|
||||
|
||||
class NeoEsp8266I2sDmx512InvertedSpeed : public NeoEsp8266I2sDmx512SpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint8_t MtbpLevel = 0x00; // low
|
||||
static const uint8_t StartBit = 0b00000001;
|
||||
static const uint8_t StopBits = 0b00000000;
|
||||
static const uint32_t Break = 0xffffffff; // Break
|
||||
static const uint32_t BreakMab = 0xfffffff8; // Break + Mab
|
||||
|
||||
static uint8_t Convert(uint8_t value)
|
||||
{
|
||||
// DMX requires LSB order
|
||||
return NeoUtil::Reverse8Bits( ~value );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class NeoEsp8266I2sWs2821SpeedBase
|
||||
{
|
||||
public:
|
||||
// 1.35 us bit send, 750Kbps
|
||||
static const uint32_t I2sClockDivisor = 27; // 0-63
|
||||
static const uint32_t I2sBaseClockDivisor = 8; // 0-63
|
||||
static const uint32_t ByteSendTimeUs = 15; // us it takes to send a single pixel element of 11 bits
|
||||
static const uint32_t BreakMabUs = 92; // Break min 88, Mab min 4
|
||||
static const size_t BreakMabSize = 12; // roundupby((BreakMabUs/1.35)/8,4) count of bytes needed for the Break+Mab timing
|
||||
static const uint32_t MtbpUs = 88; // Mtbp, min 88
|
||||
static const size_t MtbpSize = 9; // (MtbpUs/1.35)/8 count of bytes needed for the Mtbp timing
|
||||
|
||||
// DMX/WS2821 requires the first slot to be zero
|
||||
static const size_t HeaderSize = 1;
|
||||
};
|
||||
|
||||
class NeoEsp8266I2sWs2821Speed : public NeoEsp8266I2sWs2821SpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint8_t MtbpLevel = 0x1; // high
|
||||
static const uint8_t StartBit = 0b00000000;
|
||||
static const uint8_t StopBits = 0b00000011;
|
||||
static const uint32_t Break = 0x00000000; // Break
|
||||
static const uint32_t BreakMab = 0x00000007; // Break + Mab (4~12us/1.35us)
|
||||
|
||||
static uint8_t Convert(uint8_t value)
|
||||
{
|
||||
// DMX requires LSB order
|
||||
return NeoUtil::Reverse8Bits(value);
|
||||
}
|
||||
};
|
||||
|
||||
class NeoEsp8266I2sWs2821InvertedSpeed : public NeoEsp8266I2sWs2821SpeedBase
|
||||
{
|
||||
public:
|
||||
static const uint8_t MtbpLevel = 0x00; // low
|
||||
static const uint8_t StartBit = 0b00000001;
|
||||
static const uint8_t StopBits = 0b00000000;
|
||||
static const uint32_t Break = 0xffffffff; // Break
|
||||
static const uint32_t BreakMab = 0xfffffff8; // Break + Mab
|
||||
|
||||
static uint8_t Convert(uint8_t value)
|
||||
{
|
||||
// DMX requires LSB order
|
||||
return NeoUtil::Reverse8Bits(~value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T_SPEED> class NeoEsp8266I2sDmx512MethodBase : NeoEsp8266I2sMethodCore
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoEsp8266I2sDmx512MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize + T_SPEED::HeaderSize)
|
||||
{
|
||||
size_t dmaPixelBits = I2sBitsPerPixelBytes * elementSize;
|
||||
size_t dmaSettingsBits = I2sBitsPerPixelBytes * (settingsSize + T_SPEED::HeaderSize);
|
||||
|
||||
// bits + half rounding byte of bits / bits per byte
|
||||
size_t i2sBufferSize = (pixelCount * dmaPixelBits + dmaSettingsBits + 4) / 8;
|
||||
|
||||
i2sBufferSize = i2sBufferSize + T_SPEED::BreakMabSize;
|
||||
|
||||
// size is rounded up to nearest c_I2sByteBoundarySize
|
||||
i2sBufferSize = NeoUtil::RoundUp(i2sBufferSize, c_I2sByteBoundarySize);
|
||||
|
||||
// size of a looping silent space rounded up to nearest c_I2sByteBoundarySize
|
||||
size_t i2sResetSize = NeoUtil::RoundUp(T_SPEED::MtbpSize, c_I2sByteBoundarySize);
|
||||
|
||||
// protocol limits use of full block size to c_I2sByteBoundarySize
|
||||
size_t is2BufMaxBlockSize = (c_maxDmaBlockSize / c_I2sByteBoundarySize) * c_I2sByteBoundarySize;
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// first "slot" cleared due to protocol requiring it to be zero
|
||||
memset(_data, 0x00, 1);
|
||||
|
||||
AllocateI2s(i2sBufferSize, i2sResetSize, is2BufMaxBlockSize, T_SPEED::MtbpLevel);
|
||||
}
|
||||
|
||||
NeoEsp8266I2sDmx512MethodBase([[maybe_unused]] uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
NeoEsp8266I2sDmx512MethodBase(pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
|
||||
~NeoEsp8266I2sDmx512MethodBase()
|
||||
{
|
||||
uint8_t waits = 1;
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
waits = 2;
|
||||
yield();
|
||||
}
|
||||
|
||||
// wait for any pending sends to complete
|
||||
// due to internal i2s caching/send delays, this can more that once the data size
|
||||
uint32_t time = micros();
|
||||
while ((micros() - time) < ((getPixelTime() + T_SPEED::MtbpUs) * waits))
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
FreeI2s();
|
||||
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return IsIdle();
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
InitializeI2s(T_SPEED::I2sClockDivisor, T_SPEED::I2sBaseClockDivisor);
|
||||
}
|
||||
|
||||
void IRAM_ATTR Update(bool)
|
||||
{
|
||||
// wait for not actively sending data
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
FillBuffers();
|
||||
|
||||
WriteI2s();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data + T_SPEED::HeaderSize;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData - T_SPEED::HeaderSize;
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
// given 11 sending bits per pixel byte,
|
||||
static const uint16_t I2sBitsPerPixelBytes = 11;
|
||||
|
||||
const size_t _sizeData; // Size of '_data' buffer
|
||||
uint8_t* _data; // Holds LED color values
|
||||
|
||||
// encodes the data with start and stop bits
|
||||
// input buffer is bytes
|
||||
// output stream is uint31_t
|
||||
static void Encoder(const uint8_t* pSrc, const uint8_t* pSrcEnd,
|
||||
uint32_t* pOutput, const uint32_t* pOutputEnd)
|
||||
{
|
||||
static const uint32_t Mtbp = 0xffffffff * T_SPEED::MtbpLevel;
|
||||
const uint8_t* pData = pSrc;
|
||||
|
||||
int8_t outputBit = 32;
|
||||
uint32_t output = 0;
|
||||
|
||||
// DATA stream, one start, two stop
|
||||
while (pData < pSrcEnd)
|
||||
{
|
||||
uint8_t data = T_SPEED::Convert( *(pData++) );
|
||||
|
||||
if (outputBit > 10)
|
||||
{
|
||||
// simple
|
||||
outputBit -= 1;
|
||||
output |= T_SPEED::StartBit << outputBit;
|
||||
|
||||
outputBit -= 8;
|
||||
output |= data << outputBit;
|
||||
|
||||
outputBit -= 2;
|
||||
output |= T_SPEED::StopBits << outputBit;
|
||||
}
|
||||
else
|
||||
{
|
||||
// split across an output uint32_t
|
||||
// handle start bit
|
||||
if (outputBit < 1)
|
||||
{
|
||||
*(pOutput++) = output;
|
||||
output = 0;
|
||||
outputBit += 32;
|
||||
}
|
||||
outputBit -= 1;
|
||||
output |= (T_SPEED::StartBit << outputBit);
|
||||
|
||||
// handle data bits
|
||||
if (outputBit < 8)
|
||||
{
|
||||
output |= data >> (8 - outputBit);
|
||||
|
||||
*(pOutput++) = output;
|
||||
output = 0;
|
||||
outputBit += 32;
|
||||
}
|
||||
outputBit -= 8;
|
||||
output |= data << outputBit;
|
||||
|
||||
// handle stop bits
|
||||
if (outputBit < 2)
|
||||
{
|
||||
output |= T_SPEED::StopBits >> (2 - outputBit);
|
||||
|
||||
*(pOutput++) = output;
|
||||
output = 0;
|
||||
outputBit += 32;
|
||||
}
|
||||
outputBit -= 2;
|
||||
output |= T_SPEED::StopBits << outputBit;
|
||||
}
|
||||
}
|
||||
if (outputBit > 0)
|
||||
{
|
||||
// padd last output uint32_t with Mtbp
|
||||
output |= Mtbp >> (32 - outputBit);
|
||||
*(pOutput++) = output;
|
||||
}
|
||||
// fill the rest of the output with Mtbp
|
||||
while (pOutput < pOutputEnd)
|
||||
{
|
||||
*(pOutput++) = Mtbp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FillBuffers()
|
||||
{
|
||||
uint32_t* pDma32 = reinterpret_cast<uint32_t*>(_i2sBuffer);
|
||||
const uint32_t* pDma32End = reinterpret_cast<uint32_t*>(_i2sBuffer + _i2sBufferSize);
|
||||
|
||||
// first insert Break space as needed
|
||||
for (size_t count = 1;
|
||||
count < (T_SPEED::BreakMabSize/sizeof(T_SPEED::Break));
|
||||
count++)
|
||||
{
|
||||
*(pDma32++) = T_SPEED::Break;
|
||||
}
|
||||
// then tail of break with mab
|
||||
*(pDma32++) = T_SPEED::BreakMab;
|
||||
|
||||
Encoder(_data, _data + _sizeData, pDma32, pDma32End);
|
||||
}
|
||||
|
||||
uint32_t getPixelTime() const
|
||||
{
|
||||
return (T_SPEED::ByteSendTimeUs * this->_sizeData);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
// normal
|
||||
typedef NeoEsp8266I2sDmx512MethodBase<NeoEsp8266I2sDmx512Speed> NeoEsp8266Dmx512Method;
|
||||
typedef NeoEsp8266I2sDmx512MethodBase<NeoEsp8266I2sWs2821Speed> NeoEsp8266Ws2821Method;
|
||||
|
||||
// inverted
|
||||
typedef NeoEsp8266I2sDmx512MethodBase<NeoEsp8266I2sDmx512InvertedSpeed> NeoEsp8266Dmx512InvertedMethod;
|
||||
typedef NeoEsp8266I2sDmx512MethodBase<NeoEsp8266I2sWs2821InvertedSpeed> NeoEsp8266Ws2821InvertedMethod;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp8266.
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "../NeoUtil.h"
|
||||
#include "NeoEsp8266I2sMethodCore.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
|
||||
NeoEsp8266I2sMethodCore* NeoEsp8266I2sMethodCore::s_this;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,362 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp8266.
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "osapi.h"
|
||||
#include "ets_sys.h"
|
||||
|
||||
#include "i2s_reg.h"
|
||||
|
||||
#ifdef ARDUINO_ESP8266_MAJOR //this define was added in ESP8266 Arduino Core version v3.0.1
|
||||
#include "core_esp8266_i2s.h" //for Arduino core >= 3.0.1
|
||||
#else
|
||||
#include "i2s.h" //for Arduino core <= 3.0.0
|
||||
#endif
|
||||
|
||||
#include "eagle_soc.h"
|
||||
#include "esp8266_peri.h"
|
||||
#include "slc_register.h"
|
||||
|
||||
#include "osapi.h"
|
||||
#include "ets_sys.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
#if !defined(__CORE_ESP8266_VERSION_H) || defined(ARDUINO_ESP8266_RELEASE_2_5_0)
|
||||
void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id, uint32_t reg_add, uint32_t Msb, uint32_t Lsb, uint32_t indata);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct slc_queue_item
|
||||
{
|
||||
uint32 blocksize : 12;
|
||||
uint32 datalen : 12;
|
||||
uint32 unused : 5;
|
||||
uint32 sub_sof : 1;
|
||||
uint32 eof : 1;
|
||||
uint32 owner : 1;
|
||||
uint8* buf_ptr;
|
||||
struct slc_queue_item* next_link_ptr;
|
||||
};
|
||||
|
||||
enum NeoDmaState
|
||||
{
|
||||
NeoDmaState_Idle,
|
||||
NeoDmaState_Pending,
|
||||
NeoDmaState_Sending
|
||||
};
|
||||
|
||||
const uint16_t c_maxDmaBlockSize = 4095;
|
||||
|
||||
const uint8_t c_I2sPin = 3; // due to I2S hardware, the pin used is restricted to this
|
||||
|
||||
class NeoEsp8266I2sMethodCore
|
||||
{
|
||||
private:
|
||||
static const uint8_t c_StateBlockCount = 2;
|
||||
static const size_t c_StateDataSize = 4; // mulitples of c_I2sByteBoundarySize
|
||||
|
||||
// i2s sends 4 byte elements,
|
||||
static const uint16_t c_I2sByteBoundarySize = 4;
|
||||
|
||||
protected:
|
||||
static NeoEsp8266I2sMethodCore* s_this; // for the ISR
|
||||
|
||||
volatile NeoDmaState _dmaState;
|
||||
|
||||
slc_queue_item* _i2sBufDesc; // dma block descriptors
|
||||
uint16_t _i2sBufDescCount; // count of block descriptors in _i2sBufDesc
|
||||
|
||||
size_t _i2sBufferSize; // total size of _i2sBuffer
|
||||
uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc
|
||||
|
||||
size_t _i2sIdleDataTotalSize; // total size of represented zeroes, mulitple uses of _i2sIdleData
|
||||
size_t _i2sIdleDataSize; // size of _i2sIdleData
|
||||
uint8_t* _i2sIdleData;
|
||||
|
||||
uint16_t _is2BufMaxBlockSize; // max size based on size of a pixel of a single block
|
||||
|
||||
size_t GetSendSize() const
|
||||
{
|
||||
return _i2sBufferSize + _i2sIdleDataTotalSize;
|
||||
}
|
||||
|
||||
// This routine is called as soon as the DMA routine has something to tell us. All we
|
||||
// handle here is the RX_EOF_INT status, which indicate the DMA has sent a buffer whose
|
||||
// descriptor has the 'EOF' field set to 1.
|
||||
// in the case of this code, the second to last state descriptor
|
||||
static void IRAM_ATTR i2s_slc_isr(void)
|
||||
{
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
|
||||
uint32_t slc_intr_status = SLCIS;
|
||||
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
|
||||
if ((slc_intr_status & SLCIRXEOF) && s_this)
|
||||
{
|
||||
if (s_this->_dmaState != NeoDmaState_Idle)
|
||||
{
|
||||
// first two items are the state blocks
|
||||
slc_queue_item* itemLoop = s_this->_i2sBufDesc;
|
||||
slc_queue_item* itemLoopBreaker = itemLoop + 1;
|
||||
// set to loop on idle items
|
||||
itemLoopBreaker->next_link_ptr = itemLoop;
|
||||
|
||||
s_this->_dmaState = NeoDmaState_Idle;
|
||||
}
|
||||
}
|
||||
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
}
|
||||
|
||||
NeoEsp8266I2sMethodCore()
|
||||
{ };
|
||||
|
||||
void AllocateI2s(const size_t i2sBufferSize, // expected multiples of c_I2sByteBoundarySize
|
||||
const size_t i2sZeroesSize, // expected multiples of c_I2sByteBoundarySize
|
||||
const size_t is2BufMaxBlockSize,
|
||||
const uint8_t idleLevel)
|
||||
{
|
||||
_i2sBufferSize = i2sBufferSize;
|
||||
_i2sIdleDataTotalSize = i2sZeroesSize;
|
||||
_i2sIdleDataSize = _i2sIdleDataTotalSize;
|
||||
|
||||
size_t countIdleQueueItems = 1;
|
||||
if (_i2sIdleDataSize > 256)
|
||||
{
|
||||
// reuse a single idle data buffer of 256 with multiple dma slc_queue_items
|
||||
countIdleQueueItems = _i2sIdleDataSize / 256 + 1;
|
||||
_i2sIdleDataSize = 256;
|
||||
}
|
||||
else
|
||||
{
|
||||
_i2sIdleDataSize = NeoUtil::RoundUp(_i2sIdleDataSize, c_I2sByteBoundarySize);
|
||||
}
|
||||
_is2BufMaxBlockSize = is2BufMaxBlockSize;
|
||||
|
||||
_i2sBuffer = static_cast<uint8_t*>(malloc(_i2sBufferSize));
|
||||
// no need to initialize it, it gets overwritten on every send
|
||||
_i2sIdleData = static_cast<uint8_t*>(malloc(_i2sIdleDataSize));
|
||||
memset(_i2sIdleData, idleLevel * 0xff, _i2sIdleDataSize);
|
||||
|
||||
_i2sBufDescCount = (_i2sBufferSize / _is2BufMaxBlockSize) + 1 +
|
||||
countIdleQueueItems +
|
||||
c_StateBlockCount; // need more for state/latch blocks
|
||||
|
||||
_i2sBufDesc = (slc_queue_item*)malloc(_i2sBufDescCount * sizeof(slc_queue_item));
|
||||
|
||||
s_this = this; // store this for the ISR
|
||||
}
|
||||
|
||||
void FreeI2s()
|
||||
{
|
||||
StopI2s();
|
||||
|
||||
s_this = nullptr;
|
||||
pinMode(c_I2sPin, INPUT);
|
||||
|
||||
free(_i2sBuffer);
|
||||
free(_i2sBufDesc);
|
||||
free(_i2sIdleData);
|
||||
}
|
||||
|
||||
bool IsIdle() const
|
||||
{
|
||||
return (_dmaState == NeoDmaState_Idle);
|
||||
}
|
||||
|
||||
|
||||
void DmaItemInit(slc_queue_item* item, uint8_t* data, size_t sizeData, slc_queue_item* itemNext)
|
||||
{
|
||||
item->owner = 1;
|
||||
item->eof = 0; // no need to trigger interrupt generally
|
||||
item->sub_sof = 0;
|
||||
item->datalen = sizeData;
|
||||
item->blocksize = sizeData;
|
||||
item->buf_ptr = data;
|
||||
item->unused = 0;
|
||||
item->next_link_ptr = itemNext;
|
||||
}
|
||||
|
||||
void InitializeI2s(const uint32_t i2sClockDivisor, const uint32_t i2sBaseClockDivisor)
|
||||
{
|
||||
StopI2s();
|
||||
|
||||
pinMode(c_I2sPin, FUNCTION_1); // I2S0_DATA
|
||||
|
||||
uint8_t* is2Buffer = _i2sBuffer;
|
||||
uint8_t* is2BufferEnd = _i2sBuffer + _i2sBufferSize;
|
||||
uint32_t is2BufferSize;
|
||||
uint16_t indexDesc = 0;
|
||||
|
||||
// prepare the two state/latch descriptors
|
||||
uint16_t stateDataSize = min(c_StateDataSize, _i2sIdleDataSize);
|
||||
while (indexDesc < c_StateBlockCount)
|
||||
{
|
||||
DmaItemInit(&_i2sBufDesc[indexDesc], _i2sIdleData, stateDataSize, &(_i2sBufDesc[indexDesc + 1]));
|
||||
|
||||
indexDesc++;
|
||||
}
|
||||
|
||||
// prepare main data block decriptors that point into our one static dma buffer
|
||||
is2BufferSize = _i2sBufferSize;
|
||||
while (is2Buffer < is2BufferEnd)
|
||||
{
|
||||
uint32_t blockSize = (is2BufferSize > _is2BufMaxBlockSize) ? _is2BufMaxBlockSize : is2BufferSize;
|
||||
|
||||
DmaItemInit(&_i2sBufDesc[indexDesc], is2Buffer, blockSize, &(_i2sBufDesc[indexDesc + 1]));
|
||||
|
||||
is2Buffer += blockSize;
|
||||
is2BufferSize -= blockSize;
|
||||
indexDesc++;
|
||||
}
|
||||
|
||||
// last data item triggers EOF ISR
|
||||
_i2sBufDesc[indexDesc - 1].eof = 1;
|
||||
|
||||
// prepare idle block decriptors that point into our one idle dma buffer
|
||||
is2BufferSize = _i2sIdleDataTotalSize;
|
||||
while (indexDesc < _i2sBufDescCount)
|
||||
{
|
||||
uint32_t blockSize = (is2BufferSize > _i2sIdleDataSize) ? _i2sIdleDataSize : is2BufferSize;
|
||||
|
||||
DmaItemInit(&_i2sBufDesc[indexDesc], _i2sIdleData, blockSize, &(_i2sBufDesc[indexDesc + 1]));
|
||||
|
||||
is2Buffer += blockSize;
|
||||
is2BufferSize -= blockSize;
|
||||
indexDesc++;
|
||||
}
|
||||
|
||||
// the last item will loop to the first item
|
||||
_i2sBufDesc[indexDesc - 1].next_link_ptr = reinterpret_cast<struct slc_queue_item*>(&(_i2sBufDesc[0]));
|
||||
|
||||
// the last state block will loop to the first state block by defualt
|
||||
_i2sBufDesc[c_StateBlockCount - 1].next_link_ptr = reinterpret_cast<struct slc_queue_item*>(&(_i2sBufDesc[0]));
|
||||
|
||||
// setup the rest of i2s DMA
|
||||
//
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
|
||||
// start off in idel state as that is what it will be all setup to be
|
||||
// for the interrupt
|
||||
_dmaState = NeoDmaState_Idle;
|
||||
|
||||
SLCC0 |= SLCRXLR | SLCTXLR;
|
||||
SLCC0 &= ~(SLCRXLR | SLCTXLR);
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
|
||||
// Configure DMA
|
||||
SLCC0 &= ~(SLCMM << SLCM); // clear DMA MODE
|
||||
SLCC0 |= (1 << SLCM); // set DMA MODE to 1
|
||||
SLCRXDC |= SLCBINR | SLCBTNR; // enable INFOR_NO_REPLACE and TOKEN_NO_REPLACE
|
||||
SLCRXDC &= ~(SLCBRXFE | SLCBRXEM | SLCBRXFM); // disable RX_FILL, RX_EOF_MODE and RX_FILL_MODE
|
||||
|
||||
// Feed DMA the 1st buffer desc addr
|
||||
// To send data to the I2S subsystem, counter-intuitively we use the RXLINK part, not the TXLINK as you might
|
||||
// expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw
|
||||
// an error at us otherwise. Just feed it any random descriptor.
|
||||
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
|
||||
// set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid
|
||||
SLCTXL |= (uint32) & (_i2sBufDesc[_i2sBufDescCount - 1]) << SLCTXLA;
|
||||
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
|
||||
// set RX descriptor address. use first of the data addresses
|
||||
SLCRXL |= (uint32) & (_i2sBufDesc[0]) << SLCRXLA;
|
||||
|
||||
ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL);
|
||||
SLCIE = SLCIRXEOF; // Enable only for RX EOF interrupt
|
||||
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
|
||||
//Start transmission
|
||||
SLCTXL |= SLCTXLS;
|
||||
SLCRXL |= SLCRXLS;
|
||||
|
||||
I2S_CLK_ENABLE();
|
||||
I2SIC = 0x3F;
|
||||
I2SIE = 0;
|
||||
|
||||
//Reset I2S
|
||||
I2SC &= ~(I2SRST);
|
||||
I2SC |= I2SRST;
|
||||
I2SC &= ~(I2SRST);
|
||||
|
||||
// Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only)
|
||||
I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM));
|
||||
I2SFC |= I2SDE; //Enable DMA
|
||||
// Set RX/TX CHAN_MOD=0
|
||||
I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM));
|
||||
|
||||
// set the rate
|
||||
uint32_t i2s_clock_div = i2sClockDivisor & I2SCDM;
|
||||
uint8_t i2s_bck_div = i2sBaseClockDivisor & I2SBDM;
|
||||
|
||||
//!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right
|
||||
I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
|
||||
I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | (i2s_bck_div << I2SBD) | (i2s_clock_div << I2SCD);
|
||||
|
||||
I2SC |= I2STXS; // Start transmission
|
||||
}
|
||||
|
||||
void WriteI2s()
|
||||
{
|
||||
// first two items are the state blocks
|
||||
slc_queue_item* itemLoopBreaker = &(_i2sBufDesc[1]);
|
||||
slc_queue_item* itemData = itemLoopBreaker + 1;
|
||||
|
||||
// set to NOT loop on idle items
|
||||
itemLoopBreaker->next_link_ptr = itemData;
|
||||
|
||||
_dmaState = NeoDmaState_Sending;
|
||||
}
|
||||
|
||||
void StopI2s()
|
||||
{
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
|
||||
// Disable any I2S send or receive
|
||||
I2SC &= ~(I2STXS | I2SRXS);
|
||||
|
||||
// Reset I2S
|
||||
I2SC &= ~(I2SRST);
|
||||
I2SC |= I2SRST;
|
||||
I2SC &= ~(I2SRST);
|
||||
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
SLCIE = 0;
|
||||
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
|
||||
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
|
||||
|
||||
pinMode(c_I2sPin, INPUT);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP8266
|
||||
@@ -0,0 +1,176 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp8266 UART hardware
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "../NeoSettings.h"
|
||||
#include "NeoEsp8266UartMethod.h"
|
||||
#include <utility>
|
||||
extern "C"
|
||||
{
|
||||
#include <ets_sys.h>
|
||||
}
|
||||
|
||||
const volatile uint8_t* IRAM_ATTR NeoEsp8266UartContext::FillUartFifo(uint8_t uartNum,
|
||||
const volatile uint8_t* start,
|
||||
const volatile uint8_t* end)
|
||||
{
|
||||
// Remember: UARTs send less significant bit (LSB) first so
|
||||
// pushing ABCDEF byte will generate a 0FEDCBA1 signal,
|
||||
// including a LOW(0) start & a HIGH(1) stop bits.
|
||||
// Also, we have configured UART to invert logic levels, so:
|
||||
const uint8_t _uartData[4] = {
|
||||
0b110111, // On wire: 1 000 100 0 [Neopixel reads 00]
|
||||
0b000111, // On wire: 1 000 111 0 [Neopixel reads 01]
|
||||
0b110100, // On wire: 1 110 100 0 [Neopixel reads 10]
|
||||
0b000100, // On wire: 1 110 111 0 [NeoPixel reads 11]
|
||||
};
|
||||
uint8_t avail = (UART_TX_FIFO_SIZE - GetTxFifoLength(uartNum)) / 4;
|
||||
if (end - start > avail)
|
||||
{
|
||||
end = start + avail;
|
||||
}
|
||||
while (start < end)
|
||||
{
|
||||
uint8_t subpix = *start++;
|
||||
Enqueue(uartNum, _uartData[(subpix >> 6) & 0x3]);
|
||||
Enqueue(uartNum, _uartData[(subpix >> 4) & 0x3]);
|
||||
Enqueue(uartNum, _uartData[(subpix >> 2) & 0x3]);
|
||||
Enqueue(uartNum, _uartData[subpix & 0x3]);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
volatile NeoEsp8266UartInterruptContext* NeoEsp8266UartInterruptContext::s_uartInteruptContext[] = { nullptr, nullptr };
|
||||
|
||||
void NeoEsp8266UartInterruptContext::StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end)
|
||||
{
|
||||
// send the pixels asynchronously
|
||||
_asyncBuff = start;
|
||||
_asyncBuffEnd = end;
|
||||
|
||||
// enable the transmit interrupt
|
||||
USIE(uartNum) |= (1 << UIFE);
|
||||
}
|
||||
|
||||
void NeoEsp8266UartInterruptContext::Attach(uint8_t uartNum)
|
||||
{
|
||||
// Disable all interrupts
|
||||
ETS_UART_INTR_DISABLE();
|
||||
|
||||
// Clear the RX & TX FIFOS
|
||||
const uint32_t fifoResetFlags = (1 << UCTXRST) | (1 << UCRXRST);
|
||||
USC0(uartNum) |= fifoResetFlags;
|
||||
USC0(uartNum) &= ~(fifoResetFlags);
|
||||
|
||||
// attach the ISR if needed
|
||||
if (s_uartInteruptContext[0] == nullptr &&
|
||||
s_uartInteruptContext[1] == nullptr)
|
||||
{
|
||||
ETS_UART_INTR_ATTACH(Isr, s_uartInteruptContext);
|
||||
}
|
||||
|
||||
// attach the context
|
||||
s_uartInteruptContext[uartNum] = this;
|
||||
|
||||
// Set tx fifo trigger. 80 bytes gives us 200 microsecs to refill the FIFO
|
||||
USC1(uartNum) = (80 << UCFET);
|
||||
|
||||
// Disable RX & TX interrupts. It maybe still enabled by uart.c in the SDK
|
||||
USIE(uartNum) &= ~((1 << UIFF) | (1 << UIFE));
|
||||
|
||||
// Clear all pending interrupts in UART1
|
||||
USIC(uartNum) = 0xffff;
|
||||
|
||||
// Reenable interrupts
|
||||
ETS_UART_INTR_ENABLE();
|
||||
}
|
||||
|
||||
void NeoEsp8266UartInterruptContext::Detach(uint8_t uartNum)
|
||||
{
|
||||
// Disable interrupts
|
||||
ETS_UART_INTR_DISABLE();
|
||||
|
||||
if (s_uartInteruptContext[uartNum] != nullptr)
|
||||
{
|
||||
// turn off uart
|
||||
USC1(uartNum) = 0;
|
||||
USIC(uartNum) = 0xffff;
|
||||
USIE(uartNum) = 0;
|
||||
|
||||
s_uartInteruptContext[uartNum] = nullptr;
|
||||
|
||||
if (s_uartInteruptContext[0] == nullptr &&
|
||||
s_uartInteruptContext[1] == nullptr)
|
||||
{
|
||||
// detach our ISR
|
||||
ETS_UART_INTR_ATTACH(NULL, NULL);
|
||||
|
||||
// return so we don't enable interrupts since there is no ISR anymore
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Reenable interrupts
|
||||
ETS_UART_INTR_ENABLE();
|
||||
}
|
||||
|
||||
// The xtos_1int handler calls with param1 as the arg, param2 as a pointer
|
||||
// to an exception frame in memory.
|
||||
void IRAM_ATTR NeoEsp8266UartInterruptContext::Isr(void* param, [[maybe_unused]] void* exceptionFrame)
|
||||
{
|
||||
// make sure this is for us
|
||||
if (param == s_uartInteruptContext)
|
||||
{
|
||||
// Interrupt handler is shared between UART0 & UART1
|
||||
// so we need to test for both
|
||||
for (uint8_t uartNum = 0; uartNum < 2; uartNum++)
|
||||
{
|
||||
if (USIS(uartNum) && s_uartInteruptContext[uartNum] != nullptr)
|
||||
{
|
||||
// Fill the FIFO with new data
|
||||
s_uartInteruptContext[uartNum]->_asyncBuff = FillUartFifo(
|
||||
uartNum,
|
||||
s_uartInteruptContext[uartNum]->_asyncBuff,
|
||||
s_uartInteruptContext[uartNum]->_asyncBuffEnd);
|
||||
|
||||
// Disable TX interrupt when done
|
||||
if (s_uartInteruptContext[uartNum]->_asyncBuff == s_uartInteruptContext[uartNum]->_asyncBuffEnd)
|
||||
{
|
||||
// clear the TX FIFO Empty
|
||||
USIE(uartNum) &= ~(1 << UIFE);
|
||||
}
|
||||
|
||||
// Clear all interrupts flags (just in case)
|
||||
USIC(uartNum) = 0xffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,564 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp8266 UART hardware
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
|
||||
// this template method class is used to track the data being sent on the uart
|
||||
// when using the default serial ISR installed by the core
|
||||
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
|
||||
//
|
||||
class NeoEsp8266UartContext
|
||||
{
|
||||
public:
|
||||
// Gets the number of bytes waiting in the TX FIFO
|
||||
static inline uint8_t IRAM_ATTR GetTxFifoLength(uint8_t uartNum)
|
||||
{
|
||||
return (USS(uartNum) >> USTXC) & 0xff;
|
||||
}
|
||||
// Append a byte to the TX FIFO
|
||||
static inline void IRAM_ATTR Enqueue(uint8_t uartNum, uint8_t value)
|
||||
{
|
||||
USF(uartNum) = value;
|
||||
}
|
||||
|
||||
static const volatile uint8_t* IRAM_ATTR FillUartFifo(uint8_t uartNum,
|
||||
const volatile uint8_t* start,
|
||||
const volatile uint8_t* end);
|
||||
};
|
||||
|
||||
// this template method class is used to track the data being sent on the uart
|
||||
// when using our own UART ISR
|
||||
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
|
||||
//
|
||||
class NeoEsp8266UartInterruptContext : NeoEsp8266UartContext
|
||||
{
|
||||
public:
|
||||
NeoEsp8266UartInterruptContext() :
|
||||
_asyncBuff(nullptr),
|
||||
_asyncBuffEnd(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsSending()
|
||||
{
|
||||
return (_asyncBuff != _asyncBuffEnd);
|
||||
}
|
||||
|
||||
void StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end);
|
||||
void Attach(uint8_t uartNum);
|
||||
void Detach(uint8_t uartNum);
|
||||
|
||||
private:
|
||||
volatile const uint8_t* _asyncBuff;
|
||||
volatile const uint8_t* _asyncBuffEnd;
|
||||
volatile static NeoEsp8266UartInterruptContext* s_uartInteruptContext[2];
|
||||
|
||||
static void IRAM_ATTR Isr(void* param, void* exceptionFrame);
|
||||
};
|
||||
|
||||
// this template feature class is used a base for all others and contains
|
||||
// common methods
|
||||
//
|
||||
class UartFeatureBase
|
||||
{
|
||||
protected:
|
||||
static void ConfigUart(uint8_t uartNum, bool invert)
|
||||
{
|
||||
// clear all invert bits
|
||||
USC0(uartNum) &= ~((1 << UCDTRI) | (1 << UCRTSI) | (1 << UCTXI) | (1 << UCDSRI) | (1 << UCCTSI) | (1 << UCRXI));
|
||||
|
||||
if (!invert)
|
||||
{
|
||||
// For normal operations,
|
||||
// Invert the TX voltage associated with logic level so:
|
||||
// - A logic level 0 will generate a Vcc signal
|
||||
// - A logic level 1 will generate a Gnd signal
|
||||
USC0(uartNum) |= (1 << UCTXI);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// this template feature class is used to define the specifics for uart0
|
||||
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
|
||||
//
|
||||
class UartFeature0 : UartFeatureBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t Index = 0;
|
||||
static void Init(uint32_t baud, bool invert)
|
||||
{
|
||||
// Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1)
|
||||
Serial.begin(baud, SERIAL_6N1, SERIAL_TX_ONLY);
|
||||
ConfigUart(Index, invert);
|
||||
}
|
||||
};
|
||||
|
||||
// this template feature class is used to define the specifics for uart1
|
||||
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
|
||||
//
|
||||
class UartFeature1 : UartFeatureBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t Index = 1;
|
||||
static void Init(uint32_t baud, bool invert)
|
||||
{
|
||||
// Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1)
|
||||
Serial1.begin(baud, SERIAL_6N1, SERIAL_TX_ONLY);
|
||||
ConfigUart(Index, invert);
|
||||
}
|
||||
};
|
||||
|
||||
// this template method class is used a base for all others and contains
|
||||
// common properties and methods
|
||||
//
|
||||
// used by NeoEsp8266Uart and NeoEsp8266AsyncUart
|
||||
//
|
||||
class NeoEsp8266UartBase
|
||||
{
|
||||
protected:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
uint8_t* _data; // Holds LED color values
|
||||
uint32_t _startTime; // Microsecond count when last update started
|
||||
|
||||
NeoEsp8266UartBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize)
|
||||
{
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
~NeoEsp8266UartBase()
|
||||
{
|
||||
free(_data);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// this template method class is used to glue uart feature and context for
|
||||
// synchronous uart method
|
||||
//
|
||||
// used by NeoEsp8266UartMethodBase
|
||||
// T_UARTFEATURE - (UartFeature0 | UartFeature1)
|
||||
// T_UARTCONTEXT - (NeoEsp8266UartContext | NeoEsp8266UartInterruptContext)
|
||||
//
|
||||
template<typename T_UARTFEATURE, typename T_UARTCONTEXT> class NeoEsp8266Uart : public NeoEsp8266UartBase
|
||||
{
|
||||
protected:
|
||||
NeoEsp8266Uart(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
NeoEsp8266UartBase(pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
|
||||
~NeoEsp8266Uart()
|
||||
{
|
||||
// Wait until the TX fifo is empty. This way we avoid broken frames
|
||||
// when destroying & creating a NeoPixelBus to change its length.
|
||||
while (T_UARTCONTEXT::GetTxFifoLength(T_UARTFEATURE::Index) > 0)
|
||||
{
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeUart(uint32_t uartBaud, bool invert)
|
||||
{
|
||||
T_UARTFEATURE::Init(uartBaud, invert);
|
||||
}
|
||||
|
||||
void UpdateUart(bool)
|
||||
{
|
||||
// Since the UART can finish sending queued bytes in the FIFO in
|
||||
// the background, instead of waiting for the FIFO to flush
|
||||
// we annotate the start time of the frame so we can calculate
|
||||
// when it will finish.
|
||||
_startTime = micros();
|
||||
|
||||
// Then keep filling the FIFO until done
|
||||
const uint8_t* ptr = _data;
|
||||
const uint8_t* end = ptr + _sizeData;
|
||||
while (ptr != end)
|
||||
{
|
||||
ptr = const_cast<uint8_t*>(T_UARTCONTEXT::FillUartFifo(T_UARTFEATURE::Index, ptr, end));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// this template method class is used to glue uart feature and context for
|
||||
// asynchronously uart method
|
||||
//
|
||||
// This UART controller uses two buffers that are swapped in every call to
|
||||
// NeoPixelBus.Show(). One buffer contains the data that is being sent
|
||||
// asynchronosly and another buffer contains the data that will be send
|
||||
// in the next call to NeoPixelBus.Show().
|
||||
//
|
||||
// Therefore, the result of NeoPixelBus.Pixels() is invalidated after
|
||||
// every call to NeoPixelBus.Show() and must not be cached.
|
||||
//
|
||||
// used by NeoEsp8266UartMethodBase
|
||||
// T_UARTFEATURE - (UartFeature0 | UartFeature1)
|
||||
// T_UARTCONTEXT - (NeoEsp8266UartContext | NeoEsp8266UartInterruptContext)
|
||||
//
|
||||
template<typename T_UARTFEATURE, typename T_UARTCONTEXT> class NeoEsp8266AsyncUart : public NeoEsp8266UartBase
|
||||
{
|
||||
protected:
|
||||
NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
NeoEsp8266UartBase(pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
_dataSending = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
}
|
||||
|
||||
~NeoEsp8266AsyncUart()
|
||||
{
|
||||
// Remember: the UART interrupt can be sending data from _dataSending in the background
|
||||
while (_context.IsSending())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
// detach context, which will disable intr, may disable ISR
|
||||
_context.Detach(T_UARTFEATURE::Index);
|
||||
|
||||
free(_dataSending);
|
||||
}
|
||||
|
||||
void IRAM_ATTR InitializeUart(uint32_t uartBaud, bool invert)
|
||||
{
|
||||
T_UARTFEATURE::Init(uartBaud, invert);
|
||||
|
||||
// attach the context, which will enable the ISR
|
||||
_context.Attach(T_UARTFEATURE::Index);
|
||||
}
|
||||
|
||||
void UpdateUart(bool maintainBufferConsistency)
|
||||
{
|
||||
// Instruct ESP8266 hardware uart to send the pixels asynchronously
|
||||
_context.StartSending(T_UARTFEATURE::Index,
|
||||
_data,
|
||||
_data + _sizeData);
|
||||
|
||||
// Annotate when we started to send bytes, so we can calculate when we are ready to send again
|
||||
_startTime = micros();
|
||||
|
||||
if (maintainBufferConsistency)
|
||||
{
|
||||
// copy editing to sending,
|
||||
// this maintains the contract that "colors present before will
|
||||
// be the same after", otherwise GetPixelColor will be inconsistent
|
||||
memcpy(_dataSending, _data, _sizeData);
|
||||
}
|
||||
|
||||
// swap so the user can modify without affecting the async operation
|
||||
std::swap(_dataSending, _data);
|
||||
}
|
||||
|
||||
private:
|
||||
T_UARTCONTEXT _context;
|
||||
|
||||
uint8_t* _dataSending; // Holds a copy of LED color values taken when UpdateUart began
|
||||
};
|
||||
|
||||
class NeoEsp8266UartSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
|
||||
static const uint32_t UartBaud = 3200000; // 800mhz, 4 serial bytes per NeoByte
|
||||
};
|
||||
|
||||
// NeoEsp8266UartSpeedWs2813 contains the timing constants used to get NeoPixelBus running with the Ws2813
|
||||
class NeoEsp8266UartSpeedWs2812x : public NeoEsp8266UartSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 300; // us between data send bursts to reset for next update
|
||||
};
|
||||
|
||||
class NeoEsp8266UartSpeedSk6812 : public NeoEsp8266UartSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 80; // us between data send bursts to reset for next update
|
||||
};
|
||||
|
||||
class NeoEsp8266UartSpeedTm1814 : public NeoEsp8266UartSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200; // us between data send bursts to reset for next update
|
||||
};
|
||||
|
||||
class NeoEsp8266UartSpeedTm1829 : public NeoEsp8266UartSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 200; // us between data send bursts to reset for next update
|
||||
};
|
||||
|
||||
// NeoEsp8266UartSpeed800Kbps contains the timing constant used to get NeoPixelBus running at 800Khz
|
||||
class NeoEsp8266UartSpeed800Kbps : public NeoEsp8266UartSpeed800KbpsBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update
|
||||
};
|
||||
|
||||
// NeoEsp8266UartSpeed400Kbps contains the timing constant used to get NeoPixelBus running at 400Khz
|
||||
class NeoEsp8266UartSpeed400Kbps
|
||||
{
|
||||
public:
|
||||
static const uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed
|
||||
static const uint32_t UartBaud = 1600000; // 400mhz, 4 serial bytes per NeoByte
|
||||
static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update
|
||||
};
|
||||
|
||||
// NeoEsp8266UartSpeedApa106 contains the timing constant used to get NeoPixelBus running for Apa106
|
||||
// Pulse cycle = 1.71 = 1.368 longer than normal, 0.731 slower, NeoEsp8266UartSpeedApa1066
|
||||
class NeoEsp8266UartSpeedApa106
|
||||
{
|
||||
public:
|
||||
static const uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element at 400khz speed
|
||||
static const uint32_t UartBaud = 2339181; // APA106 pulse cycle of 1.71us, 4 serial bytes per NeoByte
|
||||
static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update
|
||||
};
|
||||
|
||||
class NeoEsp8266UartNotInverted
|
||||
{
|
||||
public:
|
||||
const static bool Inverted = false;
|
||||
};
|
||||
|
||||
class NeoEsp8266UartInverted
|
||||
{
|
||||
public:
|
||||
const static bool Inverted = true;
|
||||
};
|
||||
|
||||
// NeoEsp8266UartMethodBase is a light shell arround NeoEsp8266Uart or NeoEsp8266AsyncUart that
|
||||
// implements the methods needed to operate as a NeoPixelBus method.
|
||||
//
|
||||
// T_SPEED - (NeoEsp8266UartSpeed*)
|
||||
// T_BASE - (NeoEsp8266Uart | NeoEsp8266AsyncUart)
|
||||
// T_INVERT - (NeoEsp8266UartNotInverted | NeoEsp8266UartInverted)
|
||||
//
|
||||
template<typename T_SPEED, typename T_BASE, typename T_INVERT>
|
||||
class NeoEsp8266UartMethodBase: public T_BASE
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoEsp8266UartMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize)
|
||||
: T_BASE(pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
NeoEsp8266UartMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize)
|
||||
: T_BASE(pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
uint32_t delta = micros() - this->_startTime;
|
||||
return delta >= getPixelTime() + T_SPEED::ResetTimeUs;
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
this->InitializeUart(T_SPEED::UartBaud, T_INVERT::Inverted);
|
||||
|
||||
// Inverting logic levels can generate a phantom bit in the led strip bus
|
||||
// We need to delay 50+ microseconds the output stream to force a data
|
||||
// latch and discard this bit. Otherwise, that bit would be prepended to
|
||||
// the first frame corrupting it.
|
||||
this->_startTime = micros() - getPixelTime();
|
||||
}
|
||||
|
||||
void Update(bool maintainBufferConsistency)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
// the function will simply hold off (if needed) on issuing the
|
||||
// subsequent round of data until the latch time has elapsed. This
|
||||
// allows the mainline code to start generating the next frame of data
|
||||
// rather than stalling for the latch.
|
||||
while (!this->IsReadyToUpdate())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
this->UpdateUart(maintainBufferConsistency);
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return this->_data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return this->_sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t getPixelTime() const
|
||||
{
|
||||
return (T_SPEED::ByteSendTimeUs * this->_sizeData);
|
||||
};
|
||||
};
|
||||
|
||||
// uart 0
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Ws2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Sk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1814, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart0Tm1814Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1829, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart0Tm1829Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedApa106, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Apa106Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart0800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart0400KbpsMethod;
|
||||
|
||||
typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2813Method;
|
||||
typedef NeoEsp8266Uart0800KbpsMethod NeoEsp8266Uart0Ws2812Method;
|
||||
typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2811Method;
|
||||
typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2816Method;
|
||||
typedef NeoEsp8266Uart0Tm1814Method NeoEsp8266Uart0Tm1914Method;
|
||||
typedef NeoEsp8266Uart0Sk6812Method NeoEsp8266Uart0Lc8812Method;
|
||||
|
||||
// uart 1
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Ws2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Sk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1814, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart1Tm1814Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1829, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart1Tm1829Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedApa106, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Apa106Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart1800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart1400KbpsMethod;
|
||||
|
||||
typedef NeoEsp8266Uart1Ws2812xMethod NeoEsp8266Uart1Ws2813Method;
|
||||
typedef NeoEsp8266Uart1800KbpsMethod NeoEsp8266Uart1Ws2812Method;
|
||||
typedef NeoEsp8266Uart1Ws2812xMethod NeoEsp8266Uart1Ws2811Method;
|
||||
typedef NeoEsp8266Uart1Ws2812xMethod NeoEsp8266Uart1Ws2816Method;
|
||||
typedef NeoEsp8266Uart1Tm1814Method NeoEsp8266Uart1Tm1914Method;
|
||||
typedef NeoEsp8266Uart1Sk6812Method NeoEsp8266Uart1Lc8812Method;
|
||||
|
||||
// uart 0 async
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Ws2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Sk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1814, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Tm1814Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1829, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Tm1829Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedApa106, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Apa106Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0400KbpsMethod;
|
||||
|
||||
typedef NeoEsp8266AsyncUart0Ws2812xMethod NeoEsp8266AsyncUart0Ws2813Method;
|
||||
typedef NeoEsp8266AsyncUart0800KbpsMethod NeoEsp8266AsyncUart0Ws2812Method;
|
||||
typedef NeoEsp8266AsyncUart0Ws2812xMethod NeoEsp8266AsyncUart0Ws2811Method;
|
||||
typedef NeoEsp8266AsyncUart0Ws2812xMethod NeoEsp8266AsyncUart0Ws2816Method;
|
||||
typedef NeoEsp8266AsyncUart0Tm1814Method NeoEsp8266AsyncUart0Tm1914Method;
|
||||
typedef NeoEsp8266AsyncUart0Sk6812Method NeoEsp8266AsyncUart0Lc8812Method;
|
||||
|
||||
// uart 1 async
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Ws2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Sk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1814, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Tm1814Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1829, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Tm1829Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedApa106, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Apa106Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1400KbpsMethod;
|
||||
|
||||
typedef NeoEsp8266AsyncUart1Ws2812xMethod NeoEsp8266AsyncUart1Ws2813Method;
|
||||
typedef NeoEsp8266AsyncUart1800KbpsMethod NeoEsp8266AsyncUart1Ws2812Method;
|
||||
typedef NeoEsp8266AsyncUart1Ws2812xMethod NeoEsp8266AsyncUart1Ws2811Method;
|
||||
typedef NeoEsp8266AsyncUart1Ws2812xMethod NeoEsp8266AsyncUart1Ws2816Method;
|
||||
typedef NeoEsp8266AsyncUart1Tm1814Method NeoEsp8266AsyncUart1Tm1914Method;
|
||||
typedef NeoEsp8266AsyncUart1Sk6812Method NeoEsp8266AsyncUart1Lc8812Method;
|
||||
|
||||
// inverted
|
||||
//
|
||||
// uart 0
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart0Ws2812xInvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart0Sk6812InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1814, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Tm1814InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1829, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Tm1829InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedApa106, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart0Apa106InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart0800KbpsInvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart0400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp8266Uart0Ws2812xInvertedMethod NeoEsp8266Uart0Ws2813InvertedMethod;
|
||||
typedef NeoEsp8266Uart0800KbpsInvertedMethod NeoEsp8266Uart0Ws2812InvertedMethod;
|
||||
typedef NeoEsp8266Uart0Ws2812xInvertedMethod NeoEsp8266Uart0Ws2811InvertedMethod;
|
||||
typedef NeoEsp8266Uart0Ws2812xInvertedMethod NeoEsp8266Uart0Ws2816InvertedMethod;
|
||||
typedef NeoEsp8266Uart0Tm1814InvertedMethod NeoEsp8266Uart0Tm1914InvertedMethod;
|
||||
typedef NeoEsp8266Uart0Sk6812InvertedMethod NeoEsp8266Uart0Lc8812InvertedMethod;
|
||||
|
||||
// uart 1
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart1Ws2812xInvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart1Sk6812InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1814, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Tm1814InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1829, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Tm1829InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedApa106, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart1Apa106InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart1800KbpsInvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart1400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp8266Uart1Ws2812xInvertedMethod NeoEsp8266Uart1Ws2813InvertedMethod;
|
||||
typedef NeoEsp8266Uart1800KbpsInvertedMethod NeoEsp8266Uart1Ws2812InvertedMethod;
|
||||
typedef NeoEsp8266Uart1Ws2812xInvertedMethod NeoEsp8266Uart1Ws2811InvertedMethod;
|
||||
typedef NeoEsp8266Uart1Ws2812xInvertedMethod NeoEsp8266Uart1Ws2816InvertedMethod;
|
||||
typedef NeoEsp8266Uart1Tm1814InvertedMethod NeoEsp8266Uart1Tm1914InvertedMethod;
|
||||
typedef NeoEsp8266Uart1Sk6812InvertedMethod NeoEsp8266Uart1Lc8812InvertedMethod;
|
||||
|
||||
// uart 0 async
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Ws2812xInvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Sk6812InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1814, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Tm1814InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1829, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Tm1829InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedApa106, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Apa106InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0800KbpsInvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp8266AsyncUart0Ws2812xInvertedMethod NeoEsp8266AsyncUart0Ws2813InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart0800KbpsInvertedMethod NeoEsp8266AsyncUart0Ws2812InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart0Ws2812xInvertedMethod NeoEsp8266AsyncUart0Ws2811InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart0Ws2812xInvertedMethod NeoEsp8266AsyncUart0Ws2816InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart0Tm1814InvertedMethod NeoEsp8266AsyncUart0Tm1914InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart0Sk6812InvertedMethod NeoEsp8266AsyncUart0Lc8812InvertedMethod;
|
||||
|
||||
// uart 1 async
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Ws2812xInvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Sk6812InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1814, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Tm1814InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1829, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Tm1829InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedApa106, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Apa106InvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1800KbpsInvertedMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoEsp8266AsyncUart1Ws2812xInvertedMethod NeoEsp8266AsyncUart1Ws2813InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart1800KbpsInvertedMethod NeoEsp8266AsyncUart1Ws2812InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart1Ws2812xInvertedMethod NeoEsp8266AsyncUart1Ws2811InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart1Ws2812xInvertedMethod NeoEsp8266AsyncUart1Ws2816InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart1Tm1814InvertedMethod NeoEsp8266AsyncUart1Tm1914InvertedMethod;
|
||||
typedef NeoEsp8266AsyncUart1Sk6812InvertedMethod NeoEsp8266AsyncUart1Lc8812InvertedMethod;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp8266 and Esp32
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
static inline uint32_t getCycleCount(void)
|
||||
{
|
||||
uint32_t ccount;
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
__asm__ __volatile__("csrr %0,0x7e2":"=r" (ccount));
|
||||
//ccount = esp_cpu_get_ccount();
|
||||
#else
|
||||
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
|
||||
#endif
|
||||
return ccount;
|
||||
}
|
||||
|
||||
void IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
||||
const uint8_t* end,
|
||||
uint8_t pin,
|
||||
uint32_t t0h,
|
||||
uint32_t t1h,
|
||||
uint32_t period,
|
||||
size_t sizePixel,
|
||||
uint32_t tSpacing,
|
||||
bool invert)
|
||||
{
|
||||
uint32_t setValue = _BV(pin);
|
||||
uint32_t clearValue = _BV(pin);
|
||||
uint8_t mask = 0x80;
|
||||
uint8_t subpix = *pixels++;
|
||||
uint8_t element = 0;
|
||||
uint32_t cyclesStart = 0; // trigger emediately
|
||||
uint32_t cyclesNext = 0;
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
volatile uint32_t* setRegister = &GPIO.out_w1ts.val;
|
||||
volatile uint32_t* clearRegister = &GPIO.out_w1tc.val;
|
||||
setValue = _BV(pin);
|
||||
clearValue = _BV(pin);
|
||||
#else
|
||||
volatile uint32_t* setRegister = &GPIO.out_w1ts;
|
||||
volatile uint32_t* clearRegister = &GPIO.out_w1tc;
|
||||
#endif // defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#else
|
||||
uint32_t setRegister = PERIPHS_GPIO_BASEADDR + GPIO_OUT_W1TS_ADDRESS;
|
||||
uint32_t clearRegister = PERIPHS_GPIO_BASEADDR + GPIO_OUT_W1TC_ADDRESS;
|
||||
if (pin == 16)
|
||||
{
|
||||
setRegister = RTC_GPIO_OUT;
|
||||
clearRegister = RTC_GPIO_OUT;
|
||||
// reading AND writing RTC_GPIO_OUT is too slow inside the loop so
|
||||
// we only do writing in the loop
|
||||
clearValue = (READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe);
|
||||
setValue = clearValue | 1;
|
||||
}
|
||||
#endif // defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
if (invert)
|
||||
{
|
||||
std::swap(setRegister, clearRegister);
|
||||
std::swap(setValue, clearValue);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// do the checks here while we are waiting on time to pass
|
||||
uint32_t cyclesBit = t0h;
|
||||
if (subpix & mask)
|
||||
{
|
||||
cyclesBit = t1h;
|
||||
}
|
||||
|
||||
// after we have done as much work as needed for this next bit
|
||||
// now wait for the HIGH
|
||||
while (((cyclesStart = getCycleCount()) - cyclesNext) < period);
|
||||
|
||||
// set pin state
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
*setRegister = setValue;
|
||||
#else
|
||||
WRITE_PERI_REG(setRegister, setValue);
|
||||
#endif
|
||||
|
||||
// wait for the LOW
|
||||
while ((getCycleCount() - cyclesStart) < cyclesBit);
|
||||
|
||||
// reset pin start
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
*clearRegister = clearValue;
|
||||
#else
|
||||
WRITE_PERI_REG(clearRegister, clearValue);
|
||||
#endif
|
||||
|
||||
cyclesNext = cyclesStart;
|
||||
|
||||
// next bit
|
||||
mask >>= 1;
|
||||
if (mask == 0)
|
||||
{
|
||||
// no more bits to send in this byte
|
||||
// check for another byte
|
||||
if (pixels >= end)
|
||||
{
|
||||
// no more bytes to send so stop
|
||||
break;
|
||||
}
|
||||
// reset mask to first bit and get the next byte
|
||||
mask = 0x80;
|
||||
subpix = *pixels++;
|
||||
|
||||
// if pixel spacing is needed
|
||||
if (tSpacing)
|
||||
{
|
||||
element++;
|
||||
if (element == sizePixel)
|
||||
{
|
||||
element = 0;
|
||||
|
||||
// wait for pixel spacing
|
||||
while ((getCycleCount() - cyclesNext) < tSpacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
@@ -0,0 +1,364 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Esp8266 and Esp32
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
#include <eagle_soc.h>
|
||||
#endif
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define CYCLES_LOOPTEST (1) // adjustment due to loop exit test instruction cycles
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#define CYCLES_LOOPTEST (2) // adjustment due to loop exit test instruction cycles
|
||||
#else
|
||||
#define CYCLES_LOOPTEST (4) // adjustment due to loop exit test instruction cycles
|
||||
#endif
|
||||
|
||||
extern void neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
||||
const uint8_t* end,
|
||||
uint8_t pin,
|
||||
uint32_t t0h,
|
||||
uint32_t t1h,
|
||||
uint32_t period,
|
||||
size_t sizePixel,
|
||||
uint32_t tSpacing,
|
||||
bool invert);
|
||||
|
||||
|
||||
class NeoEspNotInverted
|
||||
{
|
||||
public:
|
||||
const static uint8_t IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoEspInverted
|
||||
{
|
||||
public:
|
||||
const static uint8_t IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoEspBitBangSpeedWs2811
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 3333333 - CYCLES_LOOPTEST); // 0.3us
|
||||
const static uint32_t T1H = (F_CPU / 1052632 - CYCLES_LOOPTEST); // 0.95us
|
||||
const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
|
||||
|
||||
static const uint32_t ResetTimeUs = 300;
|
||||
const static uint32_t TInterPixel = 0;
|
||||
};
|
||||
|
||||
class NeoEspBitBangSpeedWs2812x
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us
|
||||
const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us
|
||||
const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
|
||||
|
||||
static const uint32_t ResetTimeUs = 300;
|
||||
const static uint32_t TInterPixel = 0;
|
||||
};
|
||||
|
||||
class NeoEspBitBangSpeedSk6812
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us
|
||||
const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us
|
||||
const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
|
||||
|
||||
static const uint32_t ResetTimeUs = 80;
|
||||
const static uint32_t TInterPixel = 0;
|
||||
};
|
||||
|
||||
// Tm1814 normal is inverted signal
|
||||
class NeoEspBitBangSpeedTm1814
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 2916666 - CYCLES_LOOPTEST); // 0.35us
|
||||
const static uint32_t T1H = (F_CPU / 1666666 - CYCLES_LOOPTEST); // 0.75us
|
||||
const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
|
||||
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
const static uint32_t TInterPixel = 0;
|
||||
};
|
||||
|
||||
// Tm1829 normal is inverted signal
|
||||
class NeoEspBitBangSpeedTm1829
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 3333333 - CYCLES_LOOPTEST); // 0.3us
|
||||
const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us
|
||||
const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
|
||||
|
||||
static const uint32_t ResetTimeUs = 200;
|
||||
const static uint32_t TInterPixel = 0;
|
||||
};
|
||||
|
||||
class NeoEspBitBangSpeed800Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us
|
||||
const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us
|
||||
const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
|
||||
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
const static uint32_t TInterPixel = 0;
|
||||
};
|
||||
|
||||
class NeoEspBitBangSpeed400Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 2000000 - CYCLES_LOOPTEST);
|
||||
const static uint32_t T1H = (F_CPU / 833333 - CYCLES_LOOPTEST);
|
||||
const static uint32_t Period = (F_CPU / 400000 - CYCLES_LOOPTEST);
|
||||
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
const static uint32_t TInterPixel = 0;
|
||||
};
|
||||
|
||||
class NeoEspBitBangSpeedApa106
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 2857143 - CYCLES_LOOPTEST); // 0.35us
|
||||
const static uint32_t T1H = (F_CPU / 740741 - CYCLES_LOOPTEST); // 1.35
|
||||
const static uint32_t Period = (F_CPU / 606061 - CYCLES_LOOPTEST); // 1.65us
|
||||
|
||||
static const uint32_t ResetTimeUs = 50;
|
||||
const static uint32_t TInterPixel = 0;
|
||||
};
|
||||
|
||||
class NeoEspBitBangSpeedIntertek
|
||||
{
|
||||
public:
|
||||
const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us
|
||||
const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us
|
||||
const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
|
||||
|
||||
const static uint32_t ResetTimeUs = 12470;
|
||||
const static uint32_t TInterPixel = (F_CPU / 50000); // 20us
|
||||
};
|
||||
|
||||
|
||||
template<typename T_SPEED, typename T_INVERTED> class NeoEspBitBangEncode : public T_SPEED, public T_INVERTED
|
||||
{
|
||||
public:
|
||||
static void WritePixels(uint8_t pin,
|
||||
const uint8_t* data,
|
||||
size_t sizeData,
|
||||
size_t sizePixel)
|
||||
{
|
||||
neoEspBitBangWriteSpacingPixels(data,
|
||||
data + sizeData,
|
||||
pin,
|
||||
T_SPEED::T0H,
|
||||
T_SPEED::T1H,
|
||||
T_SPEED::Period,
|
||||
sizePixel,
|
||||
T_SPEED::TInterPixel,
|
||||
T_INVERTED::IdleLevel);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T_ENCODER> class NeoEspBitBangMethodBase
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoEspBitBangMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizePixel(elementSize),
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin)
|
||||
{
|
||||
pinMode(pin, OUTPUT);
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
~NeoEspBitBangMethodBase()
|
||||
{
|
||||
pinMode(_pin, INPUT);
|
||||
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
uint32_t delta = micros() - _endTime;
|
||||
|
||||
return (delta >= T_ENCODER::ResetTimeUs);
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
digitalWrite(_pin, T_ENCODER::IdleLevel);
|
||||
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
// the function will simply hold off (if needed) on issuing the
|
||||
// subsequent round of data until the latch time has elapsed. This
|
||||
// allows the mainline code to start generating the next frame of data
|
||||
// rather than stalling for the latch.
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield(); // allows for system yield if needed
|
||||
}
|
||||
|
||||
// Need 100% focus on instruction timing
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// delay(1); // required ?
|
||||
portMUX_TYPE updateMux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
portENTER_CRITICAL(&updateMux);
|
||||
#else
|
||||
noInterrupts();
|
||||
#endif
|
||||
|
||||
T_ENCODER::WritePixels(_pin,
|
||||
_data,
|
||||
_sizeData,
|
||||
_sizePixel);
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
portEXIT_CRITICAL(&updateMux);
|
||||
#else
|
||||
interrupts();
|
||||
#endif
|
||||
|
||||
// save EOD time for latch on next call
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizePixel; // size of a pixel in _data
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
const uint8_t _pin; // output pin number
|
||||
|
||||
uint32_t _endTime; // Latch timing reference
|
||||
uint8_t* _data; // Holds LED color values
|
||||
};
|
||||
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedWs2811, NeoEspNotInverted>> NeoEsp32BitBangWs2811Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedWs2812x, NeoEspNotInverted>> NeoEsp32BitBangWs2812xMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedSk6812, NeoEspNotInverted>> NeoEsp32BitBangSk6812Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedTm1814, NeoEspInverted>> NeoEsp32BitBangTm1814Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedTm1829, NeoEspInverted>> NeoEsp32BitBangTm1829Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeed800Kbps, NeoEspNotInverted>> NeoEsp32BitBang800KbpsMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeed400Kbps, NeoEspNotInverted>> NeoEsp32BitBang400KbpsMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedApa106, NeoEspNotInverted>> NeoEsp32BitBangApa106Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedIntertek, NeoEspNotInverted>> NeoEsp32BitBangIntertekMethod;
|
||||
|
||||
typedef NeoEsp32BitBangWs2812xMethod NeoEsp32BitBangWs2813Method;
|
||||
typedef NeoEsp32BitBang800KbpsMethod NeoEsp32BitBangWs2812Method;
|
||||
typedef NeoEsp32BitBangWs2812xMethod NeoEsp32BitBangWs2816Method;
|
||||
typedef NeoEsp32BitBangTm1814Method NeoEsp32BitBangTm1914Method;
|
||||
typedef NeoEsp32BitBangSk6812Method NeoEsp32BitBangLc8812Method;
|
||||
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedWs2811, NeoEspInverted>> NeoEsp32BitBangWs2811InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedWs2812x, NeoEspInverted>> NeoEsp32BitBangWs2812xInvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedSk6812, NeoEspInverted>> NeoEsp32BitBangSk6812InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedTm1814, NeoEspNotInverted>> NeoEsp32BitBangTm1814InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedTm1829, NeoEspNotInverted>> NeoEsp32BitBangTm1829InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeed800Kbps, NeoEspInverted>> NeoEsp32BitBang800KbpsInvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeed400Kbps, NeoEspInverted>> NeoEsp32BitBang400KbpsInvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedApa106, NeoEspInverted>> NeoEsp32BitBangApa106InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedIntertek, NeoEspInverted>> NeoEsp32BitBangIntertekInvertedMethod;
|
||||
|
||||
typedef NeoEsp32BitBangWs2812xInvertedMethod NeoEsp32BitBangWs2813InvertedMethod;
|
||||
typedef NeoEsp32BitBang800KbpsInvertedMethod NeoEsp32BitBangWs2812InvertedMethod;
|
||||
typedef NeoEsp32BitBangWs2812xInvertedMethod NeoEsp32BitBangWs2816InvertedMethod;
|
||||
typedef NeoEsp32BitBangTm1814InvertedMethod NeoEsp32BitBangTm1914InvertedMethod;
|
||||
typedef NeoEsp32BitBangSk6812InvertedMethod NeoEsp32BitBangLc8812InvertedMethod;
|
||||
|
||||
#else // defined(ARDUINO_ARCH_ESP8266)
|
||||
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedWs2811, NeoEspNotInverted>> NeoEsp8266BitBangWs2811Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedWs2812x, NeoEspNotInverted>> NeoEsp8266BitBangWs2812xMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedSk6812, NeoEspNotInverted>> NeoEsp8266BitBangSk6812Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedTm1814, NeoEspInverted>> NeoEsp8266BitBangTm1814Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedTm1829, NeoEspInverted>> NeoEsp8266BitBangTm1829Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeed800Kbps, NeoEspNotInverted>> NeoEsp8266BitBang800KbpsMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeed400Kbps, NeoEspNotInverted>> NeoEsp8266BitBang400KbpsMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedApa106, NeoEspNotInverted>> NeoEsp8266BitBangApa106Method;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedIntertek, NeoEspNotInverted>> NeoEsp8266BitBangIntertekMethod;
|
||||
|
||||
typedef NeoEsp8266BitBangWs2812xMethod NeoEsp8266BitBangWs2813Method;
|
||||
typedef NeoEsp8266BitBang800KbpsMethod NeoEsp8266BitBangWs2812Method;
|
||||
typedef NeoEsp8266BitBangWs2812xMethod NeoEsp8266BitBangWs2816Method;
|
||||
typedef NeoEsp8266BitBangTm1814Method NeoEsp8266BitBangTm1914Method;
|
||||
typedef NeoEsp8266BitBangSk6812Method NeoEsp8266BitBangLc8812Method;
|
||||
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedWs2811, NeoEspInverted>> NeoEsp8266BitBangWs2811InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedWs2812x, NeoEspInverted>> NeoEsp8266BitBangWs2812xInvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedSk6812, NeoEspInverted>> NeoEsp8266BitBangSk6812InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedTm1814, NeoEspNotInverted>> NeoEsp8266BitBangTm1814InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedTm1829, NeoEspNotInverted>> NeoEsp8266BitBangTm1829InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeed800Kbps, NeoEspInverted>> NeoEsp8266BitBang800KbpsInvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeed400Kbps, NeoEspInverted>> NeoEsp8266BitBang400KbpsInvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedApa106, NeoEspInverted>> NeoEsp8266BitBangApa106InvertedMethod;
|
||||
typedef NeoEspBitBangMethodBase<NeoEspBitBangEncode<NeoEspBitBangSpeedIntertek, NeoEspInverted>> NeoEsp8266BitBangIntertekInvertedMethod;
|
||||
|
||||
typedef NeoEsp8266BitBangWs2812xInvertedMethod NeoEsp8266BitBangWs2813InvertedMethod;
|
||||
typedef NeoEsp8266BitBang800KbpsInvertedMethod NeoEsp8266BitBangWs2812InvertedMethod;
|
||||
typedef NeoEsp8266BitBangWs2812xInvertedMethod NeoEsp8266BitBangWs2816InvertedMethod;
|
||||
typedef NeoEsp8266BitBangTm1814InvertedMethod NeoEsp8266BitBangTm1914InvertedMethod;
|
||||
typedef NeoEsp8266BitBangSk6812InvertedMethod NeoEsp8266BitBangLc8812InvertedMethod;
|
||||
|
||||
#endif // defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
// ESP bitbang doesn't have defaults and should avoided except for testing
|
||||
|
||||
#endif // defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
@@ -0,0 +1,683 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper classes for Nrf52* MCUs.
|
||||
Nano 33 BLE
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
The contents of this file were taken from the Adafruit NeoPixel library
|
||||
and modified only to fit within individual calling functions.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(ARDUINO_ARCH_NRF52840)
|
||||
|
||||
const uint16_t c_dmaBytesPerDataByte = 8 * sizeof(nrf_pwm_values_common_t); // bits * bytes to represent pulse
|
||||
|
||||
// for Bit* variables
|
||||
// count 1 = 0.0625us, so max count (32768) is 2048us
|
||||
|
||||
class NeoNrf52xPwmSpeedWs2811
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3us
|
||||
const static nrf_pwm_values_common_t Bit1 = 14 | 0x8000; // ~0.9us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint16_t CountReset = 240; // 300 us / 1.25us
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmSpeedWs2812x
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 6 | 0x8000; // ~0.4us
|
||||
const static nrf_pwm_values_common_t Bit1 = 13 | 0x8000; // ~0.8us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint32_t CountReset = 240 ; // 300us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmSpeedSk6812
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 6 | 0x8000; // ~0.4us
|
||||
const static nrf_pwm_values_common_t Bit1 = 13 | 0x8000; // ~0.8us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint32_t CountReset = 64; // 80us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmSpeedTm1814
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3us
|
||||
const static nrf_pwm_values_common_t Bit1 = 12; // ~0.7us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint32_t CountReset = 160; // 200us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmSpeedTm1829
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3us
|
||||
const static nrf_pwm_values_common_t Bit1 = 13 | 0x8000; // ~0.8us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint32_t CountReset = 160; // 200us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmSpeedTm1914
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3us
|
||||
const static nrf_pwm_values_common_t Bit1 = 12; // ~0.7us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint32_t CountReset = 160; // 200us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmSpeed800Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 6 | 0x8000; // ~0.4us
|
||||
const static nrf_pwm_values_common_t Bit1 = 13 | 0x8000; // ~0.8us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint32_t CountReset = 40; // 50us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmSpeed400Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 40UL; // 2.5us
|
||||
const static nrf_pwm_values_common_t Bit0 = 13 | 0x8000; // ~0.8us
|
||||
const static nrf_pwm_values_common_t Bit1 = 26 | 0x8000; // ~1.6us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint16_t CountReset = 20; // 50 us / 2.5us
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
// count 1 = 0.0625us, so max count (32768) is 2048us
|
||||
class NeoNrf52xPwmSpeedApa106
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 26UL; // ~1.525us (target is 1.65us)
|
||||
const static nrf_pwm_values_common_t Bit0 = 6 | 0x8000; // ~0.375us (target is 0.35)
|
||||
const static nrf_pwm_values_common_t Bit1 = 21 | 0x8000; // ~1.3125us (target is 1.350)
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint32_t CountReset = 40; // 50us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmSpeedTx1812
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 14UL; // ~0.875us (target is 0.9us)
|
||||
const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3125us (target is 0.3)
|
||||
const static nrf_pwm_values_common_t Bit1 = 10 | 0x8000; // ~0.625us (target is 0.6)
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint32_t CountReset = 228; // 200us / 0.875us pulse width
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeedWs2811
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3us
|
||||
const static nrf_pwm_values_common_t Bit1 = 14; // ~0.9us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint16_t CountReset = 240; // 300 us / 1.25us
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeedWs2812x
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 6; // ~0.4us
|
||||
const static nrf_pwm_values_common_t Bit1 = 13; // ~0.8us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint32_t CountReset = 240; // 300us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeedSk6812
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 6; // ~0.4us
|
||||
const static nrf_pwm_values_common_t Bit1 = 13; // ~0.8us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint32_t CountReset = 64; // 80us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeedTm1814
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3us
|
||||
const static nrf_pwm_values_common_t Bit1 = 12 | 0x8000; // ~0.7us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint32_t CountReset = 160; // 200us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeedTm1829
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3us
|
||||
const static nrf_pwm_values_common_t Bit1 = 13; // ~0.8us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint32_t CountReset = 160; // 200us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeedTm1914
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3us
|
||||
const static nrf_pwm_values_common_t Bit1 = 12 | 0x8000; // ~0.7us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW
|
||||
const static uint32_t CountReset = 160; // 200us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = LOW;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeed800Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 20UL; // 1.25us
|
||||
const static nrf_pwm_values_common_t Bit0 = 6; // ~0.4us
|
||||
const static nrf_pwm_values_common_t Bit1 = 13; // ~0.8us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint32_t CountReset = 40; // 50us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeed400Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 40UL; // 2.5us
|
||||
const static nrf_pwm_values_common_t Bit0 = 13; // ~0.8us
|
||||
const static nrf_pwm_values_common_t Bit1 = 26; // ~1.6us
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint16_t CountReset = 20; // 50 us / 2.5us
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeedApa106
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 26UL; // ~1.525us (target is 1.65us)
|
||||
const static nrf_pwm_values_common_t Bit0 = 6; // ~0.375us (target is 0.35)
|
||||
const static nrf_pwm_values_common_t Bit1 = 21; // ~1.3125us (target is 1.350)
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint32_t CountReset = 40; // 50us / 1.25us pulse width
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwmInvertedSpeedTx1812
|
||||
{
|
||||
public:
|
||||
const static uint32_t CountTop = 14UL; // ~0.875us (target is 0.9us)
|
||||
const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3125us (target is 0.3)
|
||||
const static nrf_pwm_values_common_t Bit1 = 10; // ~0.625us (target is 0.6)
|
||||
const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH
|
||||
const static uint32_t CountReset = 228; // 200us / 0.875us pulse width
|
||||
const static PinStatus IdleLevel = HIGH;
|
||||
};
|
||||
|
||||
class NeoNrf52xPwm0
|
||||
{
|
||||
public:
|
||||
NeoNrf52xPwm0() {};
|
||||
|
||||
inline static NRF_PWM_Type* Pwm()
|
||||
{
|
||||
return NRF_PWM0;
|
||||
}
|
||||
};
|
||||
|
||||
class NeoNrf52xPwm1
|
||||
{
|
||||
public:
|
||||
NeoNrf52xPwm1() {};
|
||||
|
||||
inline static NRF_PWM_Type* Pwm()
|
||||
{
|
||||
return NRF_PWM1;
|
||||
}
|
||||
};
|
||||
|
||||
class NeoNrf52xPwm2
|
||||
{
|
||||
public:
|
||||
NeoNrf52xPwm2() {};
|
||||
|
||||
inline static NRF_PWM_Type* Pwm()
|
||||
{
|
||||
return NRF_PWM2;
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(NRF_PWM3)
|
||||
class NeoNrf52xPwm3
|
||||
{
|
||||
public:
|
||||
NeoNrf52xPwm3() {};
|
||||
|
||||
inline static NRF_PWM_Type* Pwm()
|
||||
{
|
||||
return NRF_PWM3;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// dynamic channel support
|
||||
class NeoNrf52xPwmN
|
||||
{
|
||||
public:
|
||||
NeoNrf52xPwmN(NeoBusChannel channel)
|
||||
{
|
||||
NRF_PWM_Type* PWM[] = {
|
||||
NRF_PWM0,
|
||||
NRF_PWM1,
|
||||
NRF_PWM2
|
||||
#ifdef NRF_PWM3
|
||||
,NRF_PWM3
|
||||
#endif
|
||||
};
|
||||
_pwm = PWM[channel];
|
||||
}
|
||||
|
||||
inline NRF_PWM_Type* Pwm() const
|
||||
{
|
||||
return _pwm;
|
||||
}
|
||||
|
||||
protected:
|
||||
NRF_PWM_Type* _pwm;
|
||||
|
||||
NeoNrf52xPwmN() {};
|
||||
};
|
||||
|
||||
template<typename T_SPEED, typename T_BUS> class NeoNrf52xMethodBase
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
NeoNrf52xMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_pin(pin)
|
||||
{
|
||||
construct();
|
||||
}
|
||||
|
||||
NeoNrf52xMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
|
||||
_sizeData(pixelCount* elementSize + settingsSize),
|
||||
_pin(pin),
|
||||
_bus(channel)
|
||||
{
|
||||
construct();
|
||||
}
|
||||
|
||||
~NeoNrf52xMethodBase()
|
||||
{
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
dmaDeinit();
|
||||
|
||||
pinMode(_pin, INPUT);
|
||||
|
||||
free(_data);
|
||||
free(_dmaBuffer);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return (_bus.Pwm()->EVENTS_STOPPED);
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
digitalWrite(_pin, T_SPEED::IdleLevel);
|
||||
|
||||
dmaInit();
|
||||
|
||||
// must force a first update so the EVENTS_SEQEND gets set as
|
||||
// you can't set it manually
|
||||
FillBuffer();
|
||||
dmaStart();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||
// put a delay at the end of the function, the ending time is noted and
|
||||
// the function will simply hold off (if needed) on issuing the
|
||||
// subsequent round of data until the latch time has elapsed. This
|
||||
// allows the mainline code to start generating the next frame of data
|
||||
// rather than stalling for the latch.
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield(); // allows for system yield if needed
|
||||
}
|
||||
|
||||
FillBuffer();
|
||||
dmaStart();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
const uint8_t _pin; // output pin number
|
||||
const T_BUS _bus; // holds instance for multi channel support
|
||||
|
||||
uint8_t* _data; // Holds LED color values
|
||||
size_t _dmaBufferSize; // total size of _dmaBuffer
|
||||
nrf_pwm_values_common_t* _dmaBuffer; // Holds pixel data in native format for PWM hardware
|
||||
|
||||
void construct()
|
||||
{
|
||||
pinMode(_pin, OUTPUT);
|
||||
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
|
||||
_dmaBufferSize = c_dmaBytesPerDataByte * _sizeData + sizeof(nrf_pwm_values_common_t);
|
||||
_dmaBuffer = static_cast<nrf_pwm_values_common_t*>(malloc(_dmaBufferSize));
|
||||
}
|
||||
|
||||
void dmaInit()
|
||||
{
|
||||
// only use channel zero
|
||||
_bus.Pwm()->PSEL.OUT[0] = digitalPinToPinName(_pin);
|
||||
_bus.Pwm()->PSEL.OUT[1] = NC;
|
||||
_bus.Pwm()->PSEL.OUT[2] = NC;
|
||||
_bus.Pwm()->PSEL.OUT[3] = NC;
|
||||
|
||||
_bus.Pwm()->ENABLE = 1;
|
||||
_bus.Pwm()->MODE = NRF_PWM_MODE_UP;
|
||||
_bus.Pwm()->PRESCALER = NRF_PWM_CLK_16MHz;
|
||||
_bus.Pwm()->COUNTERTOP = T_SPEED::CountTop;
|
||||
_bus.Pwm()->LOOP = 1; // single fire so events get set
|
||||
_bus.Pwm()->DECODER = NRF_PWM_LOAD_COMMON;
|
||||
|
||||
// sequence zero is the primary data with a BitReset entry on the end for
|
||||
// the delay repeating
|
||||
_bus.Pwm()->SEQ[0].PTR = reinterpret_cast<uint32_t>(_dmaBuffer);
|
||||
_bus.Pwm()->SEQ[0].CNT = _dmaBufferSize / sizeof(nrf_pwm_values_common_t);
|
||||
_bus.Pwm()->SEQ[0].REFRESH = 0; // ignored
|
||||
_bus.Pwm()->SEQ[0].ENDDELAY = T_SPEED::CountReset; // ignored still?
|
||||
|
||||
// sequence one is pointing to the BitReset entry at the end of the primary data
|
||||
_bus.Pwm()->SEQ[1].PTR = reinterpret_cast<uint32_t>(_dmaBuffer + (_bus.Pwm()->SEQ[0].CNT - 1));
|
||||
_bus.Pwm()->SEQ[1].CNT = 1;
|
||||
_bus.Pwm()->SEQ[1].REFRESH = 0; // ignored
|
||||
_bus.Pwm()->SEQ[1].ENDDELAY = 0; // ignored
|
||||
|
||||
// stop when the loop finishes
|
||||
_bus.Pwm()->SHORTS = PWM_SHORTS_LOOPSDONE_STOP_Msk;
|
||||
_bus.Pwm()->INTEN = 0;
|
||||
|
||||
dmaResetEvents();
|
||||
}
|
||||
|
||||
void dmaDeinit()
|
||||
{
|
||||
_bus.Pwm()->ENABLE = 0;
|
||||
_bus.Pwm()->PSEL.OUT[0] = NC;
|
||||
}
|
||||
|
||||
void FillBuffer()
|
||||
{
|
||||
nrf_pwm_values_common_t* pDma = _dmaBuffer;
|
||||
nrf_pwm_values_common_t* pDmaEnd = _dmaBuffer + (_dmaBufferSize / sizeof(nrf_pwm_values_common_t));
|
||||
uint8_t* pEnd = _data + _sizeData;
|
||||
|
||||
for (uint8_t* pData = _data; pData < pEnd; pData++)
|
||||
{
|
||||
uint8_t data = *pData;
|
||||
|
||||
for (uint8_t bit = 0; bit < 8; bit++)
|
||||
{
|
||||
*(pDma++) = (data & 0x80) ? T_SPEED::Bit1 : T_SPEED::Bit0;
|
||||
data <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// fill the rest with BitReset as it will get repeated when delaying or
|
||||
// at the end before being stopped
|
||||
while (pDma < pDmaEnd)
|
||||
{
|
||||
*(pDma++) = T_SPEED::BitReset;
|
||||
}
|
||||
}
|
||||
|
||||
void dmaResetEvents()
|
||||
{
|
||||
_bus.Pwm()->EVENTS_LOOPSDONE = 0;
|
||||
_bus.Pwm()->EVENTS_SEQEND[0] = 0;
|
||||
_bus.Pwm()->EVENTS_SEQEND[1] = 0;
|
||||
_bus.Pwm()->EVENTS_STOPPED = 0;
|
||||
}
|
||||
|
||||
void dmaStart()
|
||||
{
|
||||
dmaResetEvents();
|
||||
_bus.Pwm()->TASKS_SEQSTART[0] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
// normal
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2811, NeoNrf52xPwmN> NeoNrf52xPwmNWs2811Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwmN> NeoNrf52xPwmNWs2812xMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwmN> NeoNrf52xPwmNWs2816Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedSk6812, NeoNrf52xPwmN> NeoNrf52xPwmNSk6812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1814, NeoNrf52xPwmN> NeoNrf52xPwmNTm1814Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1829, NeoNrf52xPwmN> NeoNrf52xPwmNTm1829Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1914, NeoNrf52xPwmN> NeoNrf52xPwmNTm1914Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTx1812, NeoNrf52xPwmN> NeoNrf52xPwmNTx1812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedApa106, NeoNrf52xPwmN> NeoNrf52xPwmNApa106Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed800Kbps, NeoNrf52xPwmN> NeoNrf52xPwmN800KbpsMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed400Kbps, NeoNrf52xPwmN> NeoNrf52xPwmN400KbpsMethod;
|
||||
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2811, NeoNrf52xPwm0> NeoNrf52xPwm0Ws2811Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwm0> NeoNrf52xPwm0Ws2812xMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwm0> NeoNrf52xPwm0Ws2816Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedSk6812, NeoNrf52xPwm0> NeoNrf52xPwm0Sk6812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1814, NeoNrf52xPwm0> NeoNrf52xPwm0Tm1814Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1829, NeoNrf52xPwm0> NeoNrf52xPwm0Tm1829Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1914, NeoNrf52xPwm0> NeoNrf52xPwm0Tm1914Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTx1812, NeoNrf52xPwm0> NeoNrf52xPwm0Tx1812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedApa106, NeoNrf52xPwm0> NeoNrf52xPwm0Apa106Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed800Kbps, NeoNrf52xPwm0> NeoNrf52xPwm0800KbpsMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed400Kbps, NeoNrf52xPwm0> NeoNrf52xPwm0400KbpsMethod;
|
||||
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2811, NeoNrf52xPwm1> NeoNrf52xPwm1Ws2811Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwm1> NeoNrf52xPwm1Ws2812xMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwm1> NeoNrf52xPwm1Ws2816Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedSk6812, NeoNrf52xPwm1> NeoNrf52xPwm1Sk6812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1814, NeoNrf52xPwm1> NeoNrf52xPwm1Tm1814Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1829, NeoNrf52xPwm1> NeoNrf52xPwm1Tm1829Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1914, NeoNrf52xPwm1> NeoNrf52xPwm1Tm1914Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTx1812, NeoNrf52xPwm1> NeoNrf52xPwm1Tx1812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedApa106, NeoNrf52xPwm1> NeoNrf52xPwm1Apa106Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed800Kbps, NeoNrf52xPwm1> NeoNrf52xPwm1800KbpsMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed400Kbps, NeoNrf52xPwm1> NeoNrf52xPwm1400KbpsMethod;
|
||||
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2811, NeoNrf52xPwm2> NeoNrf52xPwm2Ws2811Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwm2> NeoNrf52xPwm2Ws2812xMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwm2> NeoNrf52xPwm2Ws2816Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedSk6812, NeoNrf52xPwm2> NeoNrf52xPwm2Sk6812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1814, NeoNrf52xPwm2> NeoNrf52xPwm2Tm1814Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1829, NeoNrf52xPwm2> NeoNrf52xPwm2Tm1829Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1914, NeoNrf52xPwm2> NeoNrf52xPwm2Tm1914Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTx1812, NeoNrf52xPwm2> NeoNrf52xPwm2Tx1812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedApa106, NeoNrf52xPwm2> NeoNrf52xPwm2Apa106Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed800Kbps, NeoNrf52xPwm2> NeoNrf52xPwm2800KbpsMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed400Kbps, NeoNrf52xPwm2> NeoNrf52xPwm2400KbpsMethod;
|
||||
|
||||
#if defined(NRF_PWM3)
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2811, NeoNrf52xPwm3> NeoNrf52xPwm3Ws2811Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwm3> NeoNrf52xPwm3Ws2812xMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwm3> NeoNrf52xPwm3Ws2816Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedSk6812, NeoNrf52xPwm3> NeoNrf52xPwm3Sk6812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1814, NeoNrf52xPwm3> NeoNrf52xPwm3Tm1814Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1829, NeoNrf52xPwm3> NeoNrf52xPwm3Tm1829Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1914, NeoNrf52xPwm3> NeoNrf52xPwm3Tm1914Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTx1812, NeoNrf52xPwm3> NeoNrf52xPwm3Tx1812Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedApa106, NeoNrf52xPwm3> NeoNrf52xPwm3Apa106Method;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed800Kbps, NeoNrf52xPwm3> NeoNrf52xPwm3800KbpsMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed400Kbps, NeoNrf52xPwm3> NeoNrf52xPwm3400KbpsMethod;
|
||||
#endif
|
||||
|
||||
// inverted
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2811, NeoNrf52xPwmN> NeoNrf52xPwmNWs2811InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwmN> NeoNrf52xPwmNWs2812xInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwmN> NeoNrf52xPwmNWs2816InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedSk6812, NeoNrf52xPwmN> NeoNrf52xPwmNSk6812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1814, NeoNrf52xPwmN> NeoNrf52xPwmNTm1814InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1829, NeoNrf52xPwmN> NeoNrf52xPwmNTm1829InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1914, NeoNrf52xPwmN> NeoNrf52xPwmNTm1914InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTx1812, NeoNrf52xPwmN> NeoNrf52xPwmNTx1812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedApa106, NeoNrf52xPwmN> NeoNrf52xPwmNApa106InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed800Kbps, NeoNrf52xPwmN> NeoNrf52xPwmN800KbpsInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed400Kbps, NeoNrf52xPwmN> NeoNrf52xPwmN400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2811, NeoNrf52xPwm0> NeoNrf52xPwm0Ws2811InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwm0> NeoNrf52xPwm0Ws2812xInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwm0> NeoNrf52xPwm0Ws2816InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedSk6812, NeoNrf52xPwm0> NeoNrf52xPwm0Sk6812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1814, NeoNrf52xPwm0> NeoNrf52xPwm0Tm1814InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1829, NeoNrf52xPwm0> NeoNrf52xPwm0Tm1829InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1914, NeoNrf52xPwm0> NeoNrf52xPwm0Tm1914InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTx1812, NeoNrf52xPwm0> NeoNrf52xPwm0Tx1812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedApa106, NeoNrf52xPwm0> NeoNrf52xPwm0Apa106InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed800Kbps, NeoNrf52xPwm0> NeoNrf52xPwm0800KbpsInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed400Kbps, NeoNrf52xPwm0> NeoNrf52xPwm0400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2811, NeoNrf52xPwm1> NeoNrf52xPwm1Ws2811InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwm1> NeoNrf52xPwm1Ws2812xInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwm1> NeoNrf52xPwm1Ws2816InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedSk6812, NeoNrf52xPwm1> NeoNrf52xPwm1Sk6812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1814, NeoNrf52xPwm1> NeoNrf52xPwm1Tm1814InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1829, NeoNrf52xPwm1> NeoNrf52xPwm1Tm1829InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1914, NeoNrf52xPwm1> NeoNrf52xPwm1Tm1914InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTx1812, NeoNrf52xPwm1> NeoNrf52xPwm1Tx1812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedApa106, NeoNrf52xPwm1> NeoNrf52xPwm1Apa106InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed800Kbps, NeoNrf52xPwm1> NeoNrf52xPwm1800KbpsInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed400Kbps, NeoNrf52xPwm1> NeoNrf52xPwm1400KbpsInvertedMethod;
|
||||
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2811, NeoNrf52xPwm2> NeoNrf52xPwm2Ws2811InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwm2> NeoNrf52xPwm2Ws2812xInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwm2> NeoNrf52xPwm2Ws2816InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedSk6812, NeoNrf52xPwm2> NeoNrf52xPwm2Sk6812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1814, NeoNrf52xPwm2> NeoNrf52xPwm2Tm1814InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1829, NeoNrf52xPwm2> NeoNrf52xPwm2Tm1829InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1914, NeoNrf52xPwm2> NeoNrf52xPwm2Tm1914InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTx1812, NeoNrf52xPwm2> NeoNrf52xPwm2Tx1812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedApa106, NeoNrf52xPwm2> NeoNrf52xPwm2Apa106InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed800Kbps, NeoNrf52xPwm2> NeoNrf52xPwm2800KbpsInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed400Kbps, NeoNrf52xPwm2> NeoNrf52xPwm2400KbpsInvertedMethod;
|
||||
|
||||
#if defined(NRF_PWM3)
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2811, NeoNrf52xPwm3> NeoNrf52xPwm3Ws2811InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwm3> NeoNrf52xPwm3Ws2812xInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwm3> NeoNrf52xPwm3Ws2816InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedSk6812, NeoNrf52xPwm3> NeoNrf52xPwm3Sk6812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1814, NeoNrf52xPwm3> NeoNrf52xPwm3Tm1814InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1829, NeoNrf52xPwm3> NeoNrf52xPwm3Tm1829InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1914, NeoNrf52xPwm3> NeoNrf52xPwm3Tm1914InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTx1812, NeoNrf52xPwm3> NeoNrf52xPwm3Tx1812InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedApa106, NeoNrf52xPwm3> NeoNrf52xPwm3Apa106InvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed800Kbps, NeoNrf52xPwm3> NeoNrf52xPwm3800KbpsInvertedMethod;
|
||||
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeed400Kbps, NeoNrf52xPwm3> NeoNrf52xPwm3400KbpsInvertedMethod;
|
||||
#endif
|
||||
|
||||
// defaults
|
||||
typedef NeoNrf52xPwm2Ws2812xMethod NeoWs2813Method;
|
||||
typedef NeoNrf52xPwm2Ws2812xMethod NeoWs2812xMethod;
|
||||
typedef NeoNrf52xPwm2800KbpsMethod NeoWs2812Method;
|
||||
typedef NeoNrf52xPwm2Ws2812xMethod NeoWs2811Method;
|
||||
typedef NeoNrf52xPwm2Ws2812xMethod NeoWs2816Method;
|
||||
typedef NeoNrf52xPwm2Sk6812Method NeoSk6812Method;
|
||||
typedef NeoNrf52xPwm2Tm1814Method NeoTm1814Method;
|
||||
typedef NeoNrf52xPwm2Tm1829Method NeoTm1829Method;
|
||||
typedef NeoNrf52xPwm2Tm1914Method NeoTm1914Method;
|
||||
typedef NeoNrf52xPwm2Tx1812Method NeoTx1812Method;
|
||||
typedef NeoNrf52xPwm2Sk6812Method NeoLc8812Method;
|
||||
typedef NeoNrf52xPwm2Apa106Method NeoApa106Method;
|
||||
|
||||
typedef NeoNrf52xPwm2Ws2812xMethod Neo800KbpsMethod;
|
||||
typedef NeoNrf52xPwm2400KbpsMethod Neo400KbpsMethod;
|
||||
|
||||
typedef NeoNrf52xPwm2Ws2812xInvertedMethod NeoWs2813InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Ws2812xInvertedMethod NeoWs2812xInvertedMethod;
|
||||
typedef NeoNrf52xPwm2Ws2812xInvertedMethod NeoWs2811InvertedMethod;
|
||||
typedef NeoNrf52xPwm2800KbpsInvertedMethod NeoWs2812InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Ws2812xInvertedMethod NeoWs2816InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Sk6812InvertedMethod NeoSk6812InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Tm1814InvertedMethod NeoTm1814InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Tm1829InvertedMethod NeoTm1829InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Tm1914InvertedMethod NeoTm1914InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Tx1812InvertedMethod NeoTx1812InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Sk6812InvertedMethod NeoLc8812InvertedMethod;
|
||||
typedef NeoNrf52xPwm2Apa106InvertedMethod NeoApa106InvertedMethod;
|
||||
|
||||
typedef NeoNrf52xPwm2Ws2812xInvertedMethod Neo800KbpsInvertedMethod;
|
||||
typedef NeoNrf52xPwm2400KbpsInvertedMethod Neo400KbpsInvertedMethod;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,717 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
Arduino library to control a wide variety of WS2811- and WS2812-based RGB
|
||||
LED devices such as Adafruit FLORA RGB Smart Pixels and NeoPixel strips.
|
||||
Currently handles 400 and 800 KHz bitstreams on 8, 12 and 16 MHz ATmega
|
||||
MCUs, with LEDs wired for various color orders. 8 MHz MCUs provide
|
||||
output on PORTB and PORTD, while 16 MHz chips can handle most output pins
|
||||
(possible exception with upper PORT registers on the Arduino Mega).
|
||||
|
||||
Written by Phil Burgess / Paint Your Dragon for Adafruit Industries,
|
||||
contributions by PJRC, Michael Miller and other members of the open
|
||||
source community.
|
||||
|
||||
Adafruit invests time and resources providing this open source code,
|
||||
please support Adafruit and open-source hardware by purchasing products
|
||||
from Adafruit!
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
The contents of this file were taken from the Adafruit NeoPixel library
|
||||
and modified only to fit within individual calling functions.
|
||||
|
||||
NeoPixel 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixel 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
|
||||
#if (defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)) && !defined(__arm__)
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// Hand-tuned assembly code issues data to the LED drivers at a specific
|
||||
// rate. There's separate code for different CPU speeds (8, 12, 16 MHz)
|
||||
// for both the WS2811 (400 KHz) and WS2812 (800 KHz) drivers. The
|
||||
// datastream timing for the LED drivers allows a little wiggle room each
|
||||
// way (listed in the datasheets), so the conditions for compiling each
|
||||
// case are set up for a range of frequencies rather than just the exact
|
||||
// 8, 12 or 16 MHz values, permitting use with some close-but-not-spot-on
|
||||
// devices (e.g. 16.5 MHz DigiSpark). The ranges were arrived at based
|
||||
// on the datasheet figures and have not been extensively tested outside
|
||||
// the canonical 8/12/16 MHz speeds; there's no guarantee these will work
|
||||
// close to the extremes (or possibly they could be pushed further).
|
||||
// Keep in mind only one CPU speed case actually gets compiled; the
|
||||
// resulting program isn't as massive as it might look from source here.
|
||||
|
||||
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
|
||||
|
||||
#ifdef PORTD // PORTD isn't present on ATtiny85, etc.
|
||||
void send_data_8mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask)
|
||||
{
|
||||
volatile size_t i = sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
volatile uint8_t n1;
|
||||
volatile uint8_t n2 = 0; // First, next bits out
|
||||
|
||||
// Squeezing an 800 KHz stream out of an 8 MHz chip requires code
|
||||
// specific to each PORT register. At present this is only written
|
||||
// to work with pins on PORTD or PORTB, the most likely use case --
|
||||
// this covers all the pins on the Adafruit Flora and the bulk of
|
||||
// digital pins on the Arduino Pro 8 MHz (keep in mind, this code
|
||||
// doesn't even get compiled for 16 MHz boards like the Uno, Mega,
|
||||
// Leonardo, etc., so don't bother extending this out of hand).
|
||||
// Additional PORTs could be added if you really need them, just
|
||||
// duplicate the else and loop and change the PORT. Each add'l
|
||||
// PORT will require about 150(ish) bytes of program space.
|
||||
|
||||
// 10 instruction clocks per bit: HHxxxxxLLL
|
||||
// OUT instructions: ^ ^ ^ (T=0,2,7)
|
||||
|
||||
hi = PORTD | pinMask;
|
||||
lo = PORTD & ~pinMask;
|
||||
n1 = lo;
|
||||
|
||||
if (b & 0x80)
|
||||
{
|
||||
n1 = hi;
|
||||
}
|
||||
|
||||
// Dirty trick: RJMPs proceeding to the next instruction are used
|
||||
// to delay two clock cycles in one instruction word (rather than
|
||||
// using two NOPs). This was necessary in order to squeeze the
|
||||
// loop down to exactly 64 words -- the maximum possible for a
|
||||
// relative branch.
|
||||
|
||||
asm volatile(
|
||||
"headD:" "\n\t" // Clk Pseudocode
|
||||
// Bit 7:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
|
||||
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
|
||||
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
"sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40)
|
||||
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
// Bit 6:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
|
||||
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
|
||||
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
"sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20)
|
||||
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
// Bit 5:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
|
||||
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
|
||||
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
"sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10)
|
||||
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
// Bit 4:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
|
||||
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
|
||||
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
"sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08)
|
||||
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
// Bit 3:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
|
||||
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
|
||||
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
"sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04)
|
||||
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
// Bit 2:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
|
||||
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
|
||||
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
"sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02)
|
||||
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
// Bit 1:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
|
||||
"mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
|
||||
"out %[port] , %[n1]" "\n\t" // 1 PORT = n1
|
||||
"rjmp .+0" "\n\t" // 2 nop nop
|
||||
"sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01)
|
||||
"mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
|
||||
"sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet)
|
||||
// Bit 0:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi
|
||||
"mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
|
||||
"out %[port] , %[n2]" "\n\t" // 1 PORT = n2
|
||||
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++
|
||||
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80)
|
||||
"mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo
|
||||
"brne headD" "\n" // 2 while(i) (Z flag set above)
|
||||
: [byte] "+r" (b),
|
||||
[n1] "+r" (n1),
|
||||
[n2] "+r" (n2),
|
||||
[count] "+w" (i)
|
||||
: [port] "I" (_SFR_IO_ADDR(PORTD)),
|
||||
[ptr] "e" (ptr),
|
||||
[hi] "r" (hi),
|
||||
[lo] "r" (lo) );
|
||||
}
|
||||
#endif
|
||||
|
||||
void send_data_8mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask)
|
||||
{
|
||||
volatile size_t i = sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
volatile uint8_t n1;
|
||||
volatile uint8_t n2 = 0; // First, next bits out
|
||||
|
||||
// Same as above, just switched to PORTB and stripped of comments.
|
||||
hi = PORTB | pinMask;
|
||||
lo = PORTB & ~pinMask;
|
||||
n1 = lo;
|
||||
if (b & 0x80)
|
||||
{
|
||||
n1 = hi;
|
||||
}
|
||||
|
||||
asm volatile(
|
||||
"headB:" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n2] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n1]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 6" "\n\t"
|
||||
"mov %[n2] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n1] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n2]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 5" "\n\t"
|
||||
"mov %[n1] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n2] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n1]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 4" "\n\t"
|
||||
"mov %[n2] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n1] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n2]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 3" "\n\t"
|
||||
"mov %[n1] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n2] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n1]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 2" "\n\t"
|
||||
"mov %[n2] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n1] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n2]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 1" "\n\t"
|
||||
"mov %[n1] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n2] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n1]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 0" "\n\t"
|
||||
"mov %[n2] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"sbiw %[count], 1" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n1] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n2]" "\n\t"
|
||||
"ld %[byte] , %a[ptr]+" "\n\t"
|
||||
"sbrc %[byte] , 7" "\n\t"
|
||||
"mov %[n1] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"brne headB" "\n"
|
||||
: [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i)
|
||||
: [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi),
|
||||
[lo] "r" (lo));
|
||||
}
|
||||
|
||||
void send_data_8mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
||||
{
|
||||
volatile size_t i = sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
// Timing is more relaxed; unrolling the inner loop for each bit is
|
||||
// not necessary. Still using the peculiar RJMPs as 2X NOPs, not out
|
||||
// of need but just to trim the code size down a little.
|
||||
// This 400-KHz-datastream-on-8-MHz-CPU code is not quite identical
|
||||
// to the 800-on-16 code later -- the hi/lo timing between WS2811 and
|
||||
// WS2812 is not simply a 2:1 scale!
|
||||
|
||||
// 20 inst. clocks per bit: HHHHxxxxxxLLLLLLLLLL
|
||||
// ST instructions: ^ ^ ^ (T=0,4,10)
|
||||
|
||||
volatile uint8_t next, bit;
|
||||
|
||||
hi = *port | pinMask;
|
||||
lo = *port & ~pinMask;
|
||||
next = lo;
|
||||
bit = 8;
|
||||
|
||||
asm volatile(
|
||||
"head20:" "\n\t" // Clk Pseudocode (T = 0)
|
||||
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
|
||||
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
|
||||
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
|
||||
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6)
|
||||
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7)
|
||||
"dec %[bit]" "\n\t" // 1 bit-- (T = 8)
|
||||
"breq nextbyte20" "\n\t" // 1-2 if(bit == 0)
|
||||
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
|
||||
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 14)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 16)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 18)
|
||||
"rjmp head20" "\n\t" // 2 -> head20 (next bit out)
|
||||
"nextbyte20:" "\n\t" // (T = 10)
|
||||
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12)
|
||||
"nop" "\n\t" // 1 nop (T = 13)
|
||||
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14)
|
||||
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16)
|
||||
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
|
||||
"brne head20" "\n" // 2 if(i != 0) -> (next byte)
|
||||
: [port] "+e" (port),
|
||||
[byte] "+r" (b),
|
||||
[bit] "+r" (bit),
|
||||
[next] "+r" (next),
|
||||
[count] "+w" (i)
|
||||
: [hi] "r" (hi),
|
||||
[lo] "r" (lo),
|
||||
[ptr] "e" (ptr));
|
||||
}
|
||||
|
||||
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
|
||||
|
||||
#ifdef PORTD // PORTD isn't present on ATtiny85, etc.
|
||||
void send_data_12mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask)
|
||||
{
|
||||
volatile size_t i = sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
// In the 12 MHz case, an optimized 800 KHz datastream (no dead time
|
||||
// between bytes) requires a PORT-specific loop similar to the 8 MHz
|
||||
// code (but a little more relaxed in this case).
|
||||
|
||||
// 15 instruction clocks per bit: HHHHxxxxxxLLLLL
|
||||
// OUT instructions: ^ ^ ^ (T=0,4,10)
|
||||
|
||||
volatile uint8_t next;
|
||||
|
||||
hi = PORTD | pinMask;
|
||||
lo = PORTD & ~pinMask;
|
||||
next = lo;
|
||||
if (b & 0x80) next = hi;
|
||||
|
||||
// Don't "optimize" the OUT calls into the bitTime subroutine;
|
||||
// we're exploiting the RCALL and RET as 3- and 4-cycle NOPs!
|
||||
asm volatile(
|
||||
"headD:" "\n\t" // (T = 0)
|
||||
"out %[port], %[hi]" "\n\t" // (T = 1)
|
||||
"rcall bitTimeD" "\n\t" // Bit 7 (T = 15)
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeD" "\n\t" // Bit 6
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeD" "\n\t" // Bit 5
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeD" "\n\t" // Bit 4
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeD" "\n\t" // Bit 3
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeD" "\n\t" // Bit 2
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeD" "\n\t" // Bit 1
|
||||
// Bit 0:
|
||||
"out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 3)
|
||||
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5)
|
||||
"out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6)
|
||||
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7)
|
||||
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8)
|
||||
"mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9)
|
||||
"nop" "\n\t" // 1 (T = 10)
|
||||
"out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11)
|
||||
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 13)
|
||||
"brne headD" "\n\t" // 2 if(i != 0) -> (next byte)
|
||||
"rjmp doneD" "\n\t"
|
||||
"bitTimeD:" "\n\t" // nop nop nop (T = 4)
|
||||
"out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5)
|
||||
"mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6)
|
||||
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7)
|
||||
"sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8)
|
||||
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9)
|
||||
"nop" "\n\t" // 1 (T = 10)
|
||||
"out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11)
|
||||
"ret" "\n\t" // 4 nop nop nop nop (T = 15)
|
||||
"doneD:" "\n"
|
||||
: [byte] "+r" (b),
|
||||
[next] "+r" (next),
|
||||
[count] "+w" (i)
|
||||
: [port] "I" (_SFR_IO_ADDR(PORTD)),
|
||||
[ptr] "e" (ptr),
|
||||
[hi] "r" (hi),
|
||||
[lo] "r" (lo));
|
||||
}
|
||||
#endif
|
||||
|
||||
void send_data_12mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask)
|
||||
{
|
||||
volatile uint16_t i = (uint16_t)sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
volatile uint8_t next;
|
||||
|
||||
hi = PORTB | pinMask;
|
||||
lo = PORTB & ~pinMask;
|
||||
next = lo;
|
||||
if (b & 0x80)
|
||||
{
|
||||
next = hi;
|
||||
}
|
||||
|
||||
// Same as above, just set for PORTB & stripped of comments
|
||||
asm volatile(
|
||||
"headB:" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"ld %[byte] , %a[ptr]+" "\n\t"
|
||||
"out %[port] , %[next]" "\n\t"
|
||||
"mov %[next] , %[lo]" "\n\t"
|
||||
"sbrc %[byte] , 7" "\n\t"
|
||||
"mov %[next] , %[hi]" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"sbiw %[count], 1" "\n\t"
|
||||
"brne headB" "\n\t"
|
||||
"rjmp doneB" "\n\t"
|
||||
"bitTimeB:" "\n\t"
|
||||
"out %[port], %[next]" "\n\t"
|
||||
"mov %[next], %[lo]" "\n\t"
|
||||
"rol %[byte]" "\n\t"
|
||||
"sbrc %[byte], 7" "\n\t"
|
||||
"mov %[next], %[hi]" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"out %[port], %[lo]" "\n\t"
|
||||
"ret" "\n\t"
|
||||
"doneB:" "\n"
|
||||
: [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i)
|
||||
: [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi),
|
||||
[lo] "r" (lo));
|
||||
}
|
||||
|
||||
void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
||||
{
|
||||
volatile uint16_t i = (uint16_t)sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
// 30 instruction clocks per bit: HHHHHHxxxxxxxxxLLLLLLLLLLLLLLL
|
||||
// ST instructions: ^ ^ ^ (T=0,6,15)
|
||||
|
||||
volatile uint8_t next, bit;
|
||||
|
||||
hi = *port | pinMask;
|
||||
lo = *port & ~pinMask;
|
||||
next = lo;
|
||||
bit = 8;
|
||||
|
||||
asm volatile(
|
||||
"head30:" "\n\t" // Clk Pseudocode (T = 0)
|
||||
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
|
||||
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
|
||||
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 6)
|
||||
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 10)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 12)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 14)
|
||||
"nop" "\n\t" // 1 nop (T = 15)
|
||||
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 19)
|
||||
"dec %[bit]" "\n\t" // 1 bit-- (T = 20)
|
||||
"breq nextbyte30" "\n\t" // 1-2 if(bit == 0)
|
||||
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 24)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 26)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 28)
|
||||
"rjmp head30" "\n\t" // 2 -> head30 (next bit out)
|
||||
"nextbyte30:" "\n\t" // (T = 22)
|
||||
"nop" "\n\t" // 1 nop (T = 23)
|
||||
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24)
|
||||
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26)
|
||||
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 28)
|
||||
"brne head30" "\n" // 1-2 if(i != 0) -> (next byte)
|
||||
: [port] "+e" (port),
|
||||
[byte] "+r" (b),
|
||||
[bit] "+r" (bit),
|
||||
[next] "+r" (next),
|
||||
[count] "+w" (i)
|
||||
: [hi] "r" (hi),
|
||||
[lo] "r" (lo),
|
||||
[ptr] "e" (ptr));
|
||||
}
|
||||
|
||||
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU
|
||||
|
||||
void send_data_16mhz_800(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
||||
{
|
||||
volatile uint16_t i = (uint16_t)sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
// WS2811 and WS2812 have different hi/lo duty cycles; this is
|
||||
// similar but NOT an exact copy of the prior 400-on-8 code.
|
||||
|
||||
// 20 inst. clocks per bit: HHHHHxxxxxxxxLLLLLLL
|
||||
// ST instructions: ^ ^ ^ (T=0,5,13)
|
||||
|
||||
volatile uint8_t next;
|
||||
volatile uint8_t bit;
|
||||
|
||||
hi = *port | pinMask;
|
||||
lo = *port & ~pinMask;
|
||||
next = lo;
|
||||
bit = 8;
|
||||
|
||||
asm volatile(
|
||||
"head20:" "\n\t" // Clk Pseudocode (T = 0)
|
||||
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
|
||||
"sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128)
|
||||
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
|
||||
"dec %[bit]" "\n\t" // 1 bit-- (T = 5)
|
||||
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7)
|
||||
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8)
|
||||
"breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above)
|
||||
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 12)
|
||||
"nop" "\n\t" // 1 nop (T = 13)
|
||||
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
|
||||
"nop" "\n\t" // 1 nop (T = 16)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 18)
|
||||
"rjmp head20" "\n\t" // 2 -> head20 (next bit out)
|
||||
"nextbyte20:" "\n\t" // (T = 10)
|
||||
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11)
|
||||
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13)
|
||||
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
|
||||
"nop" "\n\t" // 1 nop (T = 16)
|
||||
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
|
||||
"brne head20" "\n" // 2 if(i != 0) -> (next byte)
|
||||
: [port] "+e" (port),
|
||||
[byte] "+r" (b),
|
||||
[bit] "+r" (bit),
|
||||
[next] "+r" (next),
|
||||
[count] "+w" (i)
|
||||
: [ptr] "e" (ptr),
|
||||
[hi] "r" (hi),
|
||||
[lo] "r" (lo));
|
||||
}
|
||||
|
||||
void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
||||
{
|
||||
volatile size_t i = sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
// The 400 KHz clock on 16 MHz MCU is the most 'relaxed' version.
|
||||
|
||||
// 40 inst. clocks per bit: HHHHHHHHxxxxxxxxxxxxLLLLLLLLLLLLLLLLLLLL
|
||||
// ST instructions: ^ ^ ^ (T=0,8,20)
|
||||
|
||||
volatile uint8_t next, bit;
|
||||
|
||||
hi = *port | pinMask;
|
||||
lo = *port & ~pinMask;
|
||||
next = lo;
|
||||
bit = 8;
|
||||
|
||||
asm volatile(
|
||||
"head40:" "\n\t" // Clk Pseudocode (T = 0)
|
||||
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
|
||||
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
|
||||
"mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 6)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 8)
|
||||
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 12)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 14)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 16)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 18)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 20)
|
||||
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22)
|
||||
"nop" "\n\t" // 1 nop (T = 23)
|
||||
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24)
|
||||
"dec %[bit]" "\n\t" // 1 bit-- (T = 25)
|
||||
"breq nextbyte40" "\n\t" // 1-2 if(bit == 0)
|
||||
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27)
|
||||
"nop" "\n\t" // 1 nop (T = 28)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 30)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 32)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 34)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 36)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 38)
|
||||
"rjmp head40" "\n\t" // 2 -> head40 (next bit out)
|
||||
"nextbyte40:" "\n\t" // (T = 27)
|
||||
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28)
|
||||
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 32)
|
||||
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (T = 36)
|
||||
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 38)
|
||||
"brne head40" "\n" // 1-2 if(i != 0) -> (next byte)
|
||||
: [port] "+e" (port),
|
||||
[byte] "+r" (b),
|
||||
[bit] "+r" (bit),
|
||||
[next] "+r" (next),
|
||||
[count] "+w" (i)
|
||||
: [ptr] "e" (ptr),
|
||||
[hi] "r" (hi),
|
||||
[lo] "r" (lo));
|
||||
}
|
||||
|
||||
#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
|
||||
|
||||
void send_data_32mhz(uint8_t* data,
|
||||
size_t sizeData,
|
||||
volatile uint8_t* port,
|
||||
uint8_t pinMask,
|
||||
const uint8_t cycleTiming)
|
||||
{
|
||||
volatile uint16_t i = (uint16_t)sizeData; // Loop counter
|
||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||
volatile uint8_t b = *ptr++; // Current byte value
|
||||
volatile uint8_t hi; // PORT w/output bit set high
|
||||
volatile uint8_t lo; // PORT w/output bit set low
|
||||
|
||||
volatile uint8_t next;
|
||||
volatile uint8_t bit;
|
||||
volatile uint8_t cycle;
|
||||
volatile uint8_t cycleCount;
|
||||
|
||||
hi = *port | pinMask;
|
||||
lo = *port & ~pinMask;
|
||||
next = lo;
|
||||
bit = 8;
|
||||
cycleCount = cycleTiming;
|
||||
|
||||
asm volatile(
|
||||
"head20:" "\n\t" // Clk Pseudocode
|
||||
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi
|
||||
"sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128)
|
||||
"mov %[next], %[hi]" "\n\t" // 0-1 next = hi
|
||||
"mov %[cycle], %[cycleCount]" "\n\t" // 0-1 cycle = shortCycle
|
||||
"cycleLoop1:" "\n\t"
|
||||
"dec %[cycle]" "\n\t" // 1 cycle--
|
||||
"brne cycleLoop1" "\n\t" // 2 if(cycle != 0) -> (cycleLoop1)
|
||||
"st %a[port], %[next]" "\n\t" // 2 PORT = next
|
||||
"mov %[cycle], %[cycleCount]" "\n\t" // 0-1 cycle = shortCycle
|
||||
"cycleLoop2:" "\n\t"
|
||||
"dec %[cycle]" "\n\t" // 1 cycle--
|
||||
"brne cycleLoop2" "\n\t" // 2 if(cycle != 0) -> (cycleLoop2)
|
||||
"rjmp .+0" "\n\t" // 2 nop nop (timing tuning)
|
||||
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo
|
||||
"mov %[next] , %[lo]" "\n\t" // 1 next = lo
|
||||
"mov %[cycle], %[cycleCount]" "\n\t" // 0-1 cycle = shortCycle
|
||||
"cycleLoop3:" "\n\t"
|
||||
"dec %[cycle]" "\n\t" // 1 cycle--
|
||||
"brne cycleLoop3" "\n\t" // 2 if(cycle != 0) -> (cycleLoop3)
|
||||
"dec %[bit]" "\n\t" // 1 bit--
|
||||
"breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above)
|
||||
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 15)
|
||||
"rjmp head20" "\n\t" // 2 -> head20 (next bit out)
|
||||
"nextbyte20:" "\n\t" // (T = 15)
|
||||
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 16)
|
||||
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 18)
|
||||
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 38)
|
||||
"brne head20" "\n" // 2 if(i != 0) -> (next byte)
|
||||
// outputs
|
||||
: [port] "+e" (port),
|
||||
[byte] "+r" (b),
|
||||
[bit] "+r" (bit),
|
||||
[next] "+r" (next),
|
||||
[cycle] "+r" (cycle),
|
||||
[count] "+w" (i)
|
||||
// inputs
|
||||
: [ptr] "e" (ptr),
|
||||
[hi] "r" (hi),
|
||||
[lo] "r" (lo),
|
||||
[cycleCount] "r" (cycleCount));
|
||||
}
|
||||
|
||||
#else
|
||||
#error "CPU SPEED NOT SUPPORTED"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for P9813s using general Pins (APA102).
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
|
||||
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
|
||||
#include "TwoWireBitBangImpleAvr.h"
|
||||
#else
|
||||
#include "TwoWireBitBangImple.h"
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T_TWOWIRE> class P9813MethodBase
|
||||
{
|
||||
public:
|
||||
typedef typename T_TWOWIRE::SettingsObject SettingsObject;
|
||||
|
||||
P9813MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_sizeEndFrame((pixelCount + 15) / 16), // 16 = div 2 (bit for every two pixels) div 8 (bits to bytes)
|
||||
_wire(pinClock, pinData)
|
||||
{
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
P9813MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
P9813MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
~P9813MethodBase()
|
||||
{
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return true; // dot stars don't have a required delay
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
_wire.begin(sck, miso, mosi, ss);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
_wire.begin();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
const uint8_t startFrame[4] = { 0x00 };
|
||||
const uint8_t endFrame[4] = { 0x00 };
|
||||
|
||||
_wire.beginTransaction();
|
||||
|
||||
// start frame
|
||||
_wire.transmitBytes(startFrame, sizeof(startFrame));
|
||||
|
||||
// data
|
||||
_wire.transmitBytes(_data, _sizeData);
|
||||
|
||||
// end frame
|
||||
_wire.transmitBytes(endFrame, sizeof(endFrame));
|
||||
|
||||
_wire.endTransaction();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_wire.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
const size_t _sizeEndFrame;
|
||||
|
||||
T_TWOWIRE _wire;
|
||||
uint8_t* _data; // Holds LED color values
|
||||
};
|
||||
|
||||
typedef P9813MethodBase<TwoWireBitBangImple> P9813Method;
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
#include "TwoWireSpiImple.h"
|
||||
typedef P9813MethodBase<TwoWireSpiImple<SpiSpeed20Mhz>> P9813Spi20MhzMethod;
|
||||
typedef P9813MethodBase<TwoWireSpiImple<SpiSpeed10Mhz>> P9813Spi10MhzMethod;
|
||||
typedef P9813MethodBase<TwoWireSpiImple<SpiSpeed5Mhz>> P9813Spi5MhzMethod;
|
||||
typedef P9813MethodBase<TwoWireSpiImple<SpiSpeed2Mhz>> P9813Spi2MhzMethod;
|
||||
typedef P9813MethodBase<TwoWireSpiImple<SpiSpeed1Mhz>> P9813Spi1MhzMethod;
|
||||
typedef P9813MethodBase<TwoWireSpiImple<SpiSpeed500Khz>> P9813Spi500KhzMethod;
|
||||
|
||||
typedef P9813MethodBase<TwoWireSpiImple<SpiSpeedHz>> P9813SpiHzMethod;
|
||||
|
||||
typedef P9813Spi10MhzMethod P9813SpiMethod;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for SM16716 using general Pins
|
||||
|
||||
Written by Michael C. Miller.
|
||||
Contributed by Ivo H (ivoh95)
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
|
||||
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
|
||||
#include "TwoWireBitBangImpleAvr.h"
|
||||
#else
|
||||
#include "TwoWireBitBangImple.h"
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T_TWOWIRE> class Sm16716MethodBase
|
||||
{
|
||||
public:
|
||||
typedef typename T_TWOWIRE::SettingsObject SettingsObject;
|
||||
|
||||
Sm16716MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount* elementSize + settingsSize),
|
||||
_sizeFrame(6), // 48 bits
|
||||
_wire(pinClock, pinData)
|
||||
{
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
memset(_data, 0, _sizeData);
|
||||
}
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
Sm16716MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
Sm16716MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
~Sm16716MethodBase()
|
||||
{
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return true; // dot stars don't have a required delay
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// can't support hardware SPI due to weird extra bits
|
||||
//void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
//{
|
||||
// _wire.begin(sck, miso, mosi, ss);
|
||||
//}
|
||||
#endif
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
_wire.begin();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
_wire.beginTransaction();
|
||||
|
||||
// start frame
|
||||
for (size_t frameBytes = 0; frameBytes < _sizeFrame; frameBytes++)
|
||||
{
|
||||
_wire.transmitByte(0x00);
|
||||
}
|
||||
_wire.transmitBit(LOW);
|
||||
_wire.transmitBit(LOW); // two extra 0s to make the 50 0 header
|
||||
_wire.transmitBit(HIGH); // one to start the led frame
|
||||
|
||||
for (size_t pixel = 0; pixel < (_sizeData / 3); pixel++)
|
||||
{
|
||||
_wire.transmitByte(_data[pixel]);
|
||||
_wire.transmitByte(_data[pixel + 1]);
|
||||
_wire.transmitByte(_data[pixel + 2]);
|
||||
_wire.transmitBit(HIGH); //show the color and start the next frame
|
||||
}
|
||||
|
||||
_wire.endTransaction();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_wire.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
const size_t _sizeFrame;
|
||||
|
||||
T_TWOWIRE _wire;
|
||||
uint8_t* _data; // Holds LED color values
|
||||
};
|
||||
|
||||
// can ONLY support our bitbang for wire due to requirement for custom transmitBit method
|
||||
// to handle not byte oriented data stream
|
||||
//
|
||||
typedef Sm16716MethodBase<TwoWireBitBangImple> Sm16716Method;
|
||||
@@ -0,0 +1,220 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for Tlc5947 24 channel PWM controller using general Pins.
|
||||
|
||||
Written by Michael C. Miller.
|
||||
Written by Dennis Kasprzyk.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
|
||||
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
|
||||
#include "TwoWireBitBangImpleAvr.h"
|
||||
#else
|
||||
#include "TwoWireBitBangImple.h"
|
||||
#endif
|
||||
|
||||
#define TLC5947_MODULE_PWM_CHANNEL_COUNT 24
|
||||
|
||||
class Tlc5947Converter8Bit
|
||||
{
|
||||
public:
|
||||
static const size_t sizeChannel = 1;
|
||||
static void ConvertFrame(uint8_t* sendBufferPtr, uint8_t* channelPtr)
|
||||
{
|
||||
// Write 2 channels into 3 bytes scaling 8-bit to 12-bit per channel
|
||||
for (int indexChannel = 0; indexChannel < TLC5947_MODULE_PWM_CHANNEL_COUNT; indexChannel += 2)
|
||||
{
|
||||
uint8_t ch1 = *channelPtr--;
|
||||
uint8_t ch2 = *channelPtr--;
|
||||
|
||||
*sendBufferPtr++ = ch1;
|
||||
*sendBufferPtr++ = (ch1 & 0xf0) | (ch2 >> 4);
|
||||
*sendBufferPtr++ = ((ch2 << 4) & 0xf0) | (ch2 >> 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Tlc5947Converter16Bit
|
||||
{
|
||||
public:
|
||||
static const size_t sizeChannel = 2;
|
||||
static void ConvertFrame(uint8_t* sendBufferPtr, uint8_t* sourceBufferPtr)
|
||||
{
|
||||
uint16_t* channelPtr = (uint16_t*)sourceBufferPtr;
|
||||
|
||||
// Write 2 channels into 3 bytes using upper 12-bit of each channel
|
||||
for (int indexChannel = 0; indexChannel < TLC5947_MODULE_PWM_CHANNEL_COUNT; indexChannel += 2)
|
||||
{
|
||||
uint8_t ch1 = *channelPtr--;
|
||||
uint8_t ch2 = *channelPtr--;
|
||||
|
||||
*sendBufferPtr++ = ch1 >> 8;
|
||||
*sendBufferPtr++ = (ch1 & 0xf0) | (ch2 >> 12);
|
||||
*sendBufferPtr++ = ch2 >> 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T_BITCONVERT, typename T_TWOWIRE> class Tlc5947MethodBase
|
||||
{
|
||||
public:
|
||||
typedef typename T_TWOWIRE::SettingsObject SettingsObject;
|
||||
|
||||
// 24 channel * 12 bit
|
||||
static const size_t sizeSendBuffer = 36;
|
||||
|
||||
Tlc5947MethodBase(uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint8_t pinOutputEnable, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_countModule((pixelCount * elementSize + TLC5947_MODULE_PWM_CHANNEL_COUNT - 1) / TLC5947_MODULE_PWM_CHANNEL_COUNT),
|
||||
_sizeData(_countModule * TLC5947_MODULE_PWM_CHANNEL_COUNT + settingsSize),
|
||||
_wire(pinClock, pinData),
|
||||
_pinLatch(pinLatch),
|
||||
_pinOutputEnable(pinOutputEnable)
|
||||
{
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
pinMode(pinLatch, OUTPUT);
|
||||
pinMode(pinOutputEnable, OUTPUT);
|
||||
digitalWrite(pinOutputEnable, HIGH);
|
||||
}
|
||||
|
||||
Tlc5947MethodBase(uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
Tlc5947MethodBase(pinClock, pinData, pinLatch, -1, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
Tlc5947MethodBase(uint8_t pinLatch, uint8_t pinOutputEnable, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
Tlc5947MethodBase(SCK, MOSI, pinLatch, pinOutputEnable, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
|
||||
Tlc5947MethodBase(uint8_t pinLatch, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
Tlc5947MethodBase(SCK, MOSI, pinLatch, -1, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
~Tlc5947MethodBase()
|
||||
{
|
||||
free(_data);
|
||||
pinMode(_pinLatch, INPUT);
|
||||
pinMode(_pinOutputEnable, INPUT);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
return true; // dot stars don't have a required delay
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
_wire.begin(sck, miso, mosi, ss);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
_wire.begin();
|
||||
memset(_data, 0, _sizeData);
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
|
||||
digitalWrite(_pinOutputEnable, HIGH);
|
||||
|
||||
digitalWrite(_pinLatch, LOW);
|
||||
_wire.beginTransaction();
|
||||
|
||||
// We need to write the channels in reverse order. Get a Pointer to the last channel.
|
||||
uint8_t* lastChannelPtr = _data + ((_countModule * TLC5947_MODULE_PWM_CHANNEL_COUNT - 1) * T_BITCONVERT::sizeChannel);
|
||||
for (uint16_t countSend = 0; countSend < _countModule; countSend++)
|
||||
{
|
||||
// We pass a pointer to the last channel and ConvertFrame reads the channels backwards
|
||||
T_BITCONVERT::ConvertFrame(_sendBuffer, lastChannelPtr);
|
||||
_wire.transmitBytes(_sendBuffer, sizeSendBuffer);
|
||||
lastChannelPtr -= TLC5947_MODULE_PWM_CHANNEL_COUNT * T_BITCONVERT::sizeChannel;
|
||||
}
|
||||
|
||||
_wire.endTransaction();
|
||||
digitalWrite(_pinLatch, HIGH);
|
||||
digitalWrite(_pinLatch, LOW);
|
||||
digitalWrite(_pinOutputEnable, LOW);
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_wire.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
const uint16_t _countModule; // Number of tlc5947 modules
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
|
||||
T_TWOWIRE _wire;
|
||||
uint8_t* _data; // Holds LED color values
|
||||
uint8_t _sendBuffer[sizeSendBuffer]; // Holds channel values for one module
|
||||
uint8_t _pinLatch;
|
||||
uint8_t _pinOutputEnable;
|
||||
};
|
||||
|
||||
typedef Tlc5947MethodBase<Tlc5947Converter8Bit, TwoWireBitBangImple> Tlc5947Method;
|
||||
typedef Tlc5947MethodBase<Tlc5947Converter16Bit, TwoWireBitBangImple> Tlc5947Method16Bit;
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
#include "TwoWireSpiImple.h"
|
||||
|
||||
// for standalone
|
||||
typedef Tlc5947MethodBase<Tlc5947Converter8Bit, TwoWireSpiImple<SpiSpeed30Mhz>> Tlc5947Spi30MhzMethod;
|
||||
typedef Tlc5947MethodBase<Tlc5947Converter16Bit, TwoWireSpiImple<SpiSpeed30Mhz>> Tlc5947Spi30MhzMethod16Bit;
|
||||
|
||||
// for cascaded devices
|
||||
typedef Tlc5947MethodBase<Tlc5947Converter8Bit, TwoWireSpiImple<SpiSpeed15Mhz>> Tlc5947Spi15MhzMethod;
|
||||
typedef Tlc5947MethodBase<Tlc5947Converter16Bit, TwoWireSpiImple<SpiSpeed15Mhz>> Tlc5947Spi15MhzMethod16Bit;
|
||||
|
||||
typedef Tlc5947MethodBase<Tlc5947Converter8Bit, TwoWireSpiImple<SpiSpeed15Mhz>> Tlc5947SpiMethod;
|
||||
typedef Tlc5947MethodBase<Tlc5947Converter16Bit, TwoWireSpiImple<SpiSpeed15Mhz>> Tlc5947SpiMethod16Bit;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for DotStars using general Pins (APA102/LPD8806).
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
class TwoWireBitBangImple
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
TwoWireBitBangImple(uint8_t pinClock, uint8_t pinData) :
|
||||
_pinClock(pinClock),
|
||||
_pinData(pinData)
|
||||
{
|
||||
pinMode(pinClock, OUTPUT);
|
||||
pinMode(pinData, OUTPUT);
|
||||
}
|
||||
|
||||
~TwoWireBitBangImple()
|
||||
{
|
||||
pinMode(_pinClock, INPUT);
|
||||
pinMode(_pinData, INPUT);
|
||||
}
|
||||
|
||||
void begin()
|
||||
{
|
||||
digitalWrite(_pinClock, LOW);
|
||||
digitalWrite(_pinData, LOW);
|
||||
}
|
||||
|
||||
void beginTransaction()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void endTransaction()
|
||||
{
|
||||
digitalWrite(_pinData, LOW);
|
||||
}
|
||||
|
||||
void transmitBit(uint8_t bit)
|
||||
{
|
||||
// set data bit on pin
|
||||
digitalWrite(_pinData, bit);
|
||||
|
||||
// set clock high as data is ready
|
||||
digitalWrite(_pinClock, HIGH);
|
||||
|
||||
// set clock low as data pin is changed
|
||||
digitalWrite(_pinClock, LOW);
|
||||
}
|
||||
|
||||
void transmitByte(uint8_t data)
|
||||
{
|
||||
for (int bit = 7; bit >= 0; bit--)
|
||||
{
|
||||
// set data bit on pin
|
||||
digitalWrite(_pinData, (data & 0x80) == 0x80 ? HIGH : LOW);
|
||||
|
||||
// set clock high as data is ready
|
||||
digitalWrite(_pinClock, HIGH);
|
||||
|
||||
data <<= 1;
|
||||
|
||||
// set clock low as data pin is changed
|
||||
digitalWrite(_pinClock, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void transmitBytes(const uint8_t* data, size_t dataSize)
|
||||
{
|
||||
const uint8_t* endData = data + dataSize;
|
||||
while (data < endData)
|
||||
{
|
||||
transmitByte(*data++);
|
||||
}
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t _pinClock; // output pin number for clock line
|
||||
const uint8_t _pinData; // output pin number for data line
|
||||
};
|
||||
@@ -0,0 +1,140 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for DotStars using general Pins (APA102/LPD8806).
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
class TwoWireBitBangImple
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
|
||||
TwoWireBitBangImple(uint8_t pinClock, uint8_t pinData) :
|
||||
_pinClock(pinClock),
|
||||
_pinData(pinData)
|
||||
{
|
||||
pinMode(pinClock, OUTPUT);
|
||||
pinMode(pinData, OUTPUT);
|
||||
|
||||
_portClock = portOutputRegister(digitalPinToPort(_pinClock));
|
||||
_pinMaskClock = digitalPinToBitMask(_pinClock);
|
||||
_portData = portOutputRegister(digitalPinToPort(_pinData));
|
||||
_pinMaskData = digitalPinToBitMask(_pinData);
|
||||
}
|
||||
|
||||
~TwoWireBitBangImple()
|
||||
{
|
||||
pinMode(_pinClock, INPUT);
|
||||
pinMode(_pinData, INPUT);
|
||||
}
|
||||
|
||||
void begin()
|
||||
{
|
||||
digitalWrite(_pinClock, LOW);
|
||||
digitalWrite(_pinData, LOW);
|
||||
}
|
||||
|
||||
void beginTransaction()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void endTransaction()
|
||||
{
|
||||
digitalWrite(_pinData, LOW);
|
||||
}
|
||||
|
||||
void transmitBit(uint8_t bit)
|
||||
{
|
||||
// set data bit on pin
|
||||
// digitalWrite(_pinData, bit); // HIGH : LOW
|
||||
if (bit)
|
||||
{
|
||||
*_portData |= _pinMaskData;
|
||||
}
|
||||
else
|
||||
{
|
||||
*_portData &= ~_pinMaskData;
|
||||
}
|
||||
|
||||
// set clock high as data is ready
|
||||
// digitalWrite(_pinClock, HIGH);
|
||||
*_portClock |= _pinMaskClock;
|
||||
|
||||
// set clock low as data pin is changed
|
||||
// digitalWrite(_pinClock, LOW);
|
||||
*_portClock &= ~_pinMaskClock;
|
||||
}
|
||||
|
||||
void transmitByte(uint8_t data)
|
||||
{
|
||||
for (int bit = 7; bit >= 0; bit--)
|
||||
{
|
||||
// set data bit on pin
|
||||
// digitalWrite(_pinData, (data & 0x80) == 0x80 ? HIGH : LOW);
|
||||
if (data & 0x80)
|
||||
{
|
||||
*_portData |= _pinMaskData;
|
||||
}
|
||||
else
|
||||
{
|
||||
*_portData &= ~_pinMaskData;
|
||||
}
|
||||
|
||||
// set clock high as data is ready
|
||||
// digitalWrite(_pinClock, HIGH);
|
||||
*_portClock |= _pinMaskClock;
|
||||
|
||||
data <<= 1;
|
||||
|
||||
// set clock low as data pin is changed
|
||||
// digitalWrite(_pinClock, LOW);
|
||||
*_portClock &= ~_pinMaskClock;
|
||||
}
|
||||
}
|
||||
|
||||
void transmitBytes(const uint8_t* data, size_t dataSize)
|
||||
{
|
||||
const uint8_t* endData = data + dataSize;
|
||||
while (data < endData)
|
||||
{
|
||||
transmitByte(*data++);
|
||||
}
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t _pinClock; // output pin number for clock line
|
||||
const uint8_t _pinData; // output pin number for data line
|
||||
|
||||
volatile uint8_t* _portData; // Output PORT register
|
||||
uint8_t _pinMaskData; // Output PORT bitmask
|
||||
volatile uint8_t* _portClock; // Output PORT register
|
||||
uint8_t _pinMaskClock; // Output PORT bitmask
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for DotStars using ESP32's alternate SPI (HSPI) (APA102/LPD8806).
|
||||
|
||||
Written by Michael C. Miller.
|
||||
Minor changes adapting TwoWireSpiImple to support HSPI by Louis Beaudoin (Pixelvation)
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
template<typename T_SPISPEED> class TwoWireHspiImple
|
||||
{
|
||||
public:
|
||||
typedef typename T_SPISPEED::SettingsObject SettingsObject;
|
||||
|
||||
TwoWireHspiImple(uint8_t, uint8_t) // clock and data pins ignored for hardware SPI
|
||||
{
|
||||
_hspi = new SPIClass(HSPI);
|
||||
}
|
||||
|
||||
~TwoWireHspiImple()
|
||||
{
|
||||
_hspi->end();
|
||||
delete _hspi;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// for cases where hardware SPI can have pins changed
|
||||
void begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
_hspi->begin(sck, miso, mosi, ss);
|
||||
}
|
||||
#endif
|
||||
|
||||
void begin()
|
||||
{
|
||||
_hspi->begin();
|
||||
}
|
||||
|
||||
void beginTransaction()
|
||||
{
|
||||
_hspi->beginTransaction(SPISettings(_speed.Clock, MSBFIRST, SPI_MODE0));
|
||||
}
|
||||
|
||||
void endTransaction()
|
||||
{
|
||||
_hspi->endTransaction();
|
||||
}
|
||||
|
||||
void transmitByte(uint8_t data)
|
||||
{
|
||||
_hspi->transfer(data);
|
||||
}
|
||||
|
||||
void transmitBytes(const uint8_t* data, size_t dataSize)
|
||||
{
|
||||
// ESPs have a method to write without inplace overwriting the send buffer
|
||||
// since we don't care what gets received, use it for performance
|
||||
// FIX: but for what ever reason on Esp32, its not const
|
||||
_hspi->writeBytes(const_cast<uint8_t*>(data), dataSize);
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_speed.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
SPIClass * _hspi = NULL;
|
||||
T_SPISPEED _speed;
|
||||
};
|
||||
@@ -0,0 +1,225 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for DotStars using SPI (APA102/LPD8806).
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
class SpiSpeed40Mhz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed40Mhz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 40000000L;
|
||||
};
|
||||
|
||||
class SpiSpeed30Mhz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed30Mhz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 30000000L;
|
||||
};
|
||||
|
||||
class SpiSpeed20Mhz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed20Mhz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 20000000L;
|
||||
};
|
||||
|
||||
class SpiSpeed15Mhz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed15Mhz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 15000000L;
|
||||
};
|
||||
|
||||
class SpiSpeed10Mhz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed10Mhz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 10000000L;
|
||||
};
|
||||
|
||||
class SpiSpeed5Mhz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed5Mhz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 5000000L;
|
||||
};
|
||||
|
||||
class SpiSpeed2Mhz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed2Mhz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 2000000L;
|
||||
};
|
||||
|
||||
class SpiSpeed1Mhz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed1Mhz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 1000000L;
|
||||
};
|
||||
|
||||
class SpiSpeed500Khz
|
||||
{
|
||||
public:
|
||||
typedef NeoNoSettings SettingsObject;
|
||||
SpiSpeed500Khz() {};
|
||||
|
||||
static void applySettings([[maybe_unused]] const SettingsObject& settings) {}
|
||||
|
||||
static const uint32_t Clock = 500000L;
|
||||
};
|
||||
|
||||
class NeoSpiSettings
|
||||
{
|
||||
public:
|
||||
NeoSpiSettings(uint32_t clock) :
|
||||
Clock(clock)
|
||||
{
|
||||
}
|
||||
uint32_t Clock;
|
||||
};
|
||||
|
||||
class SpiSpeedHz
|
||||
{
|
||||
public:
|
||||
typedef NeoSpiSettings SettingsObject;
|
||||
|
||||
SpiSpeedHz() :
|
||||
Clock(10000000)
|
||||
{};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
Clock = settings.Clock;
|
||||
}
|
||||
|
||||
uint32_t Clock;
|
||||
};
|
||||
|
||||
template<typename T_SPISPEED> class TwoWireSpiImple
|
||||
{
|
||||
public:
|
||||
typedef typename T_SPISPEED::SettingsObject SettingsObject;
|
||||
|
||||
TwoWireSpiImple(uint8_t, uint8_t) // clock and data pins ignored for hardware SPI
|
||||
{
|
||||
}
|
||||
|
||||
~TwoWireSpiImple()
|
||||
{
|
||||
SPI.end();
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// for cases where hardware SPI can have pins changed
|
||||
void begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
SPI.begin(sck, miso, mosi, ss);
|
||||
}
|
||||
#endif
|
||||
|
||||
void begin()
|
||||
{
|
||||
SPI.begin();
|
||||
}
|
||||
|
||||
void beginTransaction()
|
||||
{
|
||||
SPI.beginTransaction(SPISettings(_speed.Clock, MSBFIRST, SPI_MODE0));
|
||||
}
|
||||
|
||||
void endTransaction()
|
||||
{
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
void transmitByte(uint8_t data)
|
||||
{
|
||||
SPI.transfer(data);
|
||||
}
|
||||
|
||||
void transmitBytes(const uint8_t* data, size_t dataSize)
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
// ESPs have a method to write without inplace overwriting the send buffer
|
||||
// since we don't care what gets received, use it for performance
|
||||
// FIX: but for what ever reason on Esp32, its not const
|
||||
SPI.writeBytes(const_cast<uint8_t*>(data), dataSize);
|
||||
|
||||
#else
|
||||
// default ARDUINO transfer inplace overwrites the send buffer
|
||||
// which is bad in this case, so we have to send one byte at a time
|
||||
const uint8_t* endData = data + dataSize;
|
||||
while (data < endData)
|
||||
{
|
||||
SPI.transfer(*data++);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_speed.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
T_SPISPEED _speed;
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
NeoPixel library helper functions for WS2801
|
||||
|
||||
Written by Michael C. Miller.
|
||||
|
||||
I invest time and resources providing this open source code,
|
||||
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
This file is part of the Makuna/NeoPixelBus library.
|
||||
|
||||
NeoPixelBus 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 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
NeoPixelBus 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 NeoPixel. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
|
||||
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
|
||||
#include "TwoWireBitBangImpleAvr.h"
|
||||
#else
|
||||
#include "TwoWireBitBangImple.h"
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T_TWOWIRE> class Ws2801MethodBase
|
||||
{
|
||||
public:
|
||||
typedef typename T_TWOWIRE::SettingsObject SettingsObject;
|
||||
|
||||
Ws2801MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
_sizeData(pixelCount * elementSize + settingsSize),
|
||||
_wire(pinClock, pinData)
|
||||
{
|
||||
_data = static_cast<uint8_t*>(malloc(_sizeData));
|
||||
// data cleared later in Begin()
|
||||
}
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
Ws2801MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||
Ws2801MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
~Ws2801MethodBase()
|
||||
{
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
{
|
||||
uint32_t delta = micros() - _endTime;
|
||||
|
||||
return (delta >= 500);
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
|
||||
{
|
||||
_wire.begin(sck, miso, mosi, ss);
|
||||
|
||||
_endTime = micros();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
_wire.begin();
|
||||
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
void Update(bool)
|
||||
{
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
#if !defined(ARDUINO_TEEONARDU_LEO) && !defined(ARDUINO_TEEONARDU_FLORA)
|
||||
yield(); // allows for system yield if needed
|
||||
#endif
|
||||
}
|
||||
|
||||
_wire.beginTransaction();
|
||||
|
||||
// data
|
||||
_wire.transmitBytes(_data, _sizeData);
|
||||
|
||||
_wire.endTransaction();
|
||||
|
||||
// save EOD time for latch on next call
|
||||
_endTime = micros();
|
||||
}
|
||||
|
||||
bool AlwaysUpdate()
|
||||
{
|
||||
// this method requires update to be called only if changes to buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* getData() const
|
||||
{
|
||||
return _data;
|
||||
};
|
||||
|
||||
size_t getDataSize() const
|
||||
{
|
||||
return _sizeData;
|
||||
};
|
||||
|
||||
void applySettings([[maybe_unused]] const SettingsObject& settings)
|
||||
{
|
||||
_wire.applySettings(settings);
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t _sizeData; // Size of '_data' buffer below
|
||||
|
||||
uint32_t _endTime; // Latch timing reference
|
||||
T_TWOWIRE _wire;
|
||||
uint8_t* _data; // Holds LED color values
|
||||
};
|
||||
|
||||
typedef Ws2801MethodBase<TwoWireBitBangImple> Ws2801Method;
|
||||
|
||||
#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
|
||||
#include "TwoWireSpiImple.h"
|
||||
typedef Ws2801MethodBase<TwoWireSpiImple<SpiSpeed20Mhz>> Ws2801Spi20MhzMethod;
|
||||
typedef Ws2801MethodBase<TwoWireSpiImple<SpiSpeed10Mhz>> Ws2801Spi10MhzMethod;
|
||||
typedef Ws2801MethodBase<TwoWireSpiImple<SpiSpeed5Mhz>> Ws2801Spi5MhzMethod;
|
||||
typedef Ws2801MethodBase<TwoWireSpiImple<SpiSpeed2Mhz>> Ws2801Spi2MhzMethod;
|
||||
typedef Ws2801MethodBase<TwoWireSpiImple<SpiSpeed1Mhz>> Ws2801Spi1MhzMethod;
|
||||
typedef Ws2801MethodBase<TwoWireSpiImple<SpiSpeed500Khz>> Ws2801Spi500KhzMethod;
|
||||
|
||||
typedef Ws2801MethodBase<TwoWireSpiImple<SpiSpeedHz>> Ws2801SpiHzMethod;
|
||||
|
||||
typedef Ws2801Spi10MhzMethod Ws2801SpiMethod;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user