2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2021-06-10 00:00:06 -07:00
|
|
|
#include <initializer_list>
|
2022-10-15 16:33:14 -07:00
|
|
|
#include <span>
|
2019-11-17 16:39:38 -08:00
|
|
|
|
2024-09-07 13:58:15 -04:00
|
|
|
#include <hal/AddressableLED.h>
|
2019-11-17 16:39:38 -08:00
|
|
|
#include <hal/AddressableLEDTypes.h>
|
|
|
|
|
#include <hal/Types.h>
|
2020-06-29 22:25:09 -07:00
|
|
|
#include <units/time.h>
|
2019-11-17 16:39:38 -08:00
|
|
|
|
2019-12-30 01:01:20 -05:00
|
|
|
#include "util/Color.h"
|
|
|
|
|
#include "util/Color8Bit.h"
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
namespace frc {
|
|
|
|
|
|
|
|
|
|
/**
|
2025-01-13 13:26:54 -06:00
|
|
|
* A class for driving addressable LEDs, such as WS2812B, WS2815, and NeoPixels.
|
2023-04-28 20:54:58 -07:00
|
|
|
*
|
[hal, wpilib] AddressableLED: add support for other color orders (#7102)
Many LED strips use different color order (GRB in particular is common).
This makes the change at the HAL level. This solves 2 problems; first, no code needs to change in the high level drivers, which was challenging for C++, and second, simulation will behave properly as no conversion is needed. The HAL will accept an array of data objects in the same order no matter what the selected output order is, and will convert before sending it to the FPGA for output.
To accomplish this, NEON bulk load/interleave instructions are utilized. The low level implementation (load, store, and alignment functions) come from the Simd Library. The high level implementations are inspired by the image conversion functions in the simd library, but have diverged significantly.
Much of the implementation uses templates and inlined functions rather than runtime parameters; This is a trade off between the size of the generated code and the amount of function calls done at runtime. Currently, the entire conversion operation is inlined.
2025-02-07 15:36:41 -05:00
|
|
|
* Some LEDs use a different color order than the default GRB. The color order
|
|
|
|
|
* is configurable using SetColorOrder().
|
|
|
|
|
*
|
2025-07-21 21:52:10 -07:00
|
|
|
* Up to 1024 LEDs may be controlled in total across all AddressableLED
|
|
|
|
|
* instances. A single global buffer is used for all instances. The start
|
|
|
|
|
* position used for LED data for the output is set via SetStart() and the
|
|
|
|
|
* length of the strip is set via SetLength(). Both of these default to zero, so
|
|
|
|
|
* multiple instances will access the same pixel data unless SetStart() is
|
|
|
|
|
* called to adjust the starting point.
|
2019-11-17 16:39:38 -08:00
|
|
|
*/
|
2021-04-18 20:35:29 -07:00
|
|
|
class AddressableLED {
|
2019-11-17 16:39:38 -08:00
|
|
|
public:
|
[hal, wpilib] AddressableLED: add support for other color orders (#7102)
Many LED strips use different color order (GRB in particular is common).
This makes the change at the HAL level. This solves 2 problems; first, no code needs to change in the high level drivers, which was challenging for C++, and second, simulation will behave properly as no conversion is needed. The HAL will accept an array of data objects in the same order no matter what the selected output order is, and will convert before sending it to the FPGA for output.
To accomplish this, NEON bulk load/interleave instructions are utilized. The low level implementation (load, store, and alignment functions) come from the Simd Library. The high level implementations are inspired by the image conversion functions in the simd library, but have diverged significantly.
Much of the implementation uses templates and inlined functions rather than runtime parameters; This is a trade off between the size of the generated code and the amount of function calls done at runtime. Currently, the entire conversion operation is inlined.
2025-02-07 15:36:41 -05:00
|
|
|
/**
|
|
|
|
|
* Order that color data is sent over the wire.
|
|
|
|
|
*/
|
|
|
|
|
enum ColorOrder {
|
|
|
|
|
kRGB = HAL_ALED_RGB, ///< RGB order
|
|
|
|
|
kRBG = HAL_ALED_RBG, ///< RBG order
|
|
|
|
|
kBGR = HAL_ALED_BGR, ///< BGR order
|
|
|
|
|
kBRG = HAL_ALED_BRG, ///< BRG order
|
|
|
|
|
kGBR = HAL_ALED_GBR, ///< GBR order
|
|
|
|
|
kGRB = HAL_ALED_GRB ///< GRB order. This is the default order.
|
|
|
|
|
};
|
|
|
|
|
|
2019-11-17 16:39:38 -08:00
|
|
|
class LEDData : public HAL_AddressableLEDData {
|
|
|
|
|
public:
|
|
|
|
|
LEDData() : LEDData(0, 0, 0) {}
|
|
|
|
|
LEDData(int _r, int _g, int _b) {
|
|
|
|
|
r = _r;
|
|
|
|
|
g = _g;
|
|
|
|
|
b = _b;
|
|
|
|
|
}
|
2019-11-18 22:12:17 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A helper method to set all values of the LED.
|
2019-11-29 15:16:57 -08:00
|
|
|
*
|
|
|
|
|
* @param r the r value [0-255]
|
|
|
|
|
* @param g the g value [0-255]
|
|
|
|
|
* @param b the b value [0-255]
|
2019-11-18 22:12:17 -08:00
|
|
|
*/
|
2019-11-29 15:16:57 -08:00
|
|
|
void SetRGB(int r, int g, int b) {
|
2019-11-18 22:12:17 -08:00
|
|
|
this->r = r;
|
|
|
|
|
this->g = g;
|
|
|
|
|
this->b = b;
|
|
|
|
|
}
|
2019-11-29 15:16:57 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A helper method to set all values of the LED.
|
|
|
|
|
*
|
|
|
|
|
* @param h the h value [0-180]
|
|
|
|
|
* @param s the s value [0-255]
|
|
|
|
|
* @param v the v value [0-255]
|
|
|
|
|
*/
|
|
|
|
|
void SetHSV(int h, int s, int v);
|
2019-12-30 01:01:20 -05:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Sets a specific LED in the buffer.
|
|
|
|
|
*
|
|
|
|
|
* @param color The color of the LED
|
|
|
|
|
*/
|
|
|
|
|
void SetLED(const Color& color) {
|
|
|
|
|
this->r = color.red * 255;
|
|
|
|
|
this->g = color.green * 255;
|
|
|
|
|
this->b = color.blue * 255;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Sets a specific LED in the buffer.
|
|
|
|
|
*
|
|
|
|
|
* @param color The color of the LED
|
|
|
|
|
*/
|
|
|
|
|
void SetLED(const Color8Bit& color) {
|
|
|
|
|
this->r = color.red;
|
|
|
|
|
this->g = color.green;
|
|
|
|
|
this->b = color.blue;
|
|
|
|
|
}
|
2019-11-17 16:39:38 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2025-07-21 21:52:10 -07:00
|
|
|
* Constructs a new driver for a specific channel.
|
2019-11-17 16:39:38 -08:00
|
|
|
*
|
2025-07-21 21:52:10 -07:00
|
|
|
* @param channel the output channel to use
|
2019-11-17 16:39:38 -08:00
|
|
|
*/
|
2025-07-21 21:52:10 -07:00
|
|
|
explicit AddressableLED(int channel);
|
2019-11-17 16:39:38 -08:00
|
|
|
|
2024-08-22 11:48:19 -04:00
|
|
|
AddressableLED(AddressableLED&&) = default;
|
|
|
|
|
AddressableLED& operator=(AddressableLED&&) = default;
|
|
|
|
|
|
2025-07-21 21:52:10 -07:00
|
|
|
/**
|
|
|
|
|
* Gets the channel for this addressable LED.
|
|
|
|
|
*
|
|
|
|
|
* @return channel
|
|
|
|
|
*/
|
|
|
|
|
int GetChannel() const { return m_channel; }
|
|
|
|
|
|
[hal, wpilib] AddressableLED: add support for other color orders (#7102)
Many LED strips use different color order (GRB in particular is common).
This makes the change at the HAL level. This solves 2 problems; first, no code needs to change in the high level drivers, which was challenging for C++, and second, simulation will behave properly as no conversion is needed. The HAL will accept an array of data objects in the same order no matter what the selected output order is, and will convert before sending it to the FPGA for output.
To accomplish this, NEON bulk load/interleave instructions are utilized. The low level implementation (load, store, and alignment functions) come from the Simd Library. The high level implementations are inspired by the image conversion functions in the simd library, but have diverged significantly.
Much of the implementation uses templates and inlined functions rather than runtime parameters; This is a trade off between the size of the generated code and the amount of function calls done at runtime. Currently, the entire conversion operation is inlined.
2025-02-07 15:36:41 -05:00
|
|
|
/**
|
|
|
|
|
* Sets the color order for this AddressableLED. The default order is GRB.
|
|
|
|
|
*
|
|
|
|
|
* This will take effect on the next call to SetData().
|
|
|
|
|
*
|
|
|
|
|
* @param order the color order
|
|
|
|
|
*/
|
|
|
|
|
void SetColorOrder(ColorOrder order);
|
|
|
|
|
|
2019-11-17 16:39:38 -08:00
|
|
|
/**
|
2025-07-21 21:52:10 -07:00
|
|
|
* Sets the display start of the LED strip in the global buffer.
|
2019-11-17 16:39:38 -08:00
|
|
|
*
|
2025-07-21 21:52:10 -07:00
|
|
|
* @param start the strip start, in LEDs
|
2019-11-17 16:39:38 -08:00
|
|
|
*/
|
2025-07-21 21:52:10 -07:00
|
|
|
void SetStart(int start);
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
/**
|
2025-07-21 21:52:10 -07:00
|
|
|
* Gets the display start of the LED strip in the global buffer.
|
2019-11-17 16:39:38 -08:00
|
|
|
*
|
2025-07-21 21:52:10 -07:00
|
|
|
* @return the strip start, in LEDs
|
2019-11-17 16:39:38 -08:00
|
|
|
*/
|
2025-07-21 21:52:10 -07:00
|
|
|
int GetStart() const { return m_start; }
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
/**
|
2025-07-21 21:52:10 -07:00
|
|
|
* Sets the length of the LED strip.
|
2019-11-17 16:39:38 -08:00
|
|
|
*
|
2025-07-21 21:52:10 -07:00
|
|
|
* @param length the strip length, in LEDs
|
2019-11-17 16:39:38 -08:00
|
|
|
*/
|
2025-07-21 21:52:10 -07:00
|
|
|
void SetLength(int length);
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
/**
|
2025-07-21 21:52:10 -07:00
|
|
|
* Sets the LED output data. This will write to the global buffer starting at
|
|
|
|
|
* the location set by SetStart() and up to the length set by SetLength().
|
2019-11-17 16:39:38 -08:00
|
|
|
*
|
2025-07-21 21:52:10 -07:00
|
|
|
* @param ledData the buffer to write
|
2019-11-17 16:39:38 -08:00
|
|
|
*/
|
2025-07-21 21:52:10 -07:00
|
|
|
void SetData(std::span<const LEDData> ledData);
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
/**
|
2025-07-21 21:52:10 -07:00
|
|
|
* Sets the LED output data. This will write to the global buffer starting at
|
|
|
|
|
* the location set by SetStart() and up to the length set by SetLength().
|
2019-11-17 16:39:38 -08:00
|
|
|
*
|
2025-07-21 21:52:10 -07:00
|
|
|
* @param ledData the buffer to write
|
2019-11-17 16:39:38 -08:00
|
|
|
*/
|
2025-07-21 21:52:10 -07:00
|
|
|
void SetData(std::initializer_list<LEDData> ledData);
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
/**
|
2025-07-21 21:52:10 -07:00
|
|
|
* Sets the LED output data at an arbitrary location in the global buffer.
|
2019-11-17 16:39:38 -08:00
|
|
|
*
|
2025-07-21 21:52:10 -07:00
|
|
|
* @param start the start location, in LEDs
|
|
|
|
|
* @param colorOrder the color order
|
|
|
|
|
* @param ledData the buffer to write
|
2019-11-17 16:39:38 -08:00
|
|
|
*/
|
2025-07-21 21:52:10 -07:00
|
|
|
static void SetGlobalData(int start, ColorOrder colorOrder,
|
|
|
|
|
std::span<const LEDData> ledData);
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
private:
|
2024-09-07 13:58:15 -04:00
|
|
|
hal::Handle<HAL_AddressableLEDHandle, HAL_FreeAddressableLED> m_handle;
|
2025-07-21 21:52:10 -07:00
|
|
|
int m_channel;
|
|
|
|
|
int m_start{0};
|
|
|
|
|
int m_length{0};
|
|
|
|
|
ColorOrder m_colorOrder{kGRB};
|
2019-11-17 16:39:38 -08:00
|
|
|
};
|
[hal, wpilib] AddressableLED: add support for other color orders (#7102)
Many LED strips use different color order (GRB in particular is common).
This makes the change at the HAL level. This solves 2 problems; first, no code needs to change in the high level drivers, which was challenging for C++, and second, simulation will behave properly as no conversion is needed. The HAL will accept an array of data objects in the same order no matter what the selected output order is, and will convert before sending it to the FPGA for output.
To accomplish this, NEON bulk load/interleave instructions are utilized. The low level implementation (load, store, and alignment functions) come from the Simd Library. The high level implementations are inspired by the image conversion functions in the simd library, but have diverged significantly.
Much of the implementation uses templates and inlined functions rather than runtime parameters; This is a trade off between the size of the generated code and the amount of function calls done at runtime. Currently, the entire conversion operation is inlined.
2025-02-07 15:36:41 -05:00
|
|
|
|
|
|
|
|
constexpr auto format_as(AddressableLED::ColorOrder order) {
|
|
|
|
|
return static_cast<int32_t>(order);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-17 16:39:38 -08:00
|
|
|
} // namespace frc
|