Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion wled00/FX.h
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,7 @@ class WS2812FX {
inline bool isUpdating() const { return !BusManager::canAllShow(); } // return true if the strip is being sent pixel updates
inline bool isServicing() const { return _isServicing; } // returns true if strip.service() is executing
inline bool hasWhiteChannel() const { return _hasWhiteChannel; } // returns true if strip contains separate white chanel
inline bool isOffRefreshRequired() const { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814 chipset)
inline bool isOffRefreshRequired() const { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814/TM1815 chipset)
inline bool isSuspended() const { return _suspend; } // returns true if strip.service() execution is suspended
inline bool needsUpdate() const { return _triggered; } // returns true if strip received a trigger() request

Expand Down
126 changes: 126 additions & 0 deletions wled00/bus_customNPBtiming.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Custom NeoPixelBus timing definitions for WLED
*/

#pragma once
#ifndef BusCustomNPBtiming_h
#define BusCustomNPBtiming_h

#include <Arduino.h>
#include "NeoPixelBus.h"

#ifdef ARDUINO_ARCH_ESP32
// RMT driver selection, includes needed for custom RMT types
#if !defined(WLED_USE_SHARED_RMT) && !defined(__riscv)
#include <NeoEsp32RmtHIMethod.h>
#define USE_NEOPIXELBUS_RMT_HI
#else
#include "internal/methods/NeoEsp32RmtMethod.h"
#endif
#endif

////////////////////////////////
// TM1815 timing and methods //
////////////////////////////////

// TM1815 is exactly half the speed of TM1814, other features are identical so can use TM1814 as a base class
// normal timing pulses are inverted compared to WS281x (low pulse timing, high idle/reset)
#ifdef ARDUINO_ARCH_ESP32
// RMT timing
class NeoEsp32RmtSpeedTm1815 : public NeoEsp32RmtInvertedSpeedBase
{
public:
const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(740, 1780);
const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1440, 1060);
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);
};

void NeoEsp32RmtSpeedTm1815::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);
}
Comment on lines +45 to +54
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add IRAM_ATTR to the function definition.

The IRAM_ATTR attribute is present on the declaration (line 37) but missing from the definition. For ESP-IDF, the attribute should be on the definition to ensure the function is placed in IRAM for interrupt-safe execution.

🔎 Proposed fix
-void NeoEsp32RmtSpeedTm1815::Translate(const void* src,
+void IRAM_ATTR NeoEsp32RmtSpeedTm1815::Translate(const void* src,
   rmt_item32_t* dest,
   size_t src_size,
   size_t wanted_num,
   size_t* translated_size,
   size_t* item_num)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void NeoEsp32RmtSpeedTm1815::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 IRAM_ATTR NeoEsp32RmtSpeedTm1815::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);
}
🤖 Prompt for AI Agents
In wled00/bus_customNPBtiming.h around lines 45 to 54, the function definition
for NeoEsp32RmtSpeedTm1815::Translate is missing the IRAM_ATTR attribute present
on its declaration; update the definition to include IRAM_ATTR (i.e., add
IRAM_ATTR to the function signature so it matches the declaration) so the
routine is placed in IRAM for interrupt-safe execution, rebuild to confirm no
mismatched declaration/definition warnings.

#else // ESP8266
class NeoEspBitBangSpeedTm1815
{
public:
const static uint32_t T0H = (F_CPU / 5833332 - CYCLES_LOOPTEST); // 0.7us
const static uint32_t T1H = (F_CPU / 3333332 - CYCLES_LOOPTEST); // 1.5us
const static uint32_t Period = (F_CPU / 1600000 - CYCLES_LOOPTEST); // 2.5us per bit

static const uint32_t ResetTimeUs = 200;
const static uint32_t TLatch = (F_CPU / 20000 - CYCLES_LOOPTEST); // 200us, be generous
};

class NeoEsp8266UartSpeedTm1815 // values taken from "400kHz" speed bus but extended reset time (just like TM1814 does with 800kHz timings)
{
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; // 400khz output, 4 serial bytes per NeoByte
static const uint32_t ResetTimeUs = 200; // use between data send bursts to reset for next update
};

class NeoWrgbTm1815Feature :
public Neo4ByteFeature<ColorIndexW, ColorIndexR, ColorIndexG, ColorIndexB>,
public NeoElementsTm1814Settings<ColorIndexW, ColorIndexR, ColorIndexG, ColorIndexB>
{
};
#endif
Comment on lines +75 to +80
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

NeoWrgbTm1815Feature is only defined for ESP8266 but is needed for ESP32 as well.

The NeoWrgbTm1815Feature class is inside the #else // ESP8266 block, so it won't be compiled for ESP32. However, ESP32 also needs this feature class to instantiate complete TM1815 methods (combining method + feature in bus_wrapper.h). Move this class definition outside the platform-specific block.

🔎 Proposed fix

Move NeoWrgbTm1815Feature outside the #ifdef ARDUINO_ARCH_ESP32 ... #else ... #endif block so it's available for both platforms:

 };
 #endif

+class NeoWrgbTm1815Feature :
+  public Neo4ByteFeature<ColorIndexW, ColorIndexR, ColorIndexG, ColorIndexB>,
+  public NeoElementsTm1814Settings<ColorIndexW, ColorIndexR, ColorIndexG, ColorIndexB>
+{
+};
+
 // I2S / DMA custom timing
 class NeoBitsSpeedTm1815 : public NeoBitsSpeedBase

And remove the class from inside the ESP8266 block (lines 75-79).

🤖 Prompt for AI Agents
In wled00/bus_customNPBtiming.h around lines 75-80, the NeoWrgbTm1815Feature
class is currently defined only inside the ESP8266-specific branch which
prevents ESP32 from compiling it; move the NeoWrgbTm1815Feature class definition
out of the #ifdef ARDUINO_ARCH_ESP32 / #else / #endif block so it is declared
unconditionally for both platforms, and remove the duplicate definition from the
ESP8266 branch to avoid redeclaration.


// I2S / DMA custom timing
class NeoBitsSpeedTm1815 : public NeoBitsSpeedBase
{
public:
const static uint16_t BitSendTimeNs = 2500;
const static uint16_t ResetTimeUs = 200;
};

// TM1815 methods
#ifdef ARDUINO_ARCH_ESP32
// ESP32 RMT Methods
#ifdef USE_NEOPIXELBUS_RMT_HI
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1815, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1815Method;
#else
typedef NeoEsp32RmtMethodBase<NeoEsp32RmtSpeedTm1815, NeoEsp32RmtChannelN> NeoEsp32RmtNTm1815Method;
#endif
// ESP32 I2S Methods
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
#if defined(CONFIG_IDF_TARGET_ESP32S3)
typedef NeoEsp32LcdXMethodBase<NeoBitsSpeedTm1815, NeoEsp32LcdMux8Bus, NeoBitsInverted> NeoEsp32LcdX8Tm1815Method; // S3 only
#else // ESP32 classic & S2
typedef NeoEsp32I2sMethodBase<NeoBitsSpeedTm1815, NeoEsp32I2sBusZero, NeoBitsInverted, NeoEsp32I2sCadence> NeoEsp32I2s0Tm1815Method;
typedef NeoEsp32I2sXMethodBase<NeoBitsSpeedTm1815, NeoEsp32I2s0Mux8Bus, NeoBitsInverted> NeoEsp32I2s0X8Tm1815Method; // used by S2, not used by classic ESP32
#if defined(CONFIG_IDF_TARGET_ESP32)
typedef NeoEsp32I2sMethodBase<NeoBitsSpeedTm1815, NeoEsp32I2sBusOne, NeoBitsInverted, NeoEsp32I2sCadence> NeoEsp32I2s1Tm1815Method;
typedef NeoEsp32I2sXMethodBase<NeoBitsSpeedTm1815, NeoEsp32I2s1Mux8Bus, NeoBitsInverted> NeoEsp32I2s1X8Tm1815Method; // available on classic ESP32 only
#endif
#endif
// I2S Aliases
#if defined(CONFIG_IDF_TARGET_ESP32S3)
typedef NeoEsp32LcdX8Tm1815Method X8Tm1815Method;
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
typedef NeoEsp32I2s0X8Tm1815Method X8Tm1815Method;
#else // ESP32 classic
typedef NeoEsp32I2s1X8Tm1815Method X8Tm1815Method;
#endif
#endif // !CONFIG_IDF_TARGET_ESP32C3
#else // ESP8266
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1815, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart0Tm1815Method;
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedTm1815, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>, NeoEsp8266UartInverted> NeoEsp8266Uart1Tm1815Method;
typedef NeoEsp8266DmaMethodBase<NeoEsp8266I2sCadence<NeoEsp8266DmaInvertedPattern>,NeoBitsSpeedTm1815> NeoEsp8266DmaTm1815Method;
typedef NeoEspBitBangMethodBase<NeoEspBitBangSpeedTm1815, NeoEspInverted, true> NeoEsp8266BitBangTm1815Method;
#endif

#endif // BusCustomNPBtiming_h
3 changes: 2 additions & 1 deletion wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) const {


BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814))
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814 || bc.type == TYPE_TM1815)) // TM1814/15 need refresh or they fall-back to a "demo mode"
, _skip(bc.skipAmount) //sacrificial pixels
, _colorOrder(bc.colorOrder)
, _milliAmpsPerLed(bc.milliAmpsPerLed)
Expand Down Expand Up @@ -372,6 +372,7 @@ std::vector<LEDType> BusDigital::getLEDTypes() {
{TYPE_WS2812_RGB, "D", PSTR("WS281x")},
{TYPE_SK6812_RGBW, "D", PSTR("SK6812/WS2814 RGBW")},
{TYPE_TM1814, "D", PSTR("TM1814")},
{TYPE_TM1815, "D", PSTR("TM1815")},
{TYPE_WS2811_400KHZ, "D", PSTR("400kHz")},
{TYPE_TM1829, "D", PSTR("TM1829")},
{TYPE_UCS8903, "D", PSTR("UCS8903")},
Expand Down
7 changes: 4 additions & 3 deletions wled00/bus_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,9 @@ class Bus {
}
static constexpr bool hasWhite(uint8_t type) {
return (type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) ||
type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 ||
type == TYPE_FW1906 || type == TYPE_WS2805 || type == TYPE_SM16825 || // digital types with white channel
type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_TM1815 ||
type == TYPE_UCS8904 || type == TYPE_FW1906 || type == TYPE_WS2805 ||
type == TYPE_SM16825 || // digital types with white channel
(type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) || // analog types with white channel
type == TYPE_NET_DDP_RGBW || type == TYPE_NET_ARTNET_RGBW; // network types with white channel
}
Expand All @@ -192,7 +193,7 @@ class Bus {
static constexpr bool isVirtual(uint8_t type) { return (type >= TYPE_VIRTUAL_MIN && type <= TYPE_VIRTUAL_MAX); }
static constexpr bool isHub75(uint8_t type) { return (type >= TYPE_HUB75MATRIX_MIN && type <= TYPE_HUB75MATRIX_MAX); }
static constexpr bool is16bit(uint8_t type) { return type == TYPE_UCS8903 || type == TYPE_UCS8904 || type == TYPE_SM16825; }
static constexpr bool mustRefresh(uint8_t type) { return type == TYPE_TM1814; }
static constexpr bool mustRefresh(uint8_t type) { return type == TYPE_TM1814 || type == TYPE_TM1815; }
static constexpr int numPWMPins(uint8_t type) { return (type - 40); }

static inline int16_t getCCT() { return _cct; }
Expand Down
Loading