[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.
This commit is contained in:
Ryan Blue
2025-02-07 15:36:41 -05:00
committed by GitHub
parent a0976a1fd9
commit b60b2b64bd
12 changed files with 695 additions and 3 deletions

View File

@@ -12,13 +12,57 @@ import edu.wpi.first.hal.PWMJNI;
/**
* A class for driving addressable LEDs, such as WS2812B, WS2815, and NeoPixels.
*
* <p>By default, the timing supports WS2812B and WS2815 LEDs, but is configurable using
* setBitTiming()
* <p>By default, the timing supports WS2812B and WS2815 LEDs, but is configurable using {@link
* #setBitTiming(int, int, int, int)}
*
* <p>Some LEDs use a different color order than the default GRB. The color order is configurable
* using {@link #setColorOrder(ColorOrder)}.
*
* <p>Only 1 LED driver is currently supported by the roboRIO. However, multiple LED strips can be
* connected in series and controlled from the single driver.
*/
public class AddressableLED implements AutoCloseable {
/** Order that color data is sent over the wire. */
public enum ColorOrder {
/** RGB order. */
kRGB(AddressableLEDJNI.COLOR_ORDER_RGB),
/** RBG order. */
kRBG(AddressableLEDJNI.COLOR_ORDER_RBG),
/** BGR order. */
kBGR(AddressableLEDJNI.COLOR_ORDER_BGR),
/** BRG order. */
kBRG(AddressableLEDJNI.COLOR_ORDER_BRG),
/** GBR order. */
kGBR(AddressableLEDJNI.COLOR_ORDER_GBR),
/** GRB order. This is the default order. */
kGRB(AddressableLEDJNI.COLOR_ORDER_GRB);
/** The native value for this ColorOrder. */
public final int value;
ColorOrder(int value) {
this.value = value;
}
/**
* Gets a color order from an int value.
*
* @param value int value
* @return color order
*/
public ColorOrder fromValue(int value) {
return switch (value) {
case AddressableLEDJNI.COLOR_ORDER_RBG -> kRBG;
case AddressableLEDJNI.COLOR_ORDER_BGR -> kBGR;
case AddressableLEDJNI.COLOR_ORDER_BRG -> kBRG;
case AddressableLEDJNI.COLOR_ORDER_GRB -> kGRB;
case AddressableLEDJNI.COLOR_ORDER_GBR -> kGBR;
case AddressableLEDJNI.COLOR_ORDER_RGB -> kRGB;
default -> kGRB;
};
}
}
private final int m_pwmHandle;
private final int m_handle;
@@ -43,6 +87,17 @@ public class AddressableLED implements AutoCloseable {
}
}
/**
* Sets the color order for this AddressableLED. The default order is GRB.
*
* <p>This will take effect on the next call to {@link #setData(AddressableLEDBuffer)}.
*
* @param order the color order
*/
public void setColorOrder(ColorOrder order) {
AddressableLEDJNI.setColorOrder(m_handle, order.value);
}
/**
* Sets the length of the LED strip.
*