Skip to content

Commit

Permalink
Increase ws2812b timings (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
awawa-dev authored Jul 10, 2023
1 parent 3e6b8a4 commit fae32e1
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 39 deletions.
43 changes: 22 additions & 21 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ set(SECOND_SEGMENT_INDEX OFF)
set(SECOND_SEGMENT_REVERSED OFF)

# User configuration section ends here
# Usually you don't need to change anything belowe this section
# Usually you don't need to change anything below this section

cmake_minimum_required(VERSION 3.13)

Expand Down Expand Up @@ -59,33 +59,34 @@ macro(HyperSerialPicoTarget HyperSerialPicoTargetName)
pico_enable_stdio_usb(${HyperSerialPicoTargetName} 1)
pico_enable_stdio_uart(${HyperSerialPicoTargetName} 0)
pico_generate_pio_header(${HyperSerialPicoTargetName} ${CMAKE_SOURCE_DIR}/pio/neopixel.pio OUTPUT_DIR ${CMAKE_SOURCE_DIR}/generated)
pico_generate_pio_header(${HyperSerialPicoTargetName} ${CMAKE_SOURCE_DIR}/pio/neopixel_ws2812b.pio OUTPUT_DIR ${CMAKE_SOURCE_DIR}/generated)
add_custom_command(TARGET ${HyperSerialPicoTargetName} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/${HyperSerialPicoTargetName}.uf2 ${CMAKE_SOURCE_DIR}/firmwares)
endmacro()

# targets for different LED strips
IF(NOT SECOND_SEGMENT_INDEX)
HyperSerialPicoTarget("HyperSerialPico_Spi")
target_compile_definitions(HyperSerialPico_Spi PRIVATE -DSPILED_APA102 -DSPI_INTERFACE=${SPI_INTERFACE} -DDATA_PIN=${OUTPUT_SPI_DATA_PIN} -DCLOCK_PIN=${OUTPUT_SPI_CLOCK_PIN})
HyperSerialPicoTarget("HyperSerialPico_sk6812Cold")
target_compile_definitions(HyperSerialPico_sk6812Cold PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN})
HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral")
target_compile_definitions(HyperSerialPico_sk6812Neutral PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN})
HyperSerialPicoTarget("HyperSerialPico_ws2812")
target_compile_definitions(HyperSerialPico_ws2812 PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN})
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_Spi")
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})
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Cold")
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Cold" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN})
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Neutral")
target_compile_definitions("${CMAKE_PROJECT_NAME}_sk6812Neutral" PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN})
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_ws2812")
target_compile_definitions("${CMAKE_PROJECT_NAME}_ws2812" PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN})
ELSE()
IF(NOT SECOND_SEGMENT_REVERSED)
HyperSerialPicoTarget("HyperSerialPico_sk6812Cold_multisegment_at_${SECOND_SEGMENT_INDEX}")
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})
HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral_multisegment_at_${SECOND_SEGMENT_INDEX}")
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})
HyperSerialPicoTarget("HyperSerialPico_ws2812_multisegment_at_${SECOND_SEGMENT_INDEX}")
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})
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Cold_multisegment_at_${SECOND_SEGMENT_INDEX}")
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})
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Neutral_multisegment_at_${SECOND_SEGMENT_INDEX}")
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})
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_ws2812_multisegment_at_${SECOND_SEGMENT_INDEX}")
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})
ELSE()
HyperSerialPicoTarget("HyperSerialPico_sk6812Cold_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
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)
HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
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)
HyperSerialPicoTarget("HyperSerialPico_ws2812_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
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)
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Cold_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
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)
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_sk6812Neutral_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
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)
HyperSerialPicoTarget("${CMAKE_PROJECT_NAME}_ws2812_rev_multisegment_at_${SECOND_SEGMENT_INDEX}")
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)
ENDIF()
ENDIF()
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
Adalight serial port LED driver implementation for Raspberry Pi Pico RP2040.

# Example of supported boards using Rp2040
<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>
<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>

# Recommended boards with a built-in level shifter
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).

<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>

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

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).

rp2040 allows hardware SPI on corresponding pairs of pins:
spi0 ⇒ Data/Clock: GPIO3/GPIO2, GPIO19/GPIO18, GPIO7/GPIO6
spi1 ⇒ Data/Clock: GPIO11/GPIO10, GPI15/GPIO14, GPIO27/GPI26

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.
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.

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.

# Some benchmark results

Expand Down
46 changes: 30 additions & 16 deletions include/leds.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
#include <hardware/dma.h>
#include <hardware/clocks.h>
#include <neopixel.pio.h>
#include <neopixel_ws2812b.pio.h>
#include <pico/stdlib.h>
#include <pico/binary_info.h>
#include <algorithm>
Expand Down Expand Up @@ -291,6 +292,8 @@ class DmaClient
}
};

enum class NeopixelSubtype {ws2812b, sk6812};

class Neopixel : public LedDriver, public DmaClient
{

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

public:
Neopixel(int lanes, uint64_t _resetTime, int _ledsNumber, int _pin, int _dmaSize, bool alignTo24 = false):
Neopixel(NeopixelSubtype timingType, int lanes, uint64_t _resetTime, int _ledsNumber, int _pin, int _dmaSize, bool alignTo24 = false):
LedDriver(_ledsNumber, _pin, _dmaSize)
{
pio_sm_config smConfig;
Expand All @@ -310,19 +313,29 @@ class Neopixel : public LedDriver, public DmaClient

if (lanes >= 1)
{
programAddress = pio_add_program(selectedPIO, &neopixel_parallel_program);
programAddress = (timingType == NeopixelSubtype::ws2812b) ?
pio_add_program(selectedPIO, &neopixel_ws2812b_parallel_program) : pio_add_program(selectedPIO, &neopixel_parallel_program);

for(uint i=_pin; i<_pin + lanes; i++){
pio_gpio_init(selectedPIO, i);
}
smConfig = neopixel_parallel_program_get_default_config(programAddress);

smConfig = (timingType == NeopixelSubtype::ws2812b) ?
neopixel_ws2812b_parallel_program_get_default_config(programAddress) : neopixel_parallel_program_get_default_config(programAddress);

sm_config_set_out_pins(&smConfig, _pin, lanes);
sm_config_set_set_pins(&smConfig, _pin, lanes);
}
else
{
programAddress = pio_add_program(selectedPIO, &neopixel_program);
programAddress = (timingType == NeopixelSubtype::ws2812b) ?
pio_add_program(selectedPIO, &neopixel_ws2812b_program) : pio_add_program(selectedPIO, &neopixel_program);

pio_gpio_init(selectedPIO, _pin);
smConfig = neopixel_program_get_default_config(programAddress);

smConfig = (timingType == NeopixelSubtype::ws2812b) ?
neopixel_ws2812b_program_get_default_config(programAddress) : neopixel_program_get_default_config(programAddress);

sm_config_set_sideset_pins(&smConfig, _pin);
}

Expand Down Expand Up @@ -364,12 +377,13 @@ class Neopixel : public LedDriver, public DmaClient
}
};

template<int RESET_TIME, typename colorData>
template<NeopixelSubtype _type, int RESET_TIME, typename colorData>
class NeopixelType : public Neopixel
{
public:

NeopixelType(int _ledsNumber, int _pin) : Neopixel(0, RESET_TIME, _ledsNumber, _pin, _ledsNumber * sizeof(colorData), colorData::isAlignedTo24())
NeopixelType(int _ledsNumber, int _pin) :
Neopixel(_type, 0, RESET_TIME, _ledsNumber, _pin, _ledsNumber * sizeof(colorData), colorData::isAlignedTo24())
{
}

Expand Down Expand Up @@ -400,13 +414,13 @@ class NeopixelParallel

public:

NeopixelParallel(size_t pixelSize, uint64_t _resetTime, int _ledsNumber, int _pin):
NeopixelParallel(NeopixelSubtype _type, size_t pixelSize, uint64_t _resetTime, int _ledsNumber, int _pin):
myLaneMask(1 << (instances++))
{
maxLeds = std::max(maxLeds, _ledsNumber);

delete muxer;
muxer = new Neopixel(instances, _resetTime, maxLeds, _pin, maxLeds * 8 * pixelSize );
muxer = new Neopixel(_type, instances, _resetTime, maxLeds, _pin, maxLeds * 8 * pixelSize );
buffer = muxer->getBufferMemory();
}

Expand Down Expand Up @@ -440,15 +454,15 @@ class NeopixelParallel
}
};

template<int RESET_TIME, typename colorData>
template<NeopixelSubtype _type, int RESET_TIME, typename colorData>
class NeopixelParallelType : public NeopixelParallel
{
uint32_t lut[16];

public:

NeopixelParallelType(int _ledsNumber, int _basePinForLanes) : NeopixelParallel(sizeof(colorData), RESET_TIME,
_ledsNumber, _basePinForLanes)
NeopixelParallelType(int _ledsNumber, int _basePinForLanes) :
NeopixelParallel(_type, sizeof(colorData), RESET_TIME, _ledsNumber, _basePinForLanes)
{
for (uint8_t a = 0; a < 16; a++)
{
Expand Down Expand Up @@ -555,8 +569,8 @@ volatile bool DmaClient::isDmaBusy = false;


// API classes
typedef NeopixelType<650, ColorGrb32> ws2812;
typedef NeopixelType<450, ColorGrbw> sk6812;
typedef NeopixelParallelType<300, ColorGrb> ws2812p;
typedef NeopixelParallelType<80, ColorGrbw> sk6812p;
typedef NeopixelType<NeopixelSubtype::ws2812b, 650, ColorGrb32> ws2812;
typedef NeopixelType<NeopixelSubtype::sk6812, 450, ColorGrbw> sk6812;
typedef NeopixelParallelType<NeopixelSubtype::ws2812b, 300, ColorGrb> ws2812p;
typedef NeopixelParallelType<NeopixelSubtype::sk6812, 80, ColorGrbw> sk6812p;
typedef DotstarType<100, ColorDotstartBgr> apa102;
46 changes: 46 additions & 0 deletions pio/neopixel_ws2812b.pio
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
; MIT License
;
; Copyright (c) 2023 awawa-dev
;
; https://github.com/awawa-dev/HyperSerialPico
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.


.program neopixel_ws2812b
.side_set 1

.wrap_target
bitloop:
out x, 1 side 0 [3]
jmp !x do_zero side 1 [2]
do_one:
jmp bitloop side 1 [4]
do_zero:
nop side 0 [4]
.wrap

.program neopixel_ws2812b_parallel

.wrap_target
out x, 8
mov pins, !null [2]
mov pins, x [4]
mov pins, null [2]
.wrap

0 comments on commit fae32e1

Please sign in to comment.