From ef6ac97f84e521f4ec31876808e1fb06bf6231df Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Wed, 13 Nov 2013 20:42:18 +0000 Subject: [PATCH] Renamed ldp6803 to lpd6803 Added Lpd8806 device Former-commit-id: 716cd60d1ac750fc2feca6f3621b20196b52a84e --- libsrc/hyperion/CMakeLists.txt | 2 + libsrc/hyperion/Hyperion.cpp | 13 ++- libsrc/hyperion/device/LedDeviceLpd6803.cpp | 6 +- libsrc/hyperion/device/LedDeviceLpd6803.h | 4 +- libsrc/hyperion/device/LedDeviceLpd8806.cpp | 54 ++++++++++ libsrc/hyperion/device/LedDeviceLpd8806.h | 103 ++++++++++++++++++++ 6 files changed, 176 insertions(+), 6 deletions(-) create mode 100644 libsrc/hyperion/device/LedDeviceLpd8806.cpp create mode 100644 libsrc/hyperion/device/LedDeviceLpd8806.h diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index f2c6a195..2c5bf4d9 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -27,6 +27,7 @@ SET(Hyperion_HEADERS ${CURRENT_SOURCE_DIR}/device/LedDeviceSedu.h ${CURRENT_SOURCE_DIR}/device/LedDeviceWs2801.h ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.h + ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.h ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.h ) @@ -48,6 +49,7 @@ SET(Hyperion_SOURCES ${CURRENT_SOURCE_DIR}/device/LedDeviceTest.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceWs2801.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.cpp + ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp ) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 88590e65..7528ca6b 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -14,6 +14,7 @@ #include #include "device/LedDeviceLpd6803.h" +#include "device/LedDeviceLpd8806.h" #include "device/LedDeviceSedu.h" #include "device/LedDeviceTest.h" #include "device/LedDeviceWs2801.h" @@ -47,11 +48,21 @@ LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig) const std::string output = deviceConfig["output"].asString(); const unsigned rate = deviceConfig["rate"].asInt(); - LedDeviceLdp6803* deviceLdp6803 = new LedDeviceLdp6803(output, rate); + LedDeviceLpd6803* deviceLdp6803 = new LedDeviceLpd6803(output, rate); deviceLdp6803->open(); device = deviceLdp6803; } + else if (type == "lpd8806" || type == "ldp8806") + { + const std::string output = deviceConfig["output"].asString(); + const unsigned rate = deviceConfig["rate"].asInt(); + + LedDeviceLpd8806* deviceLpd8806 = new LedDeviceLpd8806(output, rate); + deviceLpd8806->open(); + + device = deviceLpd8806; + } else if (type == "sedu") { const std::string output = deviceConfig["output"].asString(); diff --git a/libsrc/hyperion/device/LedDeviceLpd6803.cpp b/libsrc/hyperion/device/LedDeviceLpd6803.cpp index bf35a291..bfaa010b 100644 --- a/libsrc/hyperion/device/LedDeviceLpd6803.cpp +++ b/libsrc/hyperion/device/LedDeviceLpd6803.cpp @@ -10,14 +10,14 @@ // hyperion local includes #include "LedDeviceLpd6803.h" -LedDeviceLdp6803::LedDeviceLdp6803(const std::string& outputDevice, const unsigned baudrate) : +LedDeviceLpd6803::LedDeviceLpd6803(const std::string& outputDevice, const unsigned baudrate) : LedSpiDevice(outputDevice, baudrate), _ledBuffer(0) { // empty } -int LedDeviceLdp6803::write(const std::vector &ledValues) +int LedDeviceLpd6803::write(const std::vector &ledValues) { // Reconfigure if the current connfiguration does not match the required configuration if (4 + 2*ledValues.size() != _ledBuffer.size()) @@ -43,7 +43,7 @@ int LedDeviceLdp6803::write(const std::vector &ledValues) return 0; } -int LedDeviceLdp6803::switchOff() +int LedDeviceLpd6803::switchOff() { return write(std::vector(_ledBuffer.size(), ColorRgb{0,0,0})); } diff --git a/libsrc/hyperion/device/LedDeviceLpd6803.h b/libsrc/hyperion/device/LedDeviceLpd6803.h index 54302d0e..c53919b2 100644 --- a/libsrc/hyperion/device/LedDeviceLpd6803.h +++ b/libsrc/hyperion/device/LedDeviceLpd6803.h @@ -14,7 +14,7 @@ /// (R, G and B in the above illustration) making 16 bits per led. Total bytes = 4 + (2 x number of /// leds) /// -class LedDeviceLdp6803 : public LedSpiDevice +class LedDeviceLpd6803 : public LedSpiDevice { public: /// @@ -23,7 +23,7 @@ public: /// @param[in] outputDevice The name of the output device (eg '/dev/spidev0.0') /// @param[in] baudrate The used baudrate for writing to the output device /// - LedDeviceLdp6803(const std::string& outputDevice, const unsigned baudrate); + LedDeviceLpd6803(const std::string& outputDevice, const unsigned baudrate); /// /// Writes the led color values to the led-device diff --git a/libsrc/hyperion/device/LedDeviceLpd8806.cpp b/libsrc/hyperion/device/LedDeviceLpd8806.cpp new file mode 100644 index 00000000..3e8cc1da --- /dev/null +++ b/libsrc/hyperion/device/LedDeviceLpd8806.cpp @@ -0,0 +1,54 @@ +// STL includes +#include +#include +#include + +// Linux includes +#include +#include + +// hyperion local includes +#include "LedDeviceLpd8806.h" + +LedDeviceLpd8806::LedDeviceLpd8806(const std::string& outputDevice, const unsigned baudrate) : + LedSpiDevice(outputDevice, baudrate), + _ledBuffer(0) +{ + // empty +} + +int LedDeviceLpd8806::write(const std::vector &ledValues) +{ + const unsigned clearSize = ledValues.size()/32+1; + // Reconfigure if the current connfiguration does not match the required configuration + if (3*ledValues.size() + clearSize != _ledBuffer.size()) + { + // Initialise the buffer + _ledBuffer.resize(3*ledValues.size() + clearSize, 0x00); + + // Perform an initial reset to start accepting data on the first led + writeBytes(clearSize, _ledBuffer.data()); + } + + // Copy the colors from the ColorRgb vector to the Ldp8806 data vector + for (unsigned iLed=0; iLed> 1); + _ledBuffer[iLed*3+1] = 0x80 | (rgb.green >> 1); + _ledBuffer[iLed*3+2] = 0x80 | (rgb.blue >> 1); + } + + // Write the data + if (writeBytes(_ledBuffer.size(), _ledBuffer.data()) < 0) + { + return -1; + } + return 0; +} + +int LedDeviceLpd8806::switchOff() +{ + return write(std::vector(_ledBuffer.size(), ColorRgb{0,0,0})); +} diff --git a/libsrc/hyperion/device/LedDeviceLpd8806.h b/libsrc/hyperion/device/LedDeviceLpd8806.h new file mode 100644 index 00000000..bf85b177 --- /dev/null +++ b/libsrc/hyperion/device/LedDeviceLpd8806.h @@ -0,0 +1,103 @@ +#pragma once + +// Local hyperion incluse +#include "LedSpiDevice.h" + +/// +/// Implementation of the LedDevice interface for writing to LPD8806 led device. +/// +/// The following description is copied from 'adafruit' (github.com/adafruit/LPD8806) +/// +/// Clearing up some misconceptions about how the LPD8806 drivers work: +/// +/// The LPD8806 is not a FIFO shift register. The first data out controls the +/// LED *closest* to the processor (unlike a typical shift register, where the +/// first data out winds up at the *furthest* LED). Each LED driver 'fills up' +/// with data and then passes through all subsequent bytes until a latch +/// condition takes place. This is actually pretty common among LED drivers. +/// +/// All color data bytes have the high bit (128) set, with the remaining +/// seven bits containing a brightness value (0-127). A byte with the high +/// bit clear has special meaning (explained later). +/// +/// The rest gets bizarre... +/// +/// The LPD8806 does not perform an in-unison latch (which would display the +/// newly-transmitted data all at once). Rather, each individual byte (even +/// the separate G, R, B components of each LED) is latched AS IT ARRIVES... +/// or more accurately, as the first bit of the subsequent byte arrives and +/// is passed through. So the strip actually refreshes at the speed the data +/// is issued, not instantaneously (this can be observed by greatly reducing +/// the data rate). This has implications for POV displays and light painting +/// applications. The 'subsequent' rule also means that at least one extra +/// byte must follow the last pixel, in order for the final blue LED to latch. +/// +/// To reset the pass-through behavior and begin sending new data to the start +/// of the strip, a number of zero bytes must be issued (remember, all color +/// data bytes have the high bit set, thus are in the range 128 to 255, so the +/// zero is 'special'). This should be done before each full payload of color +/// values to the strip. Curiously, zero bytes can only travel one meter (32 +/// LEDs) down the line before needing backup; the next meter requires an +/// extra zero byte, and so forth. Longer strips will require progressively +/// more zeros. *(see note below) +/// +/// In the interest of efficiency, it's possible to combine the former EOD +/// extra latch byte and the latter zero reset...the same data can do double +/// duty, latching the last blue LED while also resetting the strip for the +/// next payload. +/// +/// So: reset byte(s) of suitable length are issued once at startup to 'prime' +/// the strip to a known ready state. After each subsequent LED color payload, +/// these reset byte(s) are then issued at the END of each payload, both to +/// latch the last LED and to prep the strip for the start of the next payload +/// (even if that data does not arrive immediately). This avoids a tiny bit +/// of latency as the new color payload can begin issuing immediately on some +/// signal, such as a timer or GPIO trigger. +/// +/// Technically these zero byte(s) are not a latch, as the color data (save +/// for the last byte) is already latched. It's a start-of-data marker, or +/// an indicator to clear the thing-that's-not-a-shift-register. But for +/// conversational consistency with other LED drivers, we'll refer to it as +/// a 'latch' anyway. +/// +/// This has been validated independently with multiple customers' +/// hardware. Please do not report as a bug or issue pull requests for +/// this. Fewer zeros sometimes gives the *illusion* of working, the first +/// payload will correctly load and latch, but subsequent frames will drop +/// data at the end. The data shortfall won't always be visually apparent +/// depending on the color data loaded on the prior and subsequent frames. +/// Tested. Confirmed. Fact. +/// +/// +/// The summary of the story is that the following needs to be writen on the spi-device: +/// 1RRRRRRR 1GGGGGGG 1BBBBBBB 1RRRRRRR 1GGGGGGG ... ... 1GGGGGGG 1BBBBBBB 00000000 00000000 ... +/// |---------led_1----------| |---------led_2-- -led_n----------| |----clear data-- +/// +/// The number of zeroes in the 'clear data' is (#led/32 + 1)bytes (or *8 for bits) +/// +class LedDeviceLpd8806 : public LedSpiDevice +{ +public: + /// + /// Constructs the LedDevice for a string containing leds of the type LPD8806 + /// + /// @param[in] outputDevice The name of the output device (eg '/dev/spidev0.0') + /// @param[in] baudrate The used baudrate for writing to the output device + /// + LedDeviceLpd8806(const std::string& outputDevice, const unsigned baudrate); + + /// + /// 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 &ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private: + /// The buffer containing the packed RGB values + std::vector _ledBuffer; +};