Support x4 on each PIO
This commit is contained in:
Michael Miller
2024-03-11 09:36:13 -07:00
committed by GitHub
parent f700889c5c
commit 7c6744da4f
9 changed files with 1134 additions and 1 deletions

View File

@@ -0,0 +1,56 @@
//
// NeoPixel_RP2040_PioX4 -
// This sketch demonstrates the use of the PIO method allowing upto 4 instances
// Per one of the two PIO channels
//
// This example only works on the RP2040
//
// The key part of the method name is Rp2040Pio1X4,
// Rp2040 (platform specific method),
// PIO Channel 1 (most commonly available),
// X4 (4 instances allowed)
//
// In this example, it demonstrates different ColorFeatures, Method specification, and count per strip
//
#include <NeoPixelBus.h>
// Demonstrating the use of the four channels
NeoPixelBus<NeoBgrFeature, NeoRp2040Pio1X4Ws2811Method> strip1(120, 15); // note: older WS2811 and longer strip
NeoPixelBus<NeoGrbFeature, NeoRp2040Pio1X4Ws2812xMethod> strip2(100, 2); // note: modern WS2812 with letter like WS2812b
NeoPixelBus<NeoGrbFeature, NeoRp2040Pio1X4Ws2812xInvertedMethod> strip3(100, 4); // note: inverted
NeoPixelBus<NeoGrbwFeature, NeoRp2040Pio1X4Sk6812Method> strip4(50, 16); // note: RGBW and Sk6812 and smaller strip
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial attach
Serial.println();
Serial.println("Initializing...");
Serial.flush();
// must call begin on all the strips
strip1.Begin();
strip2.Begin();
strip3.Begin();
strip4.Begin();
Serial.println();
Serial.println("Running...");
}
void loop() {
delay(1000);
// draw on the strips
strip1.SetPixelColor(0, RgbColor(255, 0, 0)); // red
strip2.SetPixelColor(0, RgbColor(0, 127, 0)); // green
strip3.SetPixelColor(0, RgbColor(0, 0, 53)); // blue
strip4.SetPixelColor(0, RgbwColor(0, 0, 128, 255)); // white channel with a little blue
// show them,
// only on the last show, no matter the order, will the data be sent
strip1.Show();
strip2.Show();
strip3.Show();
strip4.Show();
}

View File

@@ -62,6 +62,10 @@ License along with NeoPixel. If not, see
#include "methods/NeoNrf52xMethod.h"
#elif defined(ARDUINO_ARCH_RP2040) // must be before __arm__
#include "methods/Rp2040/NeoRp2040x4Method.h"
#elif defined(__arm__) // must be before ARDUINO_ARCH_AVR due to Teensy incorrectly having it set
#include "methods/NeoArmMethod.h"

View File

@@ -30,7 +30,7 @@ License along with NeoPixel. If not, see
#pragma once
#if defined(__arm__) && !defined(ARDUINO_ARCH_NRF52840)
#if defined(__arm__) && !defined(ARDUINO_ARCH_NRF52840) && !defined(ARDUINO_ARCH_RP2040)
template<typename T_SPEED> class NeoArmMethodBase
{

View File

@@ -0,0 +1,175 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for RP2040.
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_RP2040
// DMA Finished State Tracking
// --------------------------------------------------------
enum Rp2040PioDmaState
{
Rp2040PioDmaState_Sending,
Rp2040PioDmaState_DmaCompleted,
Rp2040PioDmaState_FifoEmptied
};
template <uint V_IRQ_INDEX>
class NeoRp2040DmaState
{
public:
NeoRp2040DmaState() :
_endTime(0),
_state(Rp2040PioDmaState_FifoEmptied)
{
}
void SetSending()
{
_state = Rp2040PioDmaState_Sending;
}
void DmaFinished()
{
_endTime = micros();
_state = Rp2040PioDmaState_DmaCompleted;
}
bool IsReadyToSend(uint32_t resetTimeUs) const
{
bool isReady = false;
switch (_state)
{
case Rp2040PioDmaState_Sending:
break;
case Rp2040PioDmaState_DmaCompleted:
{
uint32_t delta = micros() - _endTime;
if (delta >= resetTimeUs)
{
// const method requires that we const cast to change state
*const_cast<Rp2040PioDmaState*>(&_state) = Rp2040PioDmaState_FifoEmptied;
isReady = true;
}
}
break;
default:
isReady = true;
break;
}
return isReady;
}
void Register(uint dmChannel)
{
if (s_dmaIrqObjectTable[dmChannel] == nullptr)
{
s_dmaIrqObjectTable[dmChannel] = this;
int32_t refCount = s_refCountHandler++;
if (refCount == 0)
{
// Set up end-of-DMA interrupt handler
irq_add_shared_handler(V_IRQ_INDEX ? DMA_IRQ_1 : DMA_IRQ_0,
dmaFinishIrq,
PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(V_IRQ_INDEX ? DMA_IRQ_1 : DMA_IRQ_0, true);
}
}
#if defined(NEORP2040_DEBUG)
Serial.print(" V_IRQ_INDEX = ");
Serial.print(V_IRQ_INDEX);
Serial.print(", s_dmaIrqObjectTable = ");
Serial.print((uint32_t)(s_dmaIrqObjectTable)); // address of s_dmaIrqObjectTable
Serial.println();
#endif
}
void Unregister(uint dmChannel)
{
if (s_dmaIrqObjectTable[dmChannel] == this)
{
int32_t refCount = s_refCountHandler--;
if (refCount == 0)
{
irq_set_enabled(V_IRQ_INDEX ? DMA_IRQ_1 : DMA_IRQ_0, false);
irq_remove_handler(V_IRQ_INDEX ? DMA_IRQ_1 : DMA_IRQ_0, dmaFinishIrq);
}
s_dmaIrqObjectTable[dmChannel] = nullptr;
}
}
private:
volatile uint32_t _endTime; // Latch/Reset timing reference
volatile Rp2040PioDmaState _state;
static NeoRp2040DmaState* s_dmaIrqObjectTable[NUM_DMA_CHANNELS];
static volatile int32_t s_refCountHandler;
static void dmaFinishIrq()
{
// dmaChannels are unique across both PIOs, while stateMachines are per
// PIO, so this current model below works even if both PIOs are used
for (uint dmaChannel = 0; dmaChannel < NUM_DMA_CHANNELS; dmaChannel++)
{
NeoRp2040DmaState* that = s_dmaIrqObjectTable[dmaChannel];
if (that != nullptr)
{
if (dma_irqn_get_channel_status(V_IRQ_INDEX, dmaChannel))
{
dma_irqn_acknowledge_channel(V_IRQ_INDEX, dmaChannel);
that->DmaFinished();
}
}
}
}
};
template <uint V_IRQ_INDEX>
NeoRp2040DmaState<V_IRQ_INDEX>* NeoRp2040DmaState<V_IRQ_INDEX>::s_dmaIrqObjectTable[NUM_DMA_CHANNELS] =
{
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
template <uint V_IRQ_INDEX>
volatile int32_t NeoRp2040DmaState<V_IRQ_INDEX>::s_refCountHandler = 0;
#endif

View File

@@ -0,0 +1,66 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for RP2040.
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_RP2040
// PIO Instances
// --------------------------------------------------------
class NeoRp2040PioInstance0
{
public:
NeoRp2040PioInstance0() :
Instance(pio0)
{};
const PIO Instance;
};
class NeoRp2040PioInstance1
{
public:
NeoRp2040PioInstance1() :
Instance(pio1)
{};
const PIO Instance;
};
// dynamic channel support
class NeoRp2040PioInstanceN
{
public:
NeoRp2040PioInstanceN(NeoBusChannel channel) :
Instance(channel == NeoBusChannel_0 ? pio0 : pio1)
{
}
NeoRp2040PioInstanceN() = delete; // no default constructor
const PIO Instance;
};
#endif

View File

@@ -0,0 +1,77 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for RP2040.
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 "../../NeoBusChannel.h"
#include "../../NeoSettings.h"
#include "NeoRp2040x4Method.h"
#ifdef ARDUINO_ARCH_RP2040
// See NeoRp2040PioCadenceMono3Step, NeoRp2040PioCadenceMono4Step in header for details
// on changing the PIO programs
//
// these are only required here in the .cpp due to current compiler version doesn't
// support doing this in the header file
//
//
const uint16_t NeoRp2040PioCadenceMono3Step::program_instructions[] =
{
// .wrap_target
0x6021, // 0: out x, 1 side 0
0x1023, // 1: jmp !x, 3 side 1
0x1000, // 2: jmp 0 side 1
0xa042, // 3: nop side 0
// .wrap
};
const struct pio_program NeoRp2040PioCadenceMono3Step::program =
{
.instructions = NeoRp2040PioCadenceMono3Step::program_instructions,
.length = 4,
.origin = -1,
};
const uint16_t NeoRp2040PioCadenceMono4Step::program_instructions[] =
{
// .wrap_target
0x6021, // 0: out x, 1 side 0
0x1023, // 1: jmp !x, 3 side 1
0x1100, // 2: jmp 0 side 1 [1]
0xa142, // 3: nop side 0 [1]
// .wrap
};
const struct pio_program NeoRp2040PioCadenceMono4Step::program =
{
.instructions = NeoRp2040PioCadenceMono4Step::program_instructions,
.length = 4,
.origin = -1,
};
#endif

View File

@@ -0,0 +1,211 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for RP2040.
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_RP2040
// PIO Programs (cadence)
// --------------------------------------------------------
// use https://wokwi.com/tools/pioasm
// copy relevant parts into NeoRp2040PioCadenceMono3Step and NeoRp2040PioCadenceMono4Step
//
// 3 step program
// the pulse width is divided by 3, using 33% for each stage
// where 0 = 33% and 1 = 66%
/*
.program rgbic_mono
.side_set 1
; 0 bit
; TH0 TH1 TL1
; +++ ___ ___
; 1 bit
; TH0 TH1 TL1
; +++ +++ ___
.define public TH0 1 ; T1
.define public TH1 1 ; T2
.define public TL1 1 ; T3
.wrap_target
bitloop:
out x, 1 side 0 [TL1 - 1] ; Side-set still takes place when instruction stalls
jmp !x do_zero side 1 [TH0 - 1] ; Branch on the bit we shifted out. Positive pulse
do_one:
jmp bitloop side 1 [TH1 - 1] ; Continue driving high, for a long pulse
do_zero:
nop side 0 [TH1 - 1] ; Or drive low, for a short pulse
.wrap
*/
//
class NeoRp2040PioCadenceMono3Step
{
protected:
static constexpr uint8_t wrap_target = 0;
static constexpr uint8_t wrap = 3;
static constexpr uint8_t TH0 = 1;
static constexpr uint8_t TH1 = 1;
static constexpr uint8_t TL1 = 1;
// changed from constexpr with initializtion due to
// that is only supported in c17+
static const uint16_t program_instructions[];
public:
// changed from constexpr with initializtion due to
// that is only supported in c17+
static const struct pio_program program;
static inline pio_sm_config get_default_config(uint offset)
{
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + wrap_target, offset + wrap);
sm_config_set_sideset(&c, 1, false, false);
return c;
}
static constexpr uint8_t bit_cycles = TH0 + TH1 + TL1;
};
// 4 step program
// the pulse width is divided by 4, using 25% for each stage
// where 0 = 25% and 1 = 75%
//
/*
.program rgbic_mono
.side_set 1
; 0 bit
; TH0 TH1 TL1
; +++ ___ ___
; 1 bit
; TH0 TH1 TL1
; +++ +++ ___
.define public TH0 1 ; T1
.define public TH1 2 ; T2
.define public TL1 1 ; T3
.wrap_target
bitloop:
out x, 1 side 0 [TL1 - 1] ; Side-set still takes place when instruction stalls
jmp !x do_zero side 1 [TH0 - 1] ; Branch on the bit we shifted out. Positive pulse
do_one:
jmp bitloop side 1 [TH1 - 1] ; Continue driving high, for a long pulse
do_zero:
nop side 0 [TH1 - 1] ; Or drive low, for a short pulse
.wrap
*/
//
class NeoRp2040PioCadenceMono4Step
{
protected:
static constexpr uint8_t wrap_target = 0;
static constexpr uint8_t wrap = 3;
static constexpr uint8_t TH0 = 1;
static constexpr uint8_t TH1 = 2;
static constexpr uint8_t TL1 = 1;
// changed from constexpr with initializtion due to
// that is only supported in c17+
static const uint16_t program_instructions[];
public:
// changed from constexpr with initializtion due to
// that is only supported in c17+
static const struct pio_program program;
static inline pio_sm_config get_default_config(uint offset)
{
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + wrap_target, offset + wrap);
sm_config_set_sideset(&c, 1, false, false);
return c;
}
static constexpr uint8_t bit_cycles = TH0 + TH1 + TL1;
};
// Program Wrapper
// --------------------------------------------------------
constexpr uint c_ProgramNotLoaded = static_cast<uint>(-1);
template<typename T_CADENCE>
class NeoRp2040PioMonoProgram
{
public:
static inline uint add(PIO pio_instance)
{
if (s_loadedOffset == c_ProgramNotLoaded)
{
assert(pio_can_add_program(pio_instance, &T_CADENCE::program));
s_loadedOffset = pio_add_program(pio_instance, &T_CADENCE::program);
}
return s_loadedOffset;
}
static inline void init(PIO pio_instance,
uint sm,
uint offset,
uint pin,
float bitrate,
uint shiftBits)
{
float div = clock_get_hz(clk_sys) / (bitrate * T_CADENCE::bit_cycles);
pio_sm_config c = T_CADENCE::get_default_config(offset);
sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, shiftBits);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
sm_config_set_clkdiv(&c, div);
// Set this pin's GPIO function (connect PIO to the pad)
pio_gpio_init(pio_instance, pin);
// Set the pin direction to output at the PIO
pio_sm_set_consecutive_pindirs(pio_instance, sm, pin, 1, true);
// Load our configuration, and jump to the start of the program
pio_sm_init(pio_instance, sm, offset, &c);
// Set the state machine running
pio_sm_set_enabled(pio_instance, sm, true);
}
private:
static uint s_loadedOffset; // singlet instance of loaded program
};
template<typename T_CADENCE>
uint NeoRp2040PioMonoProgram<T_CADENCE>::s_loadedOffset = c_ProgramNotLoaded;
#endif

View File

@@ -0,0 +1,113 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for RP2040.
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_RP2040
// Speeds
// --------------------------------------------------------
class NeoRp2040PioSpeedWs2811 : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono4Step>
{
public:
static constexpr float BitRateHz = 800000.0f; // 300+950
static constexpr uint32_t ResetTimeUs = 300;
};
class NeoRp2040PioSpeedWs2812x : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono3Step>
{
public:
static constexpr float BitRateHz = 800000.0f; // 400+850
static constexpr uint32_t ResetTimeUs = 300;
};
class NeoRp2040PioSpeedSk6812 : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono3Step>
{
public:
static constexpr float BitRateHz = 800000.0f; // 400+850
static constexpr uint32_t ResetTimeUs = 80;
};
// normal is inverted signal
class NeoRp2040PioSpeedTm1814 : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono3Step>
{
public:
static constexpr float BitRateHz = 800000.00f; // 360+890
static constexpr uint32_t ResetTimeUs = 200;
};
// normal is inverted signal
class NeoRp2040PioSpeedTm1829 : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono4Step>
{
public:
static constexpr float BitRateHz = 833333.33f; // 300+900
static constexpr uint32_t ResetTimeUs = 200;
};
// normal is inverted signal
class NeoRp2040PioSpeedTm1914 : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono3Step>
{
public:
static constexpr float BitRateHz = 800000.0f; // 360+890
static constexpr uint32_t ResetTimeUs = 200;
};
class NeoRp2040PioSpeed800Kbps : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono4Step>
{
public:
static constexpr float BitRateHz = 800000.0f; // 400+850
static constexpr uint32_t ResetTimeUs = 50;
};
class NeoRp2040PioSpeed400Kbps : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono3Step>
{
public:
static constexpr float BitRateHz = 400000.0f; // 800+1700
static constexpr uint32_t ResetTimeUs = 50;
};
class NeoRp2040PioSpeedApa106 : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono4Step>
{
public:
static constexpr float BitRateHz = 588235.29f; // 350+1350
static constexpr uint32_t ResetTimeUs = 50;
};
class NeoRp2040PioSpeedTx1812 : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono3Step>
{
public:
static constexpr float BitRateHz = 1111111.11f; // 300+600
static constexpr uint32_t ResetTimeUs = 80;
};
class NeoRp2040PioSpeedGs1903 : public NeoRp2040PioMonoProgram<NeoRp2040PioCadenceMono4Step>
{
public:
static constexpr float BitRateHz = 833333.33f; // 300+900
static constexpr uint32_t ResetTimeUs = 40;
};
#endif

View File

@@ -0,0 +1,431 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for RP2040.
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_RP2040
//#define NEORP2040_DEBUG
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "pico/mutex.h"
#include "NeoRp2040DmaState.h"
#include "NeoRp2040PioMonoProgram.h"
#include "NeoRp2040PioInstance.h"
#include "NeoRp2040PioSpeed.h"
// Method
// --------------------------------------------------------
template<typename T_SPEED,
typename T_PIO_INSTANCE,
bool V_INVERT = false,
uint V_IRQ_INDEX = 1>
class NeoRp2040x4MethodBase
{
public:
typedef NeoNoSettings SettingsObject;
NeoRp2040x4MethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
_sizeData(pixelCount * elementSize + settingsSize),
_pin(pin),
_mergedFifoCount((_pio.Instance->dbg_cfginfo & PIO_DBG_CFGINFO_FIFO_DEPTH_BITS) * 2) // merged TX / RX FIFO buffer in words
{
construct();
}
NeoRp2040x4MethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
_sizeData(pixelCount* elementSize + settingsSize),
_pin(pin),
_pio(channel),
_mergedFifoCount((_pio.Instance->dbg_cfginfo& PIO_DBG_CFGINFO_FIFO_DEPTH_BITS) * 2) // merged TX / RX FIFO buffer in words
{
construct();
}
~NeoRp2040x4MethodBase()
{
// wait for last send
while (!IsReadyToUpdate())
{
yield();
}
// clear any remaining just to be extra sure
pio_sm_clear_fifos(_pio.Instance, _sm);
// disable the state machine
pio_sm_set_enabled(_pio.Instance, _sm, false);
// Disable and remove interrupts
// dma_channel_cleanup(_dmaChannel); // NOT PRESENT?!
dma_irqn_set_channel_enabled(V_IRQ_INDEX, _dmaChannel, false);
// unregister static dma callback object
_dmaState.Unregister(_dmaChannel);
// unclaim dma channel and then state machine
dma_channel_unclaim(_dmaChannel);
pio_sm_unclaim(_pio.Instance, _sm);
pinMode(_pin, INPUT);
free(_dataEditing);
free(_dataSending);
}
bool IsReadyToUpdate() const
{
return _dmaState.IsReadyToSend(T_SPEED::ResetTimeUs + _fifoCacheEmptyDelta);
}
void Initialize()
{
// Select the largest FIFO fetch size that aligns with our data size
// BUT, since RP2040 is little endian, if the source element size is
// 8 bits, then the larger shift bits accounts for endianess
// and will mangle the order thinking its source was a 16/32 bits
// must you use channel_config_set_bswap() to address this, see below
uint fifoWordBits = 8; // size of a FIFO word in bits
if (_sizeData % 4 == 0)
{
// data is 4 byte aligned in size,
// use a 32 bit fifo word for effeciency
fifoWordBits = 32;
}
else if (_sizeData % 2 == 0)
{
// data is 2 byte aligned in size,
// use a 16 bit fifo word for effeciency
fifoWordBits = 16;
}
// calc the two related values from fifoWordBits
dma_channel_transfer_size dmaTransferSize = static_cast<dma_channel_transfer_size>(fifoWordBits / 16);
uint dmaTransferCount = _sizeData / (fifoWordBits / 8);;
// IRQ triggers on DMA buffer finished,
// not the FIFO buffer finished sending,
// so we calc this delta so it can be added to the reset time
//
// 1000000.0f / T_SPEED::BitRateHz = us to send one bit
float bitLengthUs = 1000000.0f / T_SPEED::BitRateHz;
// _mergedFifoCount is merged TX/RX FIFO buffer in words
// Add another word for any IRQ trigger latency (error) as
// too short is catastrophic and too long is fine
_fifoCacheEmptyDelta = bitLengthUs * fifoWordBits * (_mergedFifoCount + 1);
#if defined(NEORP2040_DEBUG)
Serial.print(", _pio.Instance = ");
Serial.print((_pio.Instance == pio1));
Serial.print(", _sizeData = ");
Serial.print(_sizeData);
Serial.print(", dmaTransferSize = ");
Serial.print(dmaTransferSize);
Serial.print(", dmaTransferCount = ");
Serial.print(dmaTransferCount);
Serial.print(", fifoWordBits = ");
Serial.print(fifoWordBits);
Serial.print(", _mergedFifoCount = ");
Serial.print(_mergedFifoCount);
Serial.print(", _fifoCacheEmptyDelta = ");
Serial.print(_fifoCacheEmptyDelta);
#endif
// Our assembled program needs to be loaded into this PIO's instruction
// memory. This SDK function will find a location (offset) in the
// instruction memory where there is enough space for our program. We need
//
uint offset = T_SPEED::add(_pio.Instance);
#if defined(NEORP2040_DEBUG)
Serial.println();
Serial.print("offset = ");
Serial.print(offset);
#endif
// Find a free state machine on our chosen PIO.
_sm = pio_claim_unused_sm(_pio.Instance, true); // panic if none available
#if defined(NEORP2040_DEBUG)
Serial.print(", _sm = ");
Serial.print(_sm);
#endif
// Configure it to run our program, and start it, using the
// helper function we included in our .pio file.
T_SPEED::init(_pio.Instance,
_sm,
offset,
_pin,
T_SPEED::BitRateHz,
fifoWordBits);
// invert output if needed
if (V_INVERT)
{
gpio_set_outover(_pin, GPIO_OVERRIDE_INVERT);
}
// find a free dma channel
_dmaChannel = dma_claim_unused_channel(true); // panic if none available
#if defined(NEORP2040_DEBUG)
Serial.print(", *_dmaChannel = ");
Serial.print(_dmaChannel);
Serial.println();
#endif
// register for IRQ shared static endTime updates
_dmaState.Register(_dmaChannel);
// Set up DMA transfer
dma_channel_config dmaConfig = dma_channel_get_default_config(_dmaChannel);
channel_config_set_transfer_data_size(&dmaConfig, dmaTransferSize);
channel_config_set_read_increment(&dmaConfig, true);
channel_config_set_write_increment(&dmaConfig, false);
// source is byte data stream, even with 16/32 transfer size
channel_config_set_bswap(&dmaConfig, true);
// Set DMA trigger
channel_config_set_dreq(&dmaConfig, pio_get_dreq(_pio.Instance, _sm, true));
dma_channel_configure(_dmaChannel,
&dmaConfig,
&(_pio.Instance->txf[_sm]), // dest
_dataSending, // src
dmaTransferCount,
false);
dma_irqn_set_channel_enabled(V_IRQ_INDEX, _dmaChannel, true);
}
void Update(bool maintainBufferConsistency)
{
// wait for last send
while (!IsReadyToUpdate())
{
yield();
}
_dmaState.SetSending();
// start next send
//
dma_channel_set_read_addr(_dmaChannel, _dataEditing, false);
dma_channel_start(_dmaChannel); // Start new transfer
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_PIO_INSTANCE _pio; // holds instance for multi channel support
const uint8_t _mergedFifoCount;
NeoRp2040DmaState<V_IRQ_INDEX> _dmaState; // Latch timing reference
uint32_t _fifoCacheEmptyDelta; // delta between dma IRQ finished and PIO Fifo empty
// 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 DMA
// holds pio state
int _sm;
int _dmaChannel;
void construct()
{
_dataEditing = static_cast<uint8_t*>(malloc(_sizeData));
// data cleared later in Begin() with a ClearTo(0)
_dataSending = static_cast<uint8_t*>(malloc(_sizeData));
// no need to initialize it, it gets overwritten on every send
}
};
// normal
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2811, NeoRp2040PioInstanceN> Rp2040x4NWs2811Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstanceN> Rp2040x4NWs2812xMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstanceN> Rp2040x4NWs2816Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedSk6812, NeoRp2040PioInstanceN> Rp2040x4NSk6812Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1814, NeoRp2040PioInstanceN, true> Rp2040x4NTm1814Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1829, NeoRp2040PioInstanceN, true> Rp2040x4NTm1829Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1914, NeoRp2040PioInstanceN, true> Rp2040x4NTm1914Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedApa106, NeoRp2040PioInstanceN> Rp2040x4NApa106Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTx1812, NeoRp2040PioInstanceN> Rp2040x4NTx1812Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedGs1903, NeoRp2040PioInstanceN> Rp2040x4NGs1903Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed800Kbps, NeoRp2040PioInstanceN> Rp2040x4N800KbpsMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed400Kbps, NeoRp2040PioInstanceN> Rp2040x4N400KbpsMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2811, NeoRp2040PioInstance0> Rp2040x4Pio0Ws2811Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstance0> Rp2040x4Pio0Ws2812xMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstance0> Rp2040x4Pio0Ws2816Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedSk6812, NeoRp2040PioInstance0> Rp2040x4Pio0Sk6812Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1814, NeoRp2040PioInstance0, true> Rp2040x4Pio0Tm1814Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1829, NeoRp2040PioInstance0, true> Rp2040x4Pio0Tm1829Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1914, NeoRp2040PioInstance0, true> Rp2040x4Pio0Tm1914Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedApa106, NeoRp2040PioInstance0> Rp2040x4Pio0Apa106Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTx1812, NeoRp2040PioInstance0> Rp2040x4Pio0Tx1812Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedGs1903, NeoRp2040PioInstance0> Rp2040x4Pio0Gs1903Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed800Kbps, NeoRp2040PioInstance0> Rp2040x4Pio0800KbpsMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed400Kbps, NeoRp2040PioInstance0> Rp2040x4Pio0400KbpsMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2811, NeoRp2040PioInstance1> Rp2040x4Pio1Ws2811Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstance1> Rp2040x4Pio1Ws2812xMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstance1> Rp2040x4Pio1Ws2816Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedSk6812, NeoRp2040PioInstance1> Rp2040x4Pio1Sk6812Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1814, NeoRp2040PioInstance1, true> Rp2040x4Pio1Tm1814Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1829, NeoRp2040PioInstance1, true> Rp2040x4Pio1Tm1829Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1914, NeoRp2040PioInstance1, true> Rp2040x4Pio1Tm1914Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedApa106, NeoRp2040PioInstance1> Rp2040x4Pio1Apa106Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTx1812, NeoRp2040PioInstance1> Rp2040x4Pio1Tx1812Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedGs1903, NeoRp2040PioInstance1> Rp2040x4Pio1Gs1903Method;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed800Kbps, NeoRp2040PioInstance1> Rp2040x4Pio1800KbpsMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed400Kbps, NeoRp2040PioInstance1> Rp2040x4Pio1400KbpsMethod;
// inverted
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2811, NeoRp2040PioInstanceN, true> Rp2040x4NWs2811InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstanceN, true> Rp2040x4NWs2812xInvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstanceN, true> Rp2040x4NWs2816InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedSk6812, NeoRp2040PioInstanceN, true> Rp2040x4NSk6812InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1814, NeoRp2040PioInstanceN> Rp2040x4NTm1814InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1829, NeoRp2040PioInstanceN> Rp2040x4NTm1829InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1914, NeoRp2040PioInstanceN> Rp2040x4NTm1914InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedApa106, NeoRp2040PioInstanceN, true> Rp2040x4NApa106InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTx1812, NeoRp2040PioInstanceN, true> Rp2040x4NTx1812InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedGs1903, NeoRp2040PioInstanceN, true> Rp2040x4NGs1903InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed800Kbps, NeoRp2040PioInstanceN, true> Rp2040x4N800KbpsInvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed400Kbps, NeoRp2040PioInstanceN, true> Rp2040x4N400KbpsInvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2811, NeoRp2040PioInstance0, true> Rp2040x4Pio0Ws2811InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstance0, true> Rp2040x4Pio0Ws2812xInvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstance0, true> Rp2040x4Pio0Ws2816InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedSk6812, NeoRp2040PioInstance0, true> Rp2040x4Pio0Sk6812InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1814, NeoRp2040PioInstance0> Rp2040x4Pio0Tm1814InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1829, NeoRp2040PioInstance0> Rp2040x4Pio0Tm1829InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1914, NeoRp2040PioInstance0> Rp2040x4Pio0Tm1914InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedApa106, NeoRp2040PioInstance0, true> Rp2040x4Pio0Apa106InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTx1812, NeoRp2040PioInstance0, true> Rp2040x4Pio0Tx1812InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedGs1903, NeoRp2040PioInstance0, true> Rp2040x4Pio0Gs1903InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed800Kbps, NeoRp2040PioInstance0, true> Rp2040x4Pio0800KbpsInvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed400Kbps, NeoRp2040PioInstance0, true> Rp2040x4Pio0400KbpsInvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2811, NeoRp2040PioInstance1, true> Rp2040x4Pio1Ws2811InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstance1, true> Rp2040x4Pio1Ws2812xInvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedWs2812x, NeoRp2040PioInstance1, true> Rp2040x4Pio1Ws2816InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedSk6812, NeoRp2040PioInstance1, true> Rp2040x4Pio1Sk6812InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1814, NeoRp2040PioInstance1> Rp2040x4Pio1Tm1814InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1829, NeoRp2040PioInstance1> Rp2040x4Pio1Tm1829InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTm1914, NeoRp2040PioInstance1> Rp2040x4Pio1Tm1914InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedApa106, NeoRp2040PioInstance1, true> Rp2040x4Pio1Apa106InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedTx1812, NeoRp2040PioInstance1, true> Rp2040x4Pio1Tx1812InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeedGs1903, NeoRp2040PioInstance1, true> Rp2040x4Pio1Gs1903InvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed800Kbps, NeoRp2040PioInstance1, true> Rp2040x4Pio1800KbpsInvertedMethod;
typedef NeoRp2040x4MethodBase<NeoRp2040PioSpeed400Kbps, NeoRp2040PioInstance1, true> Rp2040x4Pio1400KbpsInvertedMethod;
// PIO 1 method is the default method, and still x4 instances
typedef Rp2040x4Pio1Ws2812xMethod NeoWs2813Method;
typedef Rp2040x4Pio1Ws2812xMethod NeoWs2812xMethod;
typedef Rp2040x4Pio1800KbpsMethod NeoWs2812Method;
typedef Rp2040x4Pio1Ws2812xMethod NeoWs2811Method;
typedef Rp2040x4Pio1Ws2812xMethod NeoWs2816Method;
typedef Rp2040x4Pio1Sk6812Method NeoSk6812Method;
typedef Rp2040x4Pio1Tm1814Method NeoTm1814Method;
typedef Rp2040x4Pio1Tm1829Method NeoTm1829Method;
typedef Rp2040x4Pio1Tm1914Method NeoTm1914Method;
typedef Rp2040x4Pio1Sk6812Method NeoLc8812Method;
typedef Rp2040x4Pio1Apa106Method NeoApa106Method;
typedef Rp2040x4Pio1Tx1812Method NeoTx1812Method;
typedef Rp2040x4Pio1Gs1903Method NeoGs1903Method;
typedef Rp2040x4Pio1Ws2812xMethod Neo800KbpsMethod;
typedef Rp2040x4Pio1400KbpsMethod Neo400KbpsMethod;
typedef Rp2040x4Pio1Ws2812xInvertedMethod NeoWs2813InvertedMethod;
typedef Rp2040x4Pio1Ws2812xInvertedMethod NeoWs2812xInvertedMethod;
typedef Rp2040x4Pio1Ws2812xInvertedMethod NeoWs2811InvertedMethod;
typedef Rp2040x4Pio1800KbpsInvertedMethod NeoWs2812InvertedMethod;
typedef Rp2040x4Pio1Ws2812xInvertedMethod NeoWs2816InvertedMethod;
typedef Rp2040x4Pio1Sk6812InvertedMethod NeoSk6812InvertedMethod;
typedef Rp2040x4Pio1Tm1814InvertedMethod NeoTm1814InvertedMethod;
typedef Rp2040x4Pio1Tm1829InvertedMethod NeoTm1829InvertedMethod;
typedef Rp2040x4Pio1Tm1914InvertedMethod NeoTm1914InvertedMethod;
typedef Rp2040x4Pio1Sk6812InvertedMethod NeoLc8812InvertedMethod;
typedef Rp2040x4Pio1Apa106InvertedMethod NeoApa106InvertedMethod;
typedef Rp2040x4Pio1Tx1812InvertedMethod NeoTx1812InvertedMethod;
typedef Rp2040x4Pio1Gs1903InvertedMethod NeoGs1903InvertedMethod;
typedef Rp2040x4Pio1Ws2812xInvertedMethod Neo800KbpsInvertedMethod;
typedef Rp2040x4Pio1400KbpsInvertedMethod Neo400KbpsInvertedMethod;
#endif