Dynamic channels (#411)

This commit is contained in:
Michael Miller
2021-01-24 15:44:13 -08:00
committed by GitHub
parent fc6ae8ed18
commit ed3eeb1f1b
6 changed files with 229 additions and 48 deletions

View File

@@ -188,6 +188,13 @@ NeoEsp32I2s1Tm1814InvertedMethod KEYWORD1
NeoEsp32I2s1800KbpsInvertedMethod KEYWORD1
NeoEsp32I2s1400KbpsInvertedMethod KEYWORD1
NeoEsp32I2s1Apa106InvertedMethod KEYWORD1
NeoEsp32RmtNWs2811Method KEYWORD1
NeoEsp32RmtNWs2812xMethod KEYWORD1
NeoEsp32RmtNSk6812Method KEYWORD1
NeoEsp32RmtNTm1814Method KEYWORD1
NeoEsp32RmtNApa106Method KEYWORD1
NeoEsp32RmtN800KbpsMethod KEYWORD1
NeoEsp32RmtN400KbpsMethod KEYWORD1
NeoEsp32Rmt0Ws2811Method KEYWORD1
NeoEsp32Rmt0Ws2812xMethod KEYWORD1
NeoEsp32Rmt0Sk6812Method KEYWORD1
@@ -244,6 +251,13 @@ NeoEsp32Rmt7Tm1814Method KEYWORD1
NeoEsp32Rmt7Apa106Method KEYWORD1
NeoEsp32Rmt7800KbpsMethod KEYWORD1
NeoEsp32Rmt7400KbpsMethod KEYWORD1
NeoEsp32RmtNWs2811InvertedMethod KEYWORD1
NeoEsp32RmtNWs2812xInvertedMethod KEYWORD1
NeoEsp32RmtNSk6812InvertedMethod KEYWORD1
NeoEsp32RmtNTm1814InvertedMethod KEYWORD1
NeoEsp32RmtNApa106InvertedMethod KEYWORD1
NeoEsp32RmtN800KbpsInvertedMethod KEYWORD1
NeoEsp32RmtN400KbpsInvertedMethod KEYWORD1
NeoEsp32Rmt0Ws2811InvertedMethod KEYWORD1
NeoEsp32Rmt0Ws2812xInvertedMethod KEYWORD1
NeoEsp32Rmt0Sk6812InvertedMethod KEYWORD1
@@ -320,6 +334,12 @@ NeoEsp32BitBangLc8812InvertedMethod KEYWORD1
NeoEsp32BitBangApa106InvertedMethod KEYWORD1
NeoEsp32BitBang800KbpsInvertedMethod KEYWORD1
NeoEsp32BitBang400KbpsInvertedMethod KEYWORD1
NeoNrf52xPwmNWs2812xMethod KEYWORD1
NeoNrf52xPwmNSk6812Method KEYWORD1
NeoNrf52xPwmNTm1814Method KEYWORD1
NeoNrf52xPwmN800KbpsMethod KEYWORD1
NeoNrf52xPwmN400KbpsMethod KEYWORD1
NeoNrf52xPwmNApa106Method KEYWORD1
NeoNrf52xPwm0Ws2812xMethod KEYWORD1
NeoNrf52xPwm0Sk6812Method KEYWORD1
NeoNrf52xPwm0Tm1814Method KEYWORD1
@@ -344,6 +364,12 @@ NeoNrf52xPwm3Tm1814Method KEYWORD1
NeoNrf52xPwm3800KbpsMethod KEYWORD1
NeoNrf52xPwm3400KbpsMethod KEYWORD1
NeoNrf52xPwm3Apa106Method KEYWORD1
NeoNrf52xPwmNWs2812xInvertedMethod KEYWORD1
NeoNrf52xPwmNSk6812InvertedMethod KEYWORD1
NeoNrf52xPwmNTm1814InvertedMethod KEYWORD1
NeoNrf52xPwmN800KbpsInvertedMethod KEYWORD1
NeoNrf52xPwmN400KbpsInvertedMethod KEYWORD1
NeoNrf52xPwmNApa106InvertedMethod KEYWORD1
NeoNrf52xPwm0Ws2812xInvertedMethod KEYWORD1
NeoNrf52xPwm0Sk6812InvertedMethod KEYWORD1
NeoNrf52xPwm0Tm1814InvertedMethod KEYWORD1

View File

@@ -85,6 +85,8 @@ License along with NeoPixel. If not, see
#include "internal/NeoEase.h"
#include "internal/NeoGamma.h"
#include "internal/NeoBusChannel.h"
#include "internal/DotStarGenericMethod.h"
#include "internal/Lpd8806GenericMethod.h"
#include "internal/Lpd6803GenericMethod.h"
@@ -120,8 +122,6 @@ License along with NeoPixel. If not, see
#endif
template<typename T_COLOR_FEATURE, typename T_METHOD> class NeoPixelBus
{
public:
@@ -135,6 +135,13 @@ public:
{
}
NeoPixelBus(uint16_t countPixels, uint8_t pin, NeoBusChannel channel) :
_countPixels(countPixels),
_state(0),
_method(pin, countPixels, T_COLOR_FEATURE::PixelSize, T_COLOR_FEATURE::SettingsSize, channel)
{
}
NeoPixelBus(uint16_t countPixels, uint8_t pinClock, uint8_t pinData) :
_countPixels(countPixels),
_state(0),

View File

@@ -0,0 +1,29 @@
#pragma once
// For those platforms/methods that support dynamic channel setting
enum NeoBusChannel
{
NeoBusChannel_0,
NeoBusChannel_1,
NeoBusChannel_2,
// NRF52x has only 3 or 4 channels of PWM
#if defined(ARDUINO_ARCH_NRF52840)
#if defined(NRF_PWM3)
NeoBusChannel_3
#endif
// ESP32 has either 8 or 4 channels (S2 has only 4)
#elif defined(ARDUINO_ARCH_ESP32)
NeoBusChannel_3,
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
NeoBusChannel_4,
NeoBusChannel_5,
NeoBusChannel_6,
NeoBusChannel_7,
#endif // CONFIG_IDF_TARGET_ESP32S2
#endif // ARDUINO_ARCH_ESP32
};

View File

@@ -27,6 +27,7 @@ License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "NeoBusChannel.h"
#include "NeoEsp32RmtMethod.h"
#ifdef ARDUINO_ARCH_ESP32

View File

@@ -322,24 +322,32 @@ public:
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;
};
@@ -348,29 +356,50 @@ public:
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:
@@ -378,20 +407,24 @@ public:
_sizeData(pixelCount * elementSize + settingsSize),
_pin(pin)
{
_dataEditing = static_cast<uint8_t*>(malloc(_sizeData));
memset(_dataEditing, 0x00, _sizeData);
construct();
}
_dataSending = static_cast<uint8_t*>(malloc(_sizeData));
// no need to initialize it, it gets overwritten on every send
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(T_CHANNEL::RmtChannelNumber, 10000 / portTICK_PERIOD_MS));
ESP_ERROR_CHECK_WITHOUT_ABORT(rmt_wait_tx_done(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS));
ESP_ERROR_CHECK(rmt_driver_uninstall(T_CHANNEL::RmtChannelNumber));
ESP_ERROR_CHECK(rmt_driver_uninstall(_channel.RmtChannelNumber));
free(_dataEditing);
free(_dataSending);
@@ -400,7 +433,7 @@ public:
bool IsReadyToUpdate() const
{
return (ESP_OK == rmt_wait_tx_done(T_CHANNEL::RmtChannelNumber, 0));
return (ESP_OK == rmt_wait_tx_done(_channel.RmtChannelNumber, 0));
}
void Initialize()
@@ -408,7 +441,7 @@ public:
rmt_config_t config;
config.rmt_mode = RMT_MODE_TX;
config.channel = T_CHANNEL::RmtChannelNumber;
config.channel = _channel.RmtChannelNumber;
config.gpio_num = static_cast<gpio_num_t>(_pin);
config.mem_block_num = 1;
config.tx_config.loop_en = false;
@@ -422,8 +455,8 @@ public:
config.clk_div = T_SPEED::RmtClockDivider;
ESP_ERROR_CHECK(rmt_config(&config));
ESP_ERROR_CHECK(rmt_driver_install(T_CHANNEL::RmtChannelNumber, 0, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1));
ESP_ERROR_CHECK(rmt_translator_init(T_CHANNEL::RmtChannelNumber, T_SPEED::Translate));
ESP_ERROR_CHECK(rmt_driver_install(_channel.RmtChannelNumber, 0, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1));
ESP_ERROR_CHECK(rmt_translator_init(_channel.RmtChannelNumber, T_SPEED::Translate));
}
void Update(bool maintainBufferConsistency)
@@ -431,10 +464,10 @@ public:
// 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(T_CHANNEL::RmtChannelNumber, 10000 / portTICK_PERIOD_MS)))
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(T_CHANNEL::RmtChannelNumber, _dataEditing, _sizeData, false));
ESP_ERROR_CHECK_WITHOUT_ABORT(rmt_write_sample(_channel.RmtChannelNumber, _dataEditing, _sizeData, false));
if (maintainBufferConsistency)
{
@@ -462,13 +495,32 @@ public:
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));
memset(_dataEditing, 0x00, _sizeData);
_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<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtNSk6812Method;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1814Method;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtNApa106Method;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtN800KbpsMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtN400KbpsMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2811Method;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2812xMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32Rmt0Sk6812Method;
@@ -539,6 +591,14 @@ typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel7> NeoE
#endif
// inverted
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtNWs2811InvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtNWs2812xInvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtNSk6812InvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1814InvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtNApa106InvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtN800KbpsInvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtN400KbpsInvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2811InvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32Rmt0Ws2812xInvertedMethod;
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32Rmt0Sk6812InvertedMethod;

View File

@@ -229,6 +229,34 @@ public:
};
#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:
@@ -236,13 +264,15 @@ public:
_sizeData(pixelCount * elementSize + settingsSize),
_pin(pin)
{
pinMode(pin, OUTPUT);
construct();
}
_data = static_cast<uint8_t*>(malloc(_sizeData));
memset(_data, 0, _sizeData);
_dmaBufferSize = c_dmaBytesPerDataByte * _sizeData + sizeof(nrf_pwm_values_common_t);
_dmaBuffer = static_cast<nrf_pwm_values_common_t*>(malloc(_dmaBufferSize));
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()
@@ -262,7 +292,7 @@ public:
bool IsReadyToUpdate() const
{
return (T_BUS::Pwm()->EVENTS_STOPPED);
return (_bus.Pwm()->EVENTS_STOPPED);
}
void Initialize()
@@ -307,50 +337,62 @@ public:
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));
memset(_data, 0, _sizeData);
_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
T_BUS::Pwm()->PSEL.OUT[0] = digitalPinToPinName(_pin);
T_BUS::Pwm()->PSEL.OUT[1] = NC;
T_BUS::Pwm()->PSEL.OUT[2] = NC;
T_BUS::Pwm()->PSEL.OUT[3] = NC;
_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;
T_BUS::Pwm()->ENABLE = 1;
T_BUS::Pwm()->MODE = NRF_PWM_MODE_UP;
T_BUS::Pwm()->PRESCALER = NRF_PWM_CLK_16MHz;
T_BUS::Pwm()->COUNTERTOP = T_SPEED::CountTop;
T_BUS::Pwm()->LOOP = 1; // single fire so events get set
T_BUS::Pwm()->DECODER = NRF_PWM_LOAD_COMMON;
_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
T_BUS::Pwm()->SEQ[0].PTR = reinterpret_cast<uint32_t>(_dmaBuffer);
T_BUS::Pwm()->SEQ[0].CNT = _dmaBufferSize / sizeof(nrf_pwm_values_common_t);
T_BUS::Pwm()->SEQ[0].REFRESH = 0; // ignored
T_BUS::Pwm()->SEQ[0].ENDDELAY = T_SPEED::CountReset; // ignored still?
_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
T_BUS::Pwm()->SEQ[1].PTR = reinterpret_cast<uint32_t>(_dmaBuffer + (T_BUS::Pwm()->SEQ[0].CNT - 1));
T_BUS::Pwm()->SEQ[1].CNT = 1;
T_BUS::Pwm()->SEQ[1].REFRESH = 0; // ignored
T_BUS::Pwm()->SEQ[1].ENDDELAY = 0; // ignored
_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
T_BUS::Pwm()->SHORTS = PWM_SHORTS_LOOPSDONE_STOP_Msk;
T_BUS::Pwm()->INTEN = 0;
_bus.Pwm()->SHORTS = PWM_SHORTS_LOOPSDONE_STOP_Msk;
_bus.Pwm()->INTEN = 0;
dmaResetEvents();
}
void dmaDeinit()
{
T_BUS::Pwm()->ENABLE = 0;
T_BUS::Pwm()->PSEL.OUT[0] = NC;
_bus.Pwm()->ENABLE = 0;
_bus.Pwm()->PSEL.OUT[0] = NC;
}
void FillBuffer()
@@ -380,20 +422,28 @@ private:
void dmaResetEvents()
{
T_BUS::Pwm()->EVENTS_LOOPSDONE = 0;
T_BUS::Pwm()->EVENTS_SEQEND[0] = 0;
T_BUS::Pwm()->EVENTS_SEQEND[1] = 0;
T_BUS::Pwm()->EVENTS_STOPPED = 0;
_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();
T_BUS::Pwm()->TASKS_SEQSTART[0] = 1;
_bus.Pwm()->TASKS_SEQSTART[0] = 1;
}
};
// normal
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2811, NeoNrf52xPwmN> NeoNrf52xPwmNWs2811Method;
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedWs2812x, NeoNrf52xPwmN> NeoNrf52xPwmNWs2812xMethod;
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedSk6812, NeoNrf52xPwmN> NeoNrf52xPwmNSk6812Method;
typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeedTm1814, NeoNrf52xPwmN> NeoNrf52xPwmNTm1814Method;
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<NeoNrf52xPwmSpeedSk6812, NeoNrf52xPwm0> NeoNrf52xPwm0Sk6812Method;
@@ -429,6 +479,14 @@ typedef NeoNrf52xMethodBase<NeoNrf52xPwmSpeed400Kbps, NeoNrf52xPwm3> NeoNrf52xPw
#endif
// inverted
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2811, NeoNrf52xPwmN> NeoNrf52xPwmNWs2811InvertedMethod;
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedWs2812x, NeoNrf52xPwmN> NeoNrf52xPwmNWs2812xInvertedMethod;
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedSk6812, NeoNrf52xPwmN> NeoNrf52xPwmNSk6812InvertedMethod;
typedef NeoNrf52xMethodBase<NeoNrf52xPwmInvertedSpeedTm1814, NeoNrf52xPwmN> NeoNrf52xPwmNTm1814InvertedMethod;
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<NeoNrf52xPwmInvertedSpeedSk6812, NeoNrf52xPwm0> NeoNrf52xPwm0Sk6812InvertedMethod;