diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 575c76e7..2cf7e07b 100644 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -470,7 +470,7 @@ $(document).ready(function() { // create led device selection ledDevices = serverInfo.ledDevices.available - devRPiSPI = ['apa102', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'ws2812spi']; + devRPiSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'ws2812spi']; devRPiPWM = ['ws281x']; devRPiGPIO = ['piblaster']; devNET = ['atmoorb', 'fadecandy', 'philipshue', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udph801', 'udpraw']; diff --git a/libsrc/leddevice/LedDeviceSchemas.qrc b/libsrc/leddevice/LedDeviceSchemas.qrc index bd7afbae..9536835f 100644 --- a/libsrc/leddevice/LedDeviceSchemas.qrc +++ b/libsrc/leddevice/LedDeviceSchemas.qrc @@ -29,6 +29,7 @@ schemas/schema-udpraw.json schemas/schema-ws2801.json schemas/schema-ws2812spi.json + schemas/schema-apa104.json schemas/schema-ws281x.json schemas/schema-karate.json diff --git a/libsrc/leddevice/dev_spi/LedDeviceAPA104.cpp b/libsrc/leddevice/dev_spi/LedDeviceAPA104.cpp new file mode 100644 index 00000000..3a99f1c1 --- /dev/null +++ b/libsrc/leddevice/dev_spi/LedDeviceAPA104.cpp @@ -0,0 +1,95 @@ +#include "LedDeviceAPA104.h" + +/* +From the data sheet: + +(TH+TL=1.25μs±600ns) + +T0H, 0 code, high level time, 350ns ±150ns +T0L, 0 code, low level time, 1360ns ±150ns +T1H, 1 code, high level time, 360ns ±150ns +T1L, 1 code, low level time, 45ns ±150ns +WT, Wait for the processing time, NA +Trst, Reset code,low level time, 24µs + +To normalise the pulse times so they fit in 4 SPI bits: + +On the assumption that the "low" time doesnt matter much + +A SPI bit time of 0.40uS = 2.5 Mbit/sec +T0 is sent as 1000 +T1 is sent as 1110 + +With a bit of excel testing, we can work out the maximum and minimum speeds: +2000000 MIN +2235000 AVG +2470000 MAX + +Wait time: +Not Applicable for WS2812 + +Reset time: +using the max of 2470000, the bit time is 405nS +Reset time is 24uS = 59 bits = 8 bytes + +*/ + +LedDeviceAPA104::LedDeviceAPA104(const QJsonObject &deviceConfig) + : ProviderSpi() + , SPI_BYTES_PER_COLOUR(4) + , SPI_FRAME_END_LATCH_BYTES(116) + , bitpair_to_byte { + 0b10001000, + 0b10001100, + 0b11001000, + 0b11001100, + } +{ + _deviceReady = init(deviceConfig); +} + +LedDevice* LedDeviceAPA104::construct(const QJsonObject &deviceConfig) +{ + return new LedDeviceAPA104(deviceConfig); +} + +bool LedDeviceAPA104::init(const QJsonObject &deviceConfig) +{ + _baudRate_Hz = 2235000; + if ( !ProviderSpi::init(deviceConfig) ) + { + return false; + } + WarningIf(( _baudRate_Hz < 2000000 || _baudRate_Hz > 2470000 ), _log, "SPI rate %d outside recommended range (2000000 -> 2470000)", _baudRate_Hz); + + _ledBuffer.resize(_ledRGBCount * SPI_BYTES_PER_COLOUR + SPI_FRAME_END_LATCH_BYTES, 0x00); + + return true; +} + +int LedDeviceAPA104::write(const std::vector &ledValues) +{ + unsigned spi_ptr = 0; + const int SPI_BYTES_PER_LED = sizeof(ColorRgb) * SPI_BYTES_PER_COLOUR; + + for (const ColorRgb& color : ledValues) + { + uint32_t colorBits = ((unsigned int)color.red << 16) + | ((unsigned int)color.green << 8) + | color.blue; + + for (int j=SPI_BYTES_PER_LED - 1; j>=0; j--) + { + _ledBuffer[spi_ptr+j] = bitpair_to_byte[ colorBits & 0x3 ]; + colorBits >>= 2; + } + spi_ptr += SPI_BYTES_PER_LED; + } + + for (int j=0; j < SPI_FRAME_END_LATCH_BYTES; j++) + { + _ledBuffer[spi_ptr++] = 0; + } + + return writeBytes(_ledBuffer.size(), _ledBuffer.data()); +} diff --git a/libsrc/leddevice/dev_spi/LedDeviceAPA104.h b/libsrc/leddevice/dev_spi/LedDeviceAPA104.h new file mode 100644 index 00000000..23f9500d --- /dev/null +++ b/libsrc/leddevice/dev_spi/LedDeviceAPA104.h @@ -0,0 +1,43 @@ +#pragma once + +// hyperion incluse +#include "ProviderSpi.h" + +/// +/// Implementation of the LedDevice interface for writing to APA104 led device via spi. +/// +class LedDeviceAPA104 : public ProviderSpi +{ +public: + /// + /// Constructs specific LedDevice + /// + /// @param deviceConfig json device config + /// + LedDeviceAPA104(const QJsonObject &deviceConfig); + + /// constructs leddevice + static LedDevice* construct(const QJsonObject &deviceConfig); + + /// + /// Sets configuration + /// + /// @param deviceConfig the json device config + /// @return true if success + virtual bool init(const QJsonObject &deviceConfig); + +private: + /// + /// 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); + + const int SPI_BYTES_PER_COLOUR; + + const int SPI_FRAME_END_LATCH_BYTES; + + uint8_t bitpair_to_byte[4]; +}; diff --git a/libsrc/leddevice/schemas/schema-apa104.json b/libsrc/leddevice/schemas/schema-apa104.json new file mode 100644 index 00000000..0478669d --- /dev/null +++ b/libsrc/leddevice/schemas/schema-apa104.json @@ -0,0 +1,35 @@ +{ + "type":"object", + "required":true, + "properties":{ + "output": { + "type": "string", + "title":"edt_dev_spec_spipath_title", + "enum" : ["/dev/spidev0.0","/dev/spidev0.1"], + "propertyOrder" : 1 + }, + "rate": { + "type": "integer", + "title":"edt_dev_spec_baudrate_title", + "default": 2235000, + "propertyOrder" : 2 + }, + "invert": { + "type": "boolean", + "title":"edt_dev_spec_invert_title", + "default": false, + "propertyOrder" : 3 + }, + "latchTime": { + "type": "integer", + "title":"edt_dev_spec_latchtime_title", + "default": 1, + "append" : "edt_append_ms", + "minimum": 1, + "maximum": 1000, + "access" : "expert", + "propertyOrder" : 4 + } + }, + "additionalProperties": true +}