FastLED 3.9.15
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
48 void init() override { mSPI.init(); }
49
50protected:
51void showPixels(PixelController<RGB_ORDER> &pixels) override {
52 mSPI.select();
53
54 // ---- Start frame: 64 bits of 0 ----
55 // HD108 requires 8 bytes (64 bits) of zeros to initialize the strip
56 // Note: Some sources mention 128 bits (16 bytes), but 64 bits works reliably
57 for (int i = 0; i < 8; i++) mSPI.writeByte(0x00);
58
59 while (pixels.has(1)) {
60 // Load and scale pixel data in OUTPUT order (respects RGB_ORDER template parameter)
61 fl::u8 c0_8, c1_8, c2_8;
62 pixels.loadAndScaleRGB(&c0_8, &c1_8, &c2_8);
63
64 // Apply gamma correction (2.8) to convert 8-bit to 16-bit for HD108
65 // This provides smooth perceptual brightness transitions across the full 65K range
66 // Note: Brightness scaling is applied by loadAndScaleRGB() before gamma correction
67 fl::u16 c0_16 = fl::gamma_2_8(c0_8);
68 fl::u16 c1_16 = fl::gamma_2_8(c1_8);
69 fl::u16 c2_16 = fl::gamma_2_8(c2_8);
70
71 // Header bytes: HD108 per-channel gain control encoding
72 // f0: [1][RRRRR][GG] - marker bit, 5-bit R gain, 2 MSBs of G gain
73 // f1: [GGG][BBBBB] - 3 LSBs of G gain, 5-bit B gain
74 // Use maximum gain (31) for all channels to maximize precision
75 // Brightness control via 16-bit PWM values (already applied via loadAndScaleRGB)
76 constexpr fl::u8 r_gain = 31, g_gain = 31, b_gain = 31;
77 constexpr fl::u8 f0 = fl::u8(0x80 | ((r_gain & 0x1F) << 2) | ((g_gain >> 3) & 0x03));
78 constexpr fl::u8 f1 = fl::u8(((g_gain & 0x07) << 5) | (b_gain & 0x1F));
79
80 // Transmit LED frame: 2 header bytes + 6 color bytes (16-bit, big-endian, in RGB_ORDER)
81 mSPI.writeByte(f0);
82 mSPI.writeByte(f1);
83 mSPI.writeByte(fl::u8(c0_16 >> 8)); mSPI.writeByte(fl::u8(c0_16 & 0xFF)); // Channel 0 MSB, LSB
84 mSPI.writeByte(fl::u8(c1_16 >> 8)); mSPI.writeByte(fl::u8(c1_16 & 0xFF)); // Channel 1 MSB, LSB
85 mSPI.writeByte(fl::u8(c2_16 >> 8)); mSPI.writeByte(fl::u8(c2_16 & 0xFF)); // Channel 2 MSB, LSB
86
87 pixels.stepDithering();
88 pixels.advanceData();
89 }
90
91 // ---- End frame: 0xFF bytes to latch data into LEDs ----
92 // Formula: (num_leds / 2) + 4 provides sufficient clock pulses for 40 MHz operation
93 // This is more conservative than APA102's (num_leds + 15) / 16 formula
94 const int latch = pixels.size() / 2 + 4;
95 for (int i = 0; i < latch; i++) mSPI.writeByte(0xFF);
96 mSPI.endTransaction();
97}
98};
CPixelLEDController(RegistrationMode mode)
void showPixels(PixelController< RGB_ORDER > &pixels) override
Definition hd108.h:51
HD108Controller() FL_NOEXCEPT
Definition hd108.h:46
void init() override
Initialize the LED controller.
Definition hd108.h:48
fl::SPIOutput< DATA_PIN, CLOCK_PIN, SPI_SPEED > SPI
Definition hd108.h:42
defines the templated version of the CLEDController class
Serial peripheral interface (SPI) definitions per platform.
unsigned char u8
Definition stdint.h:131
u16 gamma_2_8(u8 value)
Definition ease.cpp.hpp:53
#define FL_NOEXCEPT
FASTLED_FORCE_INLINE int size() const
Get the length of the LED strip.
FASTLED_FORCE_INLINE void loadAndScaleRGB(fl::u8 *b0_out, fl::u8 *b1_out, fl::u8 *b2_out)
FASTLED_FORCE_INLINE void advanceData()
Advance the data pointer forward, adjust position counter.
FASTLED_FORCE_INLINE bool has(int n)
Do we have n pixels left to process?
FASTLED_FORCE_INLINE void stepDithering()
Step the dithering forward - creates triangular wave that toggles between pixels.
Pixel controller class.