Method File Cleanup (#683)

This commit is contained in:
Michael Miller authored and GitHub committed 2023-04-09 10:02:49 -07:00
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)
+176
View File
@@ -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
+671
View File
@@ -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)
+49
View File
@@ -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
+149
View File
@@ -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
+149
View File
@@ -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
+873
View File
@@ -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__)
+252
View File
@@ -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
+389
View File
@@ -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
+757
View File
@@ -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)
+331
View File
@@ -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
+941
View File
@@ -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
+435
View File
@@ -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
+564
View File
@@ -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)
+364
View File
@@ -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)
+683
View File
@@ -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
+717
View File
@@ -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
+145
View File
@@ -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
+138
View File
@@ -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;
+220
View File
@@ -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
+110
View File
@@ -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
};
+92
View File
@@ -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;
};
+225
View File
@@ -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;
};
+151
View File
@@ -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