Skip to content

Commit fae32e1

Browse files
authored
Increase ws2812b timings (awawa-dev#7)
1 parent 3e6b8a4 commit fae32e1

File tree

4 files changed

+109
-39
lines changed

4 files changed

+109
-39
lines changed

CMakeLists.txt

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ set(SECOND_SEGMENT_INDEX OFF)
1616
set(SECOND_SEGMENT_REVERSED OFF)
1717

1818
# User configuration section ends here
19-
# Usually you don't need to change anything belowe this section
19+
# Usually you don't need to change anything below this section
2020

2121
cmake_minimum_required(VERSION 3.13)
2222

@@ -59,33 +59,34 @@ macro(HyperSerialPicoTarget HyperSerialPicoTargetName)
5959
pico_enable_stdio_usb(${HyperSerialPicoTargetName} 1)
6060
pico_enable_stdio_uart(${HyperSerialPicoTargetName} 0)
6161
pico_generate_pio_header(${HyperSerialPicoTargetName} ${CMAKE_SOURCE_DIR}/pio/neopixel.pio OUTPUT_DIR ${CMAKE_SOURCE_DIR}/generated)
62+
pico_generate_pio_header(${HyperSerialPicoTargetName} ${CMAKE_SOURCE_DIR}/pio/neopixel_ws2812b.pio OUTPUT_DIR ${CMAKE_SOURCE_DIR}/generated)
6263
add_custom_command(TARGET ${HyperSerialPicoTargetName} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/${HyperSerialPicoTargetName}.uf2 ${CMAKE_SOURCE_DIR}/firmwares)
6364
endmacro()
6465

6566
# targets for different LED strips
6667
IF(NOT SECOND_SEGMENT_INDEX)
67-
HyperSerialPicoTarget("HyperSerialPico_Spi")
68-
target_compile_definitions(HyperSerialPico_Spi PRIVATE -DSPILED_APA102 -DSPI_INTERFACE=${SPI_INTERFACE} -DDATA_PIN=${OUTPUT_SPI_DATA_PIN} -DCLOCK_PIN=${OUTPUT_SPI_CLOCK_PIN})
69-
HyperSerialPicoTarget("HyperSerialPico_sk6812Cold")
70-
target_compile_definitions(HyperSerialPico_sk6812Cold PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN})
71-
HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral")
72-
target_compile_definitions(HyperSerialPico_sk6812Neutral PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN})
73-
HyperSerialPicoTarget("HyperSerialPico_ws2812")
74-
target_compile_definitions(HyperSerialPico_ws2812 PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN})
68+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_Spi")
69+
target_compile_definitions("${CMAKE_PROJECT_NAME}_Spi" PRIVATE -DSPILED_APA102 -DSPI_INTERFACE=${SPI_INTERFACE} -DDATA_PIN=${OUTPUT_SPI_DATA_PIN} -DCLOCK_PIN=${OUTPUT_SPI_CLOCK_PIN})
70+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Cold")
71+
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Cold" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN})
72+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Neutral")
73+
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Neutral" PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN})
74+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_ws2812")
75+
target_compile_definitions("${CMAKE_PROJECT_NAME}_ws2812" PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN})
7576
ELSE()
7677
IF(NOT SECOND_SEGMENT_REVERSED)
77-
HyperSerialPicoTarget("HyperSerialPico_sk6812Cold_multisegment_at_${SECOND_SEGMENT_INDEX}")
78-
target_compile_definitions("HyperSerialPico_sk6812Cold_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX})
79-
HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral_multisegment_at_${SECOND_SEGMENT_INDEX}")
80-
target_compile_definitions("HyperSerialPico_sk6812Neutral_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX})
81-
HyperSerialPicoTarget("HyperSerialPico_ws2812_multisegment_at_${SECOND_SEGMENT_INDEX}")
82-
target_compile_definitions("HyperSerialPico_ws2812_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX})
78+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Cold_multisegment_at_${SECOND_SEGMENT_INDEX}")
79+
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Cold_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX})
80+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Neutral_multisegment_at_${SECOND_SEGMENT_INDEX}")
81+
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Neutral_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX})
82+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_ws2812_multisegment_at_${SECOND_SEGMENT_INDEX}")
83+
target_compile_definitions("${CMAKE_PROJECT_NAME}_ws2812_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX})
8384
ELSE()
84-
HyperSerialPicoTarget("HyperSerialPico_sk6812Cold_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
85-
target_compile_definitions("HyperSerialPico_sk6812Cold_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED)
86-
HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
87-
target_compile_definitions("HyperSerialPico_sk6812Neutral_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED)
88-
HyperSerialPicoTarget("HyperSerialPico_ws2812_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
89-
target_compile_definitions("HyperSerialPico_ws2812_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED)
85+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Cold_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
86+
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Cold_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED)
87+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Neutral_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
88+
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Neutral_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED)
89+
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_ws2812_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
90+
target_compile_definitions("${CMAKE_PROJECT_NAME}_ws2812_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED)
9091
ENDIF()
9192
ENDIF()

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
Adalight serial port LED driver implementation for Raspberry Pi Pico RP2040.
33

44
# Example of supported boards using Rp2040
5-
<p align="center"><img src="https://user-images.githubusercontent.com/69086569/236885968-baab51ba-a54b-4072-9a2a-cf867f2edb4b.png"/><img src="https://user-images.githubusercontent.com/69086569/236885360-dce9cfd7-92a8-43c6-911f-649325ee8a96.png"/></p>
5+
<p align="center"><img src="https://user-images.githubusercontent.com/69086569/236885968-baab51ba-a54b-4072-9a2a-cf867f2edb4b.png" width="250" height="250"/><img src="https://user-images.githubusercontent.com/69086569/236885360-dce9cfd7-92a8-43c6-911f-649325ee8a96.png" width="250" height="250"/></p>
6+
7+
# Recommended boards with a built-in level shifter
8+
To ensure the LEDs will work properly with the Pico board, a 3.3V to 5V level shifter is needed. You can use an external one e.g. SN74AHCT125N or just buy a model of rp2040 that already has it built-in: Adafruit Feather RP2040 Scorpio (output: GPIO16-23), Pimoroni Plasma 2040 (GPIO14-15) or Pimoroni Plasma Stick 2040 W (GPIO15).
9+
10+
<p align="center"><img src="https://user-images.githubusercontent.com/69086569/242393809-4e491159-76c7-4c1e-be0a-1f10cd5291f2.png" width="200" height="200"/><img src="https://user-images.githubusercontent.com/69086569/241395006-ee27175e-677b-4971-97bc-ed294eaa8f3b.png" width="200" height="200"/><img src="https://user-images.githubusercontent.com/69086569/241394387-f8193ed8-56d5-46c6-b406-911720aed605.png" width="200" height="200"/></p>
611

712
# Supported LED strips
813
| LED strip / Device | Single lane | Multi-segment |
@@ -38,11 +43,15 @@ You need HyperHDR v20 or newer. Make sure you have enabled `AWA protocol` and `E
3843
**LED output (SK6812/WS281x):** GPIO2 for Data
3944
**LED output (SPI LEDs):** GPIO3 for Data, GPIO2 for Clock
4045

46+
If multi-segment mode is enabled for SK6812/WS281x, the output for the second segment is always the next GPIO pin (`OUTPUT_DATA_PIN` + 1).
47+
4148
rp2040 allows hardware SPI on corresponding pairs of pins:
4249
spi0 ⇒ Data/Clock: GPIO3/GPIO2, GPIO19/GPIO18, GPIO7/GPIO6
4350
spi1 ⇒ Data/Clock: GPIO11/GPIO10, GPI15/GPIO14, GPIO27/GPI26
4451

45-
Pinout can be changed, but you need to make changes to `CMakeList.txt` (e.g. `OUTPUT_DATA_PIN` / `OUTPUT_SPI_DATA_PIN` / `OUTPUT_SPI_CLOCK_PIN`) and recompile the project. Also multi-segment mode can be enabled in this file: `SECOND_SEGMENT_INDEX` option at the beginning and optionally `SECOND_SEGMENT_REVERSED`. Once compiled, the results can be found in the `firmwares` folder.
52+
Pinout can be changed, but you need to make changes to `CMakeList.txt` (e.g. `OUTPUT_DATA_PIN` / `OUTPUT_SPI_DATA_PIN` / `OUTPUT_SPI_CLOCK_PIN`) and recompile the project. Also multi-segment mode can be enabled in this file: `SECOND_SEGMENT_INDEX` option at the beginning and optionally `SECOND_SEGMENT_REVERSED`. Once compiled, the results can be found in the `firmwares` folder.
53+
54+
Of course, you can also build your custom firmware completely online using Github Actions. The manual can be found on [wiki](https://github.com/awawa-dev/HyperSerialPico/wiki). Be sure to follow the steps in the correct order.
4655

4756
# Some benchmark results
4857

include/leds.h

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#include <hardware/dma.h>
7272
#include <hardware/clocks.h>
7373
#include <neopixel.pio.h>
74+
#include <neopixel_ws2812b.pio.h>
7475
#include <pico/stdlib.h>
7576
#include <pico/binary_info.h>
7677
#include <algorithm>
@@ -291,6 +292,8 @@ class DmaClient
291292
}
292293
};
293294

295+
enum class NeopixelSubtype {ws2812b, sk6812};
296+
294297
class Neopixel : public LedDriver, public DmaClient
295298
{
296299

@@ -299,7 +302,7 @@ class Neopixel : public LedDriver, public DmaClient
299302
friend class NeopixelParallel;
300303

301304
public:
302-
Neopixel(int lanes, uint64_t _resetTime, int _ledsNumber, int _pin, int _dmaSize, bool alignTo24 = false):
305+
Neopixel(NeopixelSubtype timingType, int lanes, uint64_t _resetTime, int _ledsNumber, int _pin, int _dmaSize, bool alignTo24 = false):
303306
LedDriver(_ledsNumber, _pin, _dmaSize)
304307
{
305308
pio_sm_config smConfig;
@@ -310,19 +313,29 @@ class Neopixel : public LedDriver, public DmaClient
310313

311314
if (lanes >= 1)
312315
{
313-
programAddress = pio_add_program(selectedPIO, &neopixel_parallel_program);
316+
programAddress = (timingType == NeopixelSubtype::ws2812b) ?
317+
pio_add_program(selectedPIO, &neopixel_ws2812b_parallel_program) : pio_add_program(selectedPIO, &neopixel_parallel_program);
318+
314319
for(uint i=_pin; i<_pin + lanes; i++){
315320
pio_gpio_init(selectedPIO, i);
316321
}
317-
smConfig = neopixel_parallel_program_get_default_config(programAddress);
322+
323+
smConfig = (timingType == NeopixelSubtype::ws2812b) ?
324+
neopixel_ws2812b_parallel_program_get_default_config(programAddress) : neopixel_parallel_program_get_default_config(programAddress);
325+
318326
sm_config_set_out_pins(&smConfig, _pin, lanes);
319327
sm_config_set_set_pins(&smConfig, _pin, lanes);
320328
}
321329
else
322330
{
323-
programAddress = pio_add_program(selectedPIO, &neopixel_program);
331+
programAddress = (timingType == NeopixelSubtype::ws2812b) ?
332+
pio_add_program(selectedPIO, &neopixel_ws2812b_program) : pio_add_program(selectedPIO, &neopixel_program);
333+
324334
pio_gpio_init(selectedPIO, _pin);
325-
smConfig = neopixel_program_get_default_config(programAddress);
335+
336+
smConfig = (timingType == NeopixelSubtype::ws2812b) ?
337+
neopixel_ws2812b_program_get_default_config(programAddress) : neopixel_program_get_default_config(programAddress);
338+
326339
sm_config_set_sideset_pins(&smConfig, _pin);
327340
}
328341

@@ -364,12 +377,13 @@ class Neopixel : public LedDriver, public DmaClient
364377
}
365378
};
366379

367-
template<int RESET_TIME, typename colorData>
380+
template<NeopixelSubtype _type, int RESET_TIME, typename colorData>
368381
class NeopixelType : public Neopixel
369382
{
370383
public:
371384

372-
NeopixelType(int _ledsNumber, int _pin) : Neopixel(0, RESET_TIME, _ledsNumber, _pin, _ledsNumber * sizeof(colorData), colorData::isAlignedTo24())
385+
NeopixelType(int _ledsNumber, int _pin) :
386+
Neopixel(_type, 0, RESET_TIME, _ledsNumber, _pin, _ledsNumber * sizeof(colorData), colorData::isAlignedTo24())
373387
{
374388
}
375389

@@ -400,13 +414,13 @@ class NeopixelParallel
400414

401415
public:
402416

403-
NeopixelParallel(size_t pixelSize, uint64_t _resetTime, int _ledsNumber, int _pin):
417+
NeopixelParallel(NeopixelSubtype _type, size_t pixelSize, uint64_t _resetTime, int _ledsNumber, int _pin):
404418
myLaneMask(1 << (instances++))
405419
{
406420
maxLeds = std::max(maxLeds, _ledsNumber);
407421

408422
delete muxer;
409-
muxer = new Neopixel(instances, _resetTime, maxLeds, _pin, maxLeds * 8 * pixelSize );
423+
muxer = new Neopixel(_type, instances, _resetTime, maxLeds, _pin, maxLeds * 8 * pixelSize );
410424
buffer = muxer->getBufferMemory();
411425
}
412426

@@ -440,15 +454,15 @@ class NeopixelParallel
440454
}
441455
};
442456

443-
template<int RESET_TIME, typename colorData>
457+
template<NeopixelSubtype _type, int RESET_TIME, typename colorData>
444458
class NeopixelParallelType : public NeopixelParallel
445459
{
446460
uint32_t lut[16];
447461

448462
public:
449463

450-
NeopixelParallelType(int _ledsNumber, int _basePinForLanes) : NeopixelParallel(sizeof(colorData), RESET_TIME,
451-
_ledsNumber, _basePinForLanes)
464+
NeopixelParallelType(int _ledsNumber, int _basePinForLanes) :
465+
NeopixelParallel(_type, sizeof(colorData), RESET_TIME, _ledsNumber, _basePinForLanes)
452466
{
453467
for (uint8_t a = 0; a < 16; a++)
454468
{
@@ -555,8 +569,8 @@ volatile bool DmaClient::isDmaBusy = false;
555569

556570

557571
// API classes
558-
typedef NeopixelType<650, ColorGrb32> ws2812;
559-
typedef NeopixelType<450, ColorGrbw> sk6812;
560-
typedef NeopixelParallelType<300, ColorGrb> ws2812p;
561-
typedef NeopixelParallelType<80, ColorGrbw> sk6812p;
572+
typedef NeopixelType<NeopixelSubtype::ws2812b, 650, ColorGrb32> ws2812;
573+
typedef NeopixelType<NeopixelSubtype::sk6812, 450, ColorGrbw> sk6812;
574+
typedef NeopixelParallelType<NeopixelSubtype::ws2812b, 300, ColorGrb> ws2812p;
575+
typedef NeopixelParallelType<NeopixelSubtype::sk6812, 80, ColorGrbw> sk6812p;
562576
typedef DotstarType<100, ColorDotstartBgr> apa102;

pio/neopixel_ws2812b.pio

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
; MIT License
2+
;
3+
; Copyright (c) 2023 awawa-dev
4+
;
5+
; https://github.com/awawa-dev/HyperSerialPico
6+
;
7+
; Permission is hereby granted, free of charge, to any person obtaining a copy
8+
; of this software and associated documentation files (the "Software"), to deal
9+
; in the Software without restriction, including without limitation the rights
10+
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
; copies of the Software, and to permit persons to whom the Software is
12+
; furnished to do so, subject to the following conditions:
13+
;
14+
; The above copyright notice and this permission notice shall be included in all
15+
; copies or substantial portions of the Software.
16+
;
17+
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
; SOFTWARE.
24+
25+
26+
.program neopixel_ws2812b
27+
.side_set 1
28+
29+
.wrap_target
30+
bitloop:
31+
out x, 1 side 0 [3]
32+
jmp !x do_zero side 1 [2]
33+
do_one:
34+
jmp bitloop side 1 [4]
35+
do_zero:
36+
nop side 0 [4]
37+
.wrap
38+
39+
.program neopixel_ws2812b_parallel
40+
41+
.wrap_target
42+
out x, 8
43+
mov pins, !null [2]
44+
mov pins, x [4]
45+
mov pins, null [2]
46+
.wrap

0 commit comments

Comments
 (0)