Skip to content

Commit 2fc41ff

Browse files
authored
Add support for the WS2801 protocol (#55)
WS2801 uses a SPI-like two-wire protocol with clock and data. Although some datasheets mention maximum clock speeds of 2MHz or even 25MHz most LED strips only work reliably up to 1MHz so use that to stay on the safe side. The data frame is 24 bit R, G, B (in this order) per LED, a pause of 500us resets the LED index. Signed-off-by: Matthias Reichl <[email protected]>
1 parent be586ba commit 2fc41ff

File tree

4 files changed

+95
-0
lines changed

4 files changed

+95
-0
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ IF(NOT SECOND_SEGMENT_INDEX)
129129
IF(NOT DISABLE_SPI_LEDS)
130130
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_Spi")
131131
target_compile_definitions("${CMAKE_PROJECT_NAME}_Spi" PRIVATE -DSPILED_APA102 -DSPI_INTERFACE=${OUTPUT_SPI_INTERFACE} -DDATA_PIN=${OUTPUT_SPI_DATA_PIN} -DCLOCK_PIN=${OUTPUT_SPI_CLOCK_PIN})
132+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_ws2801")
133+
target_compile_definitions("${CMAKE_PROJECT_NAME}_ws2801" PRIVATE -DSPILED_WS2801 -DSPI_INTERFACE=${OUTPUT_SPI_INTERFACE} -DDATA_PIN=${OUTPUT_SPI_DATA_PIN} -DCLOCK_PIN=${OUTPUT_SPI_CLOCK_PIN})
132134
endif()
133135
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Cold")
134136
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Cold" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN})

include/calibration.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#endif
3636
#elif SPILED_APA102
3737
typedef ColorDotstartBgr ColorDefinition;
38+
#elif SPILED_WS2801
39+
typedef ColorRgb ColorDefinition;
3840
#endif
3941

4042

include/leds.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,22 @@ struct ColorDotstartBgr
155155
};
156156
};
157157

158+
struct ColorRgb
159+
{
160+
uint8_t R;
161+
uint8_t G;
162+
uint8_t B;
163+
164+
ColorRgb(uint8_t gray) :
165+
R(gray), G(gray), B(gray)
166+
{
167+
};
168+
169+
ColorRgb() : R(0), G(0), B(0)
170+
{
171+
};
172+
};
173+
158174
class LedDriver
159175
{
160176
protected:
@@ -559,6 +575,73 @@ class DotstarType : public Dotstar
559575
}
560576
};
561577

578+
class Ws2801 : public LedDriver, public DmaClient
579+
{
580+
uint64_t resetTime;
581+
582+
public:
583+
Ws2801(uint64_t _resetTime, int _ledsNumber, spi_inst_t* _spi, uint32_t _datapin, uint32_t _clockpin, int _dmaSize):
584+
LedDriver(_ledsNumber, _datapin, _clockpin, _dmaSize)
585+
{
586+
dmaConfigure(pio0, 0);
587+
resetTime = _resetTime;
588+
589+
spi_init(_spi, 1000000);
590+
gpio_set_function(_clockpin, GPIO_FUNC_SPI);
591+
gpio_set_function(_datapin, GPIO_FUNC_SPI);
592+
bi_decl(bi_4pins_with_func(PICO_DEFAULT_SPI_RX_PIN, _datapin, _clockpin, PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI));
593+
594+
initDmaSpi(_spi, _dmaSize);
595+
}
596+
597+
uint8_t* getBufferMemory()
598+
{
599+
return buffer;
600+
}
601+
602+
protected:
603+
604+
void renderDma()
605+
{
606+
if (isDmaBusy)
607+
return;
608+
609+
isDmaBusy = true;
610+
611+
uint64_t currentTime = time_us_64();
612+
if (currentTime < resetTime + lastRenderTime)
613+
busy_wait_us(std::min(resetTime + lastRenderTime - currentTime, resetTime));
614+
615+
memcpy(dma, buffer, dmaSize);
616+
617+
dma_channel_set_read_addr(PICO_DMA_CHANNEL, dma, true);
618+
}
619+
};
620+
621+
template<int RESET_TIME, typename colorData>
622+
class Ws2801Type : public Ws2801
623+
{
624+
public:
625+
626+
Ws2801Type(int _ledsNumber, spi_inst_t* _spi, int _dataPin, int _clockPin) :
627+
Ws2801(RESET_TIME, _ledsNumber, _spi, _dataPin, _clockPin, _ledsNumber * sizeof(colorData))
628+
{
629+
}
630+
631+
void SetPixel(int index, colorData color)
632+
{
633+
if (index >= ledsNumber)
634+
return;
635+
636+
*(reinterpret_cast<colorData*>(buffer)+index) = color;
637+
}
638+
639+
void renderSingleLane()
640+
{
641+
renderDma();
642+
}
643+
};
644+
562645
Neopixel* NeopixelParallel::muxer = nullptr;
563646
uint8_t* NeopixelParallel::buffer = nullptr;
564647
int NeopixelParallel::instances = 0;
@@ -574,3 +657,4 @@ typedef NeopixelType<NeopixelSubtype::sk6812, 450, ColorGrbw> sk6812;
574657
typedef NeopixelParallelType<NeopixelSubtype::ws2812b, 300, ColorGrb> ws2812p;
575658
typedef NeopixelParallelType<NeopixelSubtype::sk6812, 80, ColorGrbw> sk6812p;
576659
typedef DotstarType<100, ColorDotstartBgr> apa102;
660+
typedef Ws2801Type<500, ColorRgb> ws2801;

source/main.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
#ifdef SPILED_APA102
7070
#pragma message(VAR_NAME_VALUE(SPILED_APA102))
7171
#endif
72+
#ifdef SPILED_WS2801
73+
#pragma message(VAR_NAME_VALUE(SPILED_WS2801))
74+
#endif
7275

7376
#ifdef NEOPIXEL_RGBW
7477
#define LED_DRIVER sk6812
@@ -80,6 +83,10 @@
8083
#define LED_DRIVER apa102
8184
#pragma message(VAR_NAME_VALUE(SPI_INTERFACE))
8285
#endif
86+
#ifdef SPILED_WS2801
87+
#define LED_DRIVER ws2801
88+
#pragma message(VAR_NAME_VALUE(SPI_INTERFACE))
89+
#endif
8390

8491
#pragma message(VAR_NAME_VALUE(DATA_PIN))
8592
#ifdef CLOCK_PIN

0 commit comments

Comments
 (0)