diff --git a/src/internal/NeoMethods.h b/src/internal/NeoMethods.h
index cc241306..54151a0a 100644
--- a/src/internal/NeoMethods.h
+++ b/src/internal/NeoMethods.h
@@ -63,9 +63,12 @@ License along with NeoPixel. If not, see
#include "methods/DotStarEsp32DmaSpiMethod.h"
#include "methods/NeoEsp32I2sXMethod.h"
#include "methods/NeoEsp32LcdXMethod.h"
+#endif
-
+#if defined(SOC_PARLIO_SUPPORTED)
+#include "methods/Hd108Esp32ParlioMethod.h"
#endif
+
#include "methods/NeoEspBitBangMethod.h"
#elif defined(ARDUINO_ARCH_NRF52840) // must be before __arm__
diff --git a/src/internal/methods/Hd108Esp32ParlioMethod.h b/src/internal/methods/Hd108Esp32ParlioMethod.h
new file mode 100644
index 00000000..ef4e688d
--- /dev/null
+++ b/src/internal/methods/Hd108Esp32ParlioMethod.h
@@ -0,0 +1,204 @@
+/*-------------------------------------------------------------------------
+NeoPixel library helper functions for HD108 using PARLIO peripheral on compatible ESP32 chips
+
+Written by Tom Magnier, adapted from DotStarEsp32DmaSpiMethod
+
+I invest time and resources providing this open source code,
+please support me by donating (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
+.
+-------------------------------------------------------------------------*/
+
+// Available on ESP32-C5 / C6 / H2 / H4 / P4
+#if defined(ARDUINO_ARCH_ESP32) && defined(SOC_PARLIO_SUPPORTED)
+
+#pragma once
+
+/* 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/esp32c6/api-reference/peripherals/parlio/parlio_tx.html
+*/
+
+extern "C"
+{
+#include
+}
+
+template class Hd108Esp32ParlioMethodBase
+{
+public:
+ typedef typename T_SPEED::SettingsObject SettingsObject;
+
+ Hd108Esp32ParlioMethodBase(uint8_t clockPin, uint8_t dataPin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
+ _clockPin((gpio_num_t)clockPin),
+ _dataPin((gpio_num_t)dataPin),
+ _sizeStartFrame(16), // 128 bits (16 bytes)
+ _sizePixelData(pixelCount * elementSize + settingsSize),
+ _sizeEndFrame((pixelCount + 7) / 8 < 8 ? 8 : (pixelCount + 7) / 8) // one clock per bit, no less than 8 bytes
+ {
+
+ _bufferSize = _sizeStartFrame + _sizePixelData + _sizeEndFrame;
+
+ // must have a 4 byte aligned buffer for DMA
+ uint32_t alignment = _bufferSize % 4;
+ if (alignment)
+ {
+ _bufferSize += 4 - alignment;
+ }
+
+ _data = static_cast(malloc(_bufferSize));
+ _dmadata = static_cast(heap_caps_malloc(_bufferSize, MALLOC_CAP_DMA));
+
+ // data cleared later in Begin()
+ }
+
+ ~Hd108Esp32ParlioMethodBase()
+ {
+ if (_parlio_tx_handle)
+ {
+ deinitParlio();
+ }
+ free(_data);
+ heap_caps_free(_dmadata);
+ }
+
+ bool IsReadyToUpdate() const
+ {
+ esp_err_t ret = parlio_tx_unit_wait_all_done(_parlio_tx_handle, 0);
+
+ return (ret == ESP_OK);
+ }
+
+ void Initialize()
+ {
+ memset(_data, 0x00, _sizeStartFrame); //Init start frame
+ memset(_data + _sizeStartFrame + _sizePixelData, 0x00, _bufferSize - (_sizeStartFrame + _sizePixelData)); //Init end frame
+
+ initParlio();
+ }
+
+ void Update(bool)
+ {
+ while(!IsReadyToUpdate())
+ portYIELD();
+
+ memcpy(_dmadata, _data, _bufferSize);
+
+ parlio_transmit_config_t transmit_config = {
+ .idle_value = 0x00, // All data lines are low in idle state
+ };
+ ESP_ERROR_CHECK(parlio_tx_unit_transmit(_parlio_tx_handle, _dmadata, _bufferSize * 8, &transmit_config));
+ }
+
+ bool AlwaysUpdate()
+ {
+ // this method requires update to be called only if changes to buffer
+ return false;
+ }
+
+ bool SwapBuffers()
+ {
+ 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 (_parlio_tx_handle)
+ {
+ deinitParlio();
+ initParlio();
+ }
+ }
+
+private:
+ void initParlio()
+ {
+ parlio_tx_unit_config_t config = {
+ .clk_src = PARLIO_CLK_SRC_DEFAULT, // Select the default clock source
+ .clk_in_gpio_num = (gpio_num_t)-1, // Don't use external clock source
+ .output_clk_freq_hz = _speed.Clock, // Output clock frequency
+ .data_width = 1, // Data width is 1 bit (single data pin)
+ .data_gpio_nums = {
+ _dataPin
+ },
+ .clk_out_gpio_num = _clockPin, // Clock pin
+ .valid_gpio_num = (gpio_num_t)-1, // Don't use valid signal
+ .trans_queue_depth = 8, // Transaction queue depth (max count of pending transactions)
+ .max_transfer_size = _bufferSize, // Maximum transfer size (number of bytes per transaction)
+ .sample_edge = PARLIO_SAMPLE_EDGE_POS, // Sample data on the rising edge of the clock
+ .bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, //Data endianness : MSB first
+ .flags = {
+ //.clk_gate_en = 1, // NOT SUPPORTED ON C6 - Disable clock when not transmitting
+ },
+ };
+ ESP_LOGD("PARLIO", "Initializing PARLIO with clock freq %d Hz, source %d", config.output_clk_freq_hz, config.clk_src);
+
+ // Create TX unit instance
+ ESP_ERROR_CHECK(parlio_new_tx_unit(&config, &_parlio_tx_handle));
+ // Enable TX unit
+ ESP_ERROR_CHECK(parlio_tx_unit_enable(_parlio_tx_handle));
+ }
+
+ void deinitParlio()
+ {
+ while (!IsReadyToUpdate())
+ portYIELD();
+
+ ESP_ERROR_CHECK(parlio_tx_unit_disable(_parlio_tx_handle));
+ ESP_ERROR_CHECK(parlio_del_tx_unit(_parlio_tx_handle));
+ _parlio_tx_handle = NULL;
+ }
+
+ const size_t _sizeStartFrame;
+ const size_t _sizePixelData; // Size of '_data' buffer below, minus (_sizeStartFrame + _sizeEndFrame)
+ const size_t _sizeEndFrame;
+
+ size_t _bufferSize;
+ uint8_t* _data; // Holds start/end frames and LED color values
+ uint8_t* _dmadata; // Holds start/end frames and LED color values
+
+ parlio_tx_unit_handle_t _parlio_tx_handle = NULL;
+ T_SPEED _speed;
+ gpio_num_t _clockPin;
+ gpio_num_t _dataPin;
+};
+
+
+typedef Hd108Esp32ParlioMethodBase Hd108Esp32Parlio40MhzMethod;
+typedef Hd108Esp32ParlioMethodBase Hd108Esp32Parlio20MhzMethod;
+typedef Hd108Esp32ParlioMethodBase Hd108Esp32Parlio10MhzMethod;
+typedef Hd108Esp32ParlioMethodBase Hd108Esp32Parlio5MhzMethod;
+typedef Hd108Esp32ParlioMethodBase Hd108Esp32Parlio2MhzMethod;
+typedef Hd108Esp32ParlioMethodBase Hd108Esp32Parlio1MhzMethod;
+typedef Hd108Esp32ParlioMethodBase Hd108Esp32Parlio500KhzMethod;
+typedef Hd108Esp32ParlioMethodBase Hd108Esp32ParlioHzMethod;
+
+typedef Hd108Esp32Parlio10MhzMethod Hd108Esp32ParlioMethod;
+
+#endif // defined(ARDUINO_ARCH_ESP32) && defined(SOC_PARLIO_SUPPORTED)
\ No newline at end of file
diff --git a/src/internal/methods/Hd108GenericMethod.h b/src/internal/methods/Hd108GenericMethod.h
index 6b9cbe5c..dc37d3e1 100644
--- a/src/internal/methods/Hd108GenericMethod.h
+++ b/src/internal/methods/Hd108GenericMethod.h
@@ -41,6 +41,7 @@ template class Hd108MethodBase
Hd108MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
_sizeData(pixelCount * elementSize + settingsSize),
+ _sizeEndFrame((pixelCount + 7) / 8), // ceiling of pixel count divided by 8 (one clock per bit)
_wire(pinClock, pinData)
{
_data = static_cast(malloc(_sizeData));
@@ -77,20 +78,19 @@ template class Hd108MethodBase
}
void Update(bool)
- {
- const uint8_t startFrame[16] = { 0x00 };
- const uint8_t endFrame[4] = { 0xff };
-
+ {
_wire.beginTransaction();
- // start frame
- _wire.transmitBytes(startFrame, sizeof(startFrame));
+ //start frame : 128bits (16bytes) with DIN = 0
+ for (int i = 0; i < 16; i++)
+ _wire.transmitByte(0x00);
// data
_wire.transmitBytes(_data, _sizeData);
- // end frame
- _wire.transmitBytes(endFrame, sizeof(endFrame));
+ //end frame : at least one bit per LED ; no less than 8 bytes
+ for (size_t i = 0; i < max(_sizeEndFrame,(size_t)8); i++)
+ _wire.transmitByte(0x00);
_wire.endTransaction();
}
@@ -123,6 +123,7 @@ template class Hd108MethodBase
private:
const size_t _sizeData; // Size of '_data' buffer below
+ const size_t _sizeEndFrame;
T_TWOWIRE _wire;
uint8_t* _data; // Holds LED color values