FastLED 3.10.8
Loading...
Searching...
No Matches
hd108.h
Go to the documentation of this file.
1#pragma once
2
3#include "eorder.h"
4// IWYU pragma: begin_keep
5#include "platforms/shared/spi_pixel_writer.h" // ok platform headers
6// IWYU pragma: end_keep
7#include "fastspi.h"
9#include "fl/math/ease.h"
10#include "fl/stl/noexcept.h"
11
13//
14// HD108/NS108 16-bit SPI chipset
15// - SPI mode: MODE3 recommended (CPOL=1, CPHA=1)
16//
17// Key features:
18// 1. 16-bit color depth (65536 levels per channel)
19// 2. 5-bit global current control (0-31) for brightness management
20// 3. Higher color accuracy than APA102, especially at low brightness
21// 4. Dual-byte header encoding for brightness/current control
22//
23// Protocol:
24// - Start frame: 64 bits (8 bytes) of zeros
25// - Per LED: 2 header bytes + 6 color bytes (RGB @ 16-bit each)
26// - End frame: (num_leds / 2) + 4 bytes of 0xFF for latching
27//
28// References:
29// - GitHub Issue #1045: Community protocol discussion
30// - Pull Request #2119: Initial implementation by arfoll
31// - Manufacturer: www.hd108-led.com protocol documentation
32//
34
35// HD108 Controller class
40template <int DATA_PIN, fl::u8 CLOCK_PIN, EOrder RGB_ORDER = GRB, fl::u32 SPI_SPEED = DATA_RATE_MHZ(25)>
41class HD108Controller : public CPixelLEDController<RGB_ORDER> {
42 typedef fl::SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
44
45public:
47 #if defined(FL_IS_ESP32)
49 mSPI.setBus(bus);
50 return *this;
51 }
52 #endif
53
54 void init() FL_NO_EXCEPT override { mSPI.init(); }
55
56protected:
58 mSPI.select();
59
60 // ---- Start frame: 64 bits of 0 ----
61 // HD108 requires 8 bytes (64 bits) of zeros to initialize the strip
62 // Note: Some sources mention 128 bits (16 bytes), but 64 bits works reliably
63 for (int i = 0; i < 8; i++) mSPI.writeByte(0x00);
64
65 while (pixels.has(1)) {
66 // Load and scale pixel data in OUTPUT order (respects RGB_ORDER template parameter)
67 fl::u8 c0_8, c1_8, c2_8;
68 pixels.loadAndScaleRGB(&c0_8, &c1_8, &c2_8);
69
70 // Apply gamma correction (2.8) to convert 8-bit to 16-bit for HD108
71 // This provides smooth perceptual brightness transitions across the full 65K range
72 // Note: Brightness scaling is applied by loadAndScaleRGB() before gamma correction
73 fl::u16 c0_16 = fl::gamma_2_8(c0_8);
74 fl::u16 c1_16 = fl::gamma_2_8(c1_8);
75 fl::u16 c2_16 = fl::gamma_2_8(c2_8);
76
77 // Header bytes: HD108 per-channel gain control encoding
78 // f0: [1][RRRRR][GG] - marker bit, 5-bit R gain, 2 MSBs of G gain
79 // f1: [GGG][BBBBB] - 3 LSBs of G gain, 5-bit B gain
80 // Use maximum gain (31) for all channels to maximize precision
81 // Brightness control via 16-bit PWM values (already applied via loadAndScaleRGB)
82 constexpr fl::u8 r_gain = 31, g_gain = 31, b_gain = 31;
83 constexpr fl::u8 f0 = fl::u8(0x80 | ((r_gain & 0x1F) << 2) | ((g_gain >> 3) & 0x03));
84 constexpr fl::u8 f1 = fl::u8(((g_gain & 0x07) << 5) | (b_gain & 0x1F));
85
86 // Transmit LED frame: 2 header bytes + 6 color bytes (16-bit, big-endian, in RGB_ORDER)
87 mSPI.writeByte(f0);
88 mSPI.writeByte(f1);
89 mSPI.writeByte(fl::u8(c0_16 >> 8)); mSPI.writeByte(fl::u8(c0_16 & 0xFF)); // Channel 0 MSB, LSB
90 mSPI.writeByte(fl::u8(c1_16 >> 8)); mSPI.writeByte(fl::u8(c1_16 & 0xFF)); // Channel 1 MSB, LSB
91 mSPI.writeByte(fl::u8(c2_16 >> 8)); mSPI.writeByte(fl::u8(c2_16 & 0xFF)); // Channel 2 MSB, LSB
92
93 pixels.stepDithering();
94 pixels.advanceData();
95 }
96
97 // ---- End frame: 0xFF bytes to latch data into LEDs ----
98 // Formula: (num_leds / 2) + 4 provides sufficient clock pulses for 40 MHz operation
99 // This is more conservative than APA102's (num_leds + 15) / 16 formula
100 const int latch = pixels.size() / 2 + 4;
101 for (int i = 0; i < latch; i++) mSPI.writeByte(0xFF);
102 mSPI.endTransaction();
103}
104};
CPixelLEDController(RegistrationMode mode) FL_NO_EXCEPT
void showPixels(PixelController< RGB_ORDER > &pixels) FL_NO_EXCEPT override
Definition hd108.h:57
HD108Controller() FL_NO_EXCEPT
Definition hd108.h:46
void init() FL_NO_EXCEPT override
Initialize the LED controller.
Definition hd108.h:54
fl::SPIOutput< DATA_PIN, CLOCK_PIN, SPI_SPEED > SPI
Definition hd108.h:42
CLEDController & setSpiBus(Esp32SpiBus bus) FL_NO_EXCEPT
Select an ESP-IDF SPI host for this clocked controller before init().
fl::CLEDController CLEDController
defines the templated version of the CLEDController class
Serial peripheral interface (SPI) definitions per platform.
unsigned char u8
Definition stdint.h:131
Esp32SpiBus
ESP-IDF SPI host selection for an individual clocked LED controller.
Definition spi_bus.h:18
u16 gamma_2_8(u8 value) FL_NO_EXCEPT
Definition ease.cpp.hpp:54
#define FL_NO_EXCEPT
Pixel controller class.