Add new WS281x driver

Add a new WS281x driver which is a wrapper around jgarff's ws281x library which
works on Pi B+, Pi 2, Pi Zero and probably Pi 3 as well.

jgarff's ws281x library is included as a submodule


Former-commit-id: e473dfd36d31b14598da5e56e4b8bf9f2aa7bb24
This commit is contained in:
Mark Walker 2016-03-04 00:07:02 -08:00
parent 14fc1d9bb6
commit 78e606a1c4
9 changed files with 180 additions and 6 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "dependencies/external/protobuf"]
path = dependencies/external/protobuf
url = https://github.com/tvdzwan/protobuf.git
[submodule "dependencies/external/rpi_ws281x"]
path = dependencies/external/rpi_ws281x
url = https://github.com/jgarff/rpi_ws281x

View File

@ -34,6 +34,9 @@ message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
option(ENABLE_WS2812BPWM "Enable the WS2812b-PWM device" OFF)
message(STATUS "ENABLE_WS2812BPWM = " ${ENABLE_WS2812BPWM})
option(ENABLE_WS281XPWM "Enable the WS281x-PWM device" OFF)
message(STATUS "ENABLE_WS281XPWM = " ${ENABLE_WS281XPWM})
option(ENABLE_X11 "Enable the X11 grabber" OFF)
message(STATUS "ENABLE_X11 = " ${ENABLE_X11})
@ -56,6 +59,10 @@ if(ENABLE_OSX AND ENABLE_DISPMANX)
message(FATAL_ERROR "dispmanx grabber and osx grabber cannot be used at the same time")
endif(ENABLE_OSX AND ENABLE_DISPMANX)
if(ENABLE_WS2812BPWM AND ENABLE_WS281XPWM)
message(FATAL_ERROR "WS2812b and WS281x drivers cannot be used at the same time")
endif(ENABLE_WS2812BPWM AND ENABLE_WS281XPWM)
#if(ENABLE_QT5)
# TODO vs ENABLE_QT4?
#endif(ENABLE_QT5)

View File

@ -12,6 +12,9 @@
// Define to enable the ws2812b-pwm-device
#cmakedefine ENABLE_WS2812BPWM
// Define to enable the ws281x-pwm-via-dma-device using jgarff's library
#cmakedefine ENABLE_WS281XPWM
// Define to enable the spi-device
#cmakedefine ENABLE_TINKERFORGE

View File

@ -4,6 +4,13 @@ add_subdirectory(build/jsoncpp)
add_subdirectory(build/serial)
add_subdirectory(build/tinkerforge)
if(ENABLE_WS281XPWM)
add_library(ws281x
external/rpi_ws281x/mailbox.c external/rpi_ws281x/ws2811.c
external/rpi_ws281x/pwm.c external/rpi_ws281x/dma.c
external/rpi_ws281x/rpihw.c)
endif(ENABLE_WS281XPWM)
if(ENABLE_PROTOBUF)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared protobuf library")
add_subdirectory(external/protobuf)

1
dependencies/external/rpi_ws281x vendored Submodule

@ -0,0 +1 @@
Subproject commit 34c917e25044a7aca4f6dc9326c48c1474b8f28c

View File

@ -33,9 +33,9 @@ SET(Leddevice_HEADERS
${CURRENT_SOURCE_DIR}/LedDevicePaintpack.h
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h
${CURRENT_SOURCE_DIR}/LedDeviceSedu.h
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h
${CURRENT_SOURCE_DIR}/LedDeviceUdp.h
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h
${CURRENT_SOURCE_DIR}/LedDeviceUdp.h
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h
${CURRENT_SOURCE_DIR}/LedDeviceAtmo.h
@ -56,9 +56,9 @@ SET(Leddevice_SOURCES
${CURRENT_SOURCE_DIR}/LedDevicePaintpack.cpp
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp
${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.cpp
${CURRENT_SOURCE_DIR}/LedDeviceUdp.cpp
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.cpp
${CURRENT_SOURCE_DIR}/LedDeviceUdp.cpp
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.cpp
@ -97,6 +97,18 @@ SET(Leddevice_SOURCES
)
endif(ENABLE_WS2812BPWM)
if(ENABLE_WS281XPWM)
include_directories(../../dependencies/external/rpi_ws281x)
SET(Leddevice_HEADERS
${Leddevice_HEADERS}
${CURRENT_SOURCE_DIR}/LedDeviceWS281x.h
)
SET(Leddevice_SOURCES
${Leddevice_SOURCES}
${CURRENT_SOURCE_DIR}/LedDeviceWS281x.cpp
)
endif(ENABLE_WS281XPWM)
if(ENABLE_TINKERFORGE)
SET(Leddevice_HEADERS
${Leddevice_HEADERS}
@ -138,6 +150,10 @@ if(ENABLE_TINKERFORGE)
target_link_libraries(leddevice tinkerforge)
endif()
if(ENABLE_WS281XPWM)
target_link_libraries(leddevice ws281x)
endif()
if(APPLE)
target_link_libraries(leddevice hidapi-mac)
else()

View File

@ -42,6 +42,10 @@
#include "LedDeviceWS2812b.h"
#endif
#ifdef ENABLE_WS281XPWM
#include "LedDeviceWS281x.h"
#endif
LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
{
std::cout << "Device configuration: " << deviceConfig << std::endl;
@ -285,6 +289,18 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
LedDeviceWS2812b * ledDeviceWS2812b = new LedDeviceWS2812b();
device = ledDeviceWS2812b;
}
#endif
#ifdef ENABLE_WS281XPWM
else if (type == "ws281x")
{
const int gpio = deviceConfig.get("gpio", 18).asInt();
const int leds = deviceConfig.get("leds", 12).asInt();
const uint32_t freq = deviceConfig.get("freq", (Json::UInt)800000ul).asInt();
const int dmanum = deviceConfig.get("dmanum", 5).asInt();
LedDeviceWS281x * ledDeviceWS281x = new LedDeviceWS281x(gpio, leds, freq, dmanum);
device = ledDeviceWS281x;
}
#endif
else
{

View File

@ -0,0 +1,78 @@
#include <iostream>
#include "LedDeviceWS281x.h"
// Constructor
LedDeviceWS281x::LedDeviceWS281x(const int gpio, const int leds, const uint32_t freq, const int dmanum)
{
initialized = false;
led_string.freq = freq;
led_string.dmanum = dmanum;
led_string.channel[0].gpionum = gpio;
led_string.channel[0].invert = 0;
led_string.channel[0].count = leds;
led_string.channel[0].brightness = 255;
led_string.channel[0].strip_type = WS2811_STRIP_RGB;
led_string.channel[1].gpionum = 0;
led_string.channel[1].invert = 0;
led_string.channel[1].count = 0;
led_string.channel[1].brightness = 0;
led_string.channel[0].strip_type = WS2811_STRIP_RGB;
if (ws2811_init(&led_string) < 0) {
std::cout << "Unable to initialize ws281x library." << std::endl;
throw -1;
}
initialized = true;
}
// Send new values down the LED chain
int LedDeviceWS281x::write(const std::vector<ColorRgb> &ledValues)
{
if (!initialized)
return -1;
int idx = 0;
for (const ColorRgb& color : ledValues)
{
if (idx >= led_string.channel[0].count)
break;
led_string.channel[0].leds[idx++] = ((uint32_t)color.red << 16) + ((uint32_t)color.green << 8) + color.blue;
}
while (idx < led_string.channel[0].count)
led_string.channel[0].leds[idx++] = 0;
if (ws2811_render(&led_string))
return -1;
return 0;
}
// Turn off the LEDs by sending 000000's
// TODO Allow optional power switch out another gpio, if this code handles it can
// make it more likely we don't accidentally drive data into an off strip
int LedDeviceWS281x::switchOff()
{
if (!initialized)
return -1;
int idx = 0;
while (idx < led_string.channel[0].count)
led_string.channel[0].leds[idx++] = 0;
if (ws2811_render(&led_string))
return -1;
return 0;
}
// Destructor
LedDeviceWS281x::~LedDeviceWS281x()
{
if (initialized)
{
std::cout << "Shutdown WS281x PWM and DMA channel" << std::endl;
ws2811_fini(&led_string);
}
initialized = false;
}

View File

@ -0,0 +1,43 @@
#ifndef LEDDEVICEWS281X_H_
#define LEDDEVICEWS281X_H_
#pragma once
#include <leddevice/LedDevice.h>
#include <ws2811.h>
class LedDeviceWS281x : public LedDevice
{
public:
///
/// Constructs the LedDevice for WS281x (one wire 800kHz)
///
/// @param gpio The gpio pin to use (BCM chip counting, default is 18)
/// @param leds The number of leds attached to the gpio pin
/// @param freq The target frequency for the data line, default is 800000
/// @param dmanum The DMA channel to use, default is 5
///
LedDeviceWS281x(const int gpio, const int leds, const uint32_t freq, int dmanum);
///
/// Destructor of the LedDevice, waits for DMA to complete and then cleans up
///
~LedDeviceWS281x();
///
/// Writes the led color values to the led-device
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> &ledValues);
/// Switch the leds off
virtual int switchOff();
private:
ws2811_t led_string;
bool initialized;
};
#endif /* LEDDEVICEWS281X_H_ */