mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-22 01:11:42 +00:00
[hal, wpilib] Update Addressable LED support (#8100)
This commit is contained in:
@@ -6,19 +6,18 @@ package edu.wpi.first.wpilibj;
|
||||
|
||||
import edu.wpi.first.hal.AddressableLEDJNI;
|
||||
import edu.wpi.first.hal.HAL;
|
||||
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 {@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.
|
||||
* <p>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.
|
||||
*/
|
||||
public class AddressableLED implements AutoCloseable {
|
||||
/** Order that color data is sent over the wire. */
|
||||
@@ -62,18 +61,21 @@ public class AddressableLED implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private final int m_pwmHandle;
|
||||
private final int m_channel;
|
||||
private final int m_handle;
|
||||
private int m_start;
|
||||
private int m_length;
|
||||
private ColorOrder m_colorOrder = ColorOrder.kGRB;
|
||||
|
||||
/**
|
||||
* Constructs a new driver for a specific port.
|
||||
* Constructs a new driver for a specific channel.
|
||||
*
|
||||
* @param port the output port to use (Must be a PWM header, not on MXP)
|
||||
* @param channel the output channel to use
|
||||
*/
|
||||
public AddressableLED(int port) {
|
||||
m_pwmHandle = PWMJNI.initializePWMPort(port);
|
||||
m_handle = AddressableLEDJNI.initialize(m_pwmHandle);
|
||||
HAL.reportUsage("IO", port, "AddressableLED");
|
||||
public AddressableLED(int channel) {
|
||||
m_channel = channel;
|
||||
m_handle = AddressableLEDJNI.initialize(channel);
|
||||
HAL.reportUsage("IO", channel, "AddressableLED");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,9 +83,15 @@ public class AddressableLED implements AutoCloseable {
|
||||
if (m_handle != 0) {
|
||||
AddressableLEDJNI.free(m_handle);
|
||||
}
|
||||
if (m_pwmHandle != 0) {
|
||||
PWMJNI.freePWMPort(m_pwmHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the output channel.
|
||||
*
|
||||
* @return the output channel
|
||||
*/
|
||||
public int getChannel() {
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,71 +102,63 @@ public class AddressableLED implements AutoCloseable {
|
||||
* @param order the color order
|
||||
*/
|
||||
public void setColorOrder(ColorOrder order) {
|
||||
AddressableLEDJNI.setColorOrder(m_handle, order.value);
|
||||
m_colorOrder = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display start of the LED strip in the global buffer.
|
||||
*
|
||||
* @param start the strip start, in LEDs
|
||||
*/
|
||||
public void setStart(int start) {
|
||||
m_start = start;
|
||||
AddressableLEDJNI.setStart(m_handle, start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display start of the LED strip in the global buffer.
|
||||
*
|
||||
* @return the strip start, in LEDs
|
||||
*/
|
||||
public int getStart() {
|
||||
return m_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the LED strip.
|
||||
*
|
||||
* <p>Calling this is an expensive call, so it's best to call it once, then just update data.
|
||||
*
|
||||
* <p>The max length is 5460 LEDs.
|
||||
*
|
||||
* @param length the strip length
|
||||
* @param length the strip length, in LEDs
|
||||
*/
|
||||
public void setLength(int length) {
|
||||
m_length = length;
|
||||
AddressableLEDJNI.setLength(m_handle, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the LED output data.
|
||||
*
|
||||
* <p>If the output is enabled, this will start writing the next data cycle. It is safe to call,
|
||||
* even while output is enabled.
|
||||
* <p>This will write to the global buffer starting at the location set by setStart() and up to
|
||||
* the length set by setLength().
|
||||
*
|
||||
* @param buffer the buffer to write
|
||||
*/
|
||||
public void setData(AddressableLEDBuffer buffer) {
|
||||
AddressableLEDJNI.setData(m_handle, buffer.m_buffer);
|
||||
AddressableLEDJNI.setData(
|
||||
m_start,
|
||||
m_colorOrder.value,
|
||||
buffer.m_buffer,
|
||||
0,
|
||||
3 * Math.min(m_length, buffer.getLength()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bit timing.
|
||||
* Sets the LED output data at an arbitrary location in the global buffer.
|
||||
*
|
||||
* <p>By default, the driver is set up to drive WS2812B and WS2815, so nothing needs to be set for
|
||||
* those.
|
||||
*
|
||||
* @param highTime0 high time for 0 bit in nanoseconds (default 400 ns)
|
||||
* @param lowTime0 low time for 0 bit in nanoseconds (default 900 ns)
|
||||
* @param highTime1 high time for 1 bit in nanoseconds (default 900 ns)
|
||||
* @param lowTime1 low time for 1 bit in nanoseconds (default 600 ns)
|
||||
* @param start the start location, in LEDs
|
||||
* @param colorOrder the color order
|
||||
* @param buffer the buffer to write
|
||||
*/
|
||||
public void setBitTiming(int highTime0, int lowTime0, int highTime1, int lowTime1) {
|
||||
AddressableLEDJNI.setBitTiming(m_handle, highTime0, lowTime0, highTime1, lowTime1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sync time.
|
||||
*
|
||||
* <p>The sync time is the time to hold output so LEDs enable. Default set for WS2812B and WS2815.
|
||||
*
|
||||
* @param syncTime the sync time in microseconds (default 280 μs)
|
||||
*/
|
||||
public void setSyncTime(int syncTime) {
|
||||
AddressableLEDJNI.setSyncTime(m_handle, syncTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the output.
|
||||
*
|
||||
* <p>The output writes continuously.
|
||||
*/
|
||||
public void start() {
|
||||
AddressableLEDJNI.start(m_handle);
|
||||
}
|
||||
|
||||
/** Stops the output. */
|
||||
public void stop() {
|
||||
AddressableLEDJNI.stop(m_handle);
|
||||
public static void setGlobalData(int start, ColorOrder colorOrder, AddressableLEDBuffer buffer) {
|
||||
AddressableLEDJNI.setData(start, colorOrder.value, buffer.m_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public class AddressableLEDBuffer implements LEDReader, LEDWriter {
|
||||
* @param length The length of the buffer in pixels
|
||||
*/
|
||||
public AddressableLEDBuffer(int length) {
|
||||
m_buffer = new byte[length * 4];
|
||||
m_buffer = new byte[length * 3];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,10 +27,9 @@ public class AddressableLEDBuffer implements LEDReader, LEDWriter {
|
||||
*/
|
||||
@Override
|
||||
public void setRGB(int index, int r, int g, int b) {
|
||||
m_buffer[index * 4] = (byte) b;
|
||||
m_buffer[(index * 4) + 1] = (byte) g;
|
||||
m_buffer[(index * 4) + 2] = (byte) r;
|
||||
m_buffer[(index * 4) + 3] = 0;
|
||||
m_buffer[index * 3] = (byte) b;
|
||||
m_buffer[(index * 3) + 1] = (byte) g;
|
||||
m_buffer[(index * 3) + 2] = (byte) r;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +39,7 @@ public class AddressableLEDBuffer implements LEDReader, LEDWriter {
|
||||
*/
|
||||
@Override
|
||||
public int getLength() {
|
||||
return m_buffer.length / 4;
|
||||
return m_buffer.length / 3;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +50,7 @@ public class AddressableLEDBuffer implements LEDReader, LEDWriter {
|
||||
*/
|
||||
@Override
|
||||
public int getRed(int index) {
|
||||
return m_buffer[index * 4 + 2] & 0xFF;
|
||||
return m_buffer[index * 3 + 2] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +61,7 @@ public class AddressableLEDBuffer implements LEDReader, LEDWriter {
|
||||
*/
|
||||
@Override
|
||||
public int getGreen(int index) {
|
||||
return m_buffer[index * 4 + 1] & 0xFF;
|
||||
return m_buffer[index * 3 + 1] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +72,7 @@ public class AddressableLEDBuffer implements LEDReader, LEDWriter {
|
||||
*/
|
||||
@Override
|
||||
public int getBlue(int index) {
|
||||
return m_buffer[index * 4] & 0xFF;
|
||||
return m_buffer[index * 3] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,15 +8,18 @@ import edu.wpi.first.hal.simulation.AddressableLEDDataJNI;
|
||||
import edu.wpi.first.hal.simulation.ConstBufferCallback;
|
||||
import edu.wpi.first.hal.simulation.NotifyCallback;
|
||||
import edu.wpi.first.wpilibj.AddressableLED;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/** Class to control a simulated addressable LED. */
|
||||
public class AddressableLEDSim {
|
||||
private final int m_index;
|
||||
private final int m_channel;
|
||||
|
||||
/** Constructs for the first addressable LED. */
|
||||
public AddressableLEDSim() {
|
||||
m_index = 0;
|
||||
/**
|
||||
* Constructs an addressable LED for a specific channel.
|
||||
*
|
||||
* @param channel output channel
|
||||
*/
|
||||
public AddressableLEDSim(int channel) {
|
||||
m_channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -26,38 +29,7 @@ public class AddressableLEDSim {
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public AddressableLEDSim(AddressableLED addressableLED) {
|
||||
// there is only support for a single AddressableLED, so no lookup
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
private AddressableLEDSim(int index) {
|
||||
m_index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AddressableLEDSim for a PWM channel.
|
||||
*
|
||||
* @param pwmChannel PWM channel
|
||||
* @return Simulated object
|
||||
* @throws NoSuchElementException if no AddressableLED is configured for that channel
|
||||
*/
|
||||
public static AddressableLEDSim createForChannel(int pwmChannel) {
|
||||
int index = AddressableLEDDataJNI.findForChannel(pwmChannel);
|
||||
if (index < 0) {
|
||||
throw new NoSuchElementException("no addressable LED found for PWM channel " + pwmChannel);
|
||||
}
|
||||
return new AddressableLEDSim(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AddressableLEDSim for a simulated index. The index is incremented for each simulated
|
||||
* AddressableLED.
|
||||
*
|
||||
* @param index simulator index
|
||||
* @return Simulated object
|
||||
*/
|
||||
public static AddressableLEDSim createForIndex(int index) {
|
||||
return new AddressableLEDSim(index);
|
||||
m_channel = addressableLED.getChannel();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,8 +40,8 @@ public class AddressableLEDSim {
|
||||
* @return the {@link CallbackStore} object associated with this callback.
|
||||
*/
|
||||
public CallbackStore registerInitializedCallback(NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = AddressableLEDDataJNI.registerInitializedCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, AddressableLEDDataJNI::cancelInitializedCallback);
|
||||
int uid = AddressableLEDDataJNI.registerInitializedCallback(m_channel, callback, initialNotify);
|
||||
return new CallbackStore(m_channel, uid, AddressableLEDDataJNI::cancelInitializedCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +50,7 @@ public class AddressableLEDSim {
|
||||
* @return true if initialized
|
||||
*/
|
||||
public boolean getInitialized() {
|
||||
return AddressableLEDDataJNI.getInitialized(m_index);
|
||||
return AddressableLEDDataJNI.getInitialized(m_channel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,37 +59,37 @@ public class AddressableLEDSim {
|
||||
* @param initialized the new value
|
||||
*/
|
||||
public void setInitialized(boolean initialized) {
|
||||
AddressableLEDDataJNI.setInitialized(m_index, initialized);
|
||||
AddressableLEDDataJNI.setInitialized(m_channel, initialized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback on the output port.
|
||||
* Register a callback on the start.
|
||||
*
|
||||
* @param callback the callback that will be called whenever the output port is changed
|
||||
* @param callback the callback that will be called whenever the start is changed
|
||||
* @param initialNotify if true, the callback will be run on the initial value
|
||||
* @return the {@link CallbackStore} object associated with this callback.
|
||||
*/
|
||||
public CallbackStore registerOutputPortCallback(NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = AddressableLEDDataJNI.registerOutputPortCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, AddressableLEDDataJNI::cancelOutputPortCallback);
|
||||
public CallbackStore registerStartCallback(NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = AddressableLEDDataJNI.registerStartCallback(m_channel, callback, initialNotify);
|
||||
return new CallbackStore(m_channel, uid, AddressableLEDDataJNI::cancelStartCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output port.
|
||||
* Get the start.
|
||||
*
|
||||
* @return the output port
|
||||
* @return the start
|
||||
*/
|
||||
public int getOutputPort() {
|
||||
return AddressableLEDDataJNI.getOutputPort(m_index);
|
||||
public int getStart() {
|
||||
return AddressableLEDDataJNI.getStart(m_channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the output port.
|
||||
* Change the start.
|
||||
*
|
||||
* @param outputPort the new output port
|
||||
* @param start the new start
|
||||
*/
|
||||
public void setOutputPort(int outputPort) {
|
||||
AddressableLEDDataJNI.setOutputPort(m_index, outputPort);
|
||||
public void setStart(int start) {
|
||||
AddressableLEDDataJNI.setStart(m_channel, start);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,8 +100,8 @@ public class AddressableLEDSim {
|
||||
* @return the {@link CallbackStore} object associated with this callback.
|
||||
*/
|
||||
public CallbackStore registerLengthCallback(NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = AddressableLEDDataJNI.registerLengthCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, AddressableLEDDataJNI::cancelLengthCallback);
|
||||
int uid = AddressableLEDDataJNI.registerLengthCallback(m_channel, callback, initialNotify);
|
||||
return new CallbackStore(m_channel, uid, AddressableLEDDataJNI::cancelLengthCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +110,7 @@ public class AddressableLEDSim {
|
||||
* @return the length
|
||||
*/
|
||||
public int getLength() {
|
||||
return AddressableLEDDataJNI.getLength(m_index);
|
||||
return AddressableLEDDataJNI.getLength(m_channel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,37 +119,7 @@ public class AddressableLEDSim {
|
||||
* @param length the new value
|
||||
*/
|
||||
public void setLength(int length) {
|
||||
AddressableLEDDataJNI.setLength(m_index, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback on whether the LEDs are running.
|
||||
*
|
||||
* @param callback the callback that will be called whenever the LED state is changed
|
||||
* @param initialNotify if true, the callback will be run on the initial value
|
||||
* @return the {@link CallbackStore} object associated with this callback.
|
||||
*/
|
||||
public CallbackStore registerRunningCallback(NotifyCallback callback, boolean initialNotify) {
|
||||
int uid = AddressableLEDDataJNI.registerRunningCallback(m_index, callback, initialNotify);
|
||||
return new CallbackStore(m_index, uid, AddressableLEDDataJNI::cancelRunningCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the LEDs are running.
|
||||
*
|
||||
* @return true if they are
|
||||
*/
|
||||
public boolean getRunning() {
|
||||
return AddressableLEDDataJNI.getRunning(m_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change whether the LEDs are active.
|
||||
*
|
||||
* @param running the new value
|
||||
*/
|
||||
public void setRunning(boolean running) {
|
||||
AddressableLEDDataJNI.setRunning(m_index, running);
|
||||
AddressableLEDDataJNI.setLength(m_channel, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,9 +128,9 @@ public class AddressableLEDSim {
|
||||
* @param callback the callback that will be called whenever the LED data is changed
|
||||
* @return the {@link CallbackStore} object associated with this callback.
|
||||
*/
|
||||
public CallbackStore registerDataCallback(ConstBufferCallback callback) {
|
||||
int uid = AddressableLEDDataJNI.registerDataCallback(m_index, callback);
|
||||
return new CallbackStore(m_index, uid, AddressableLEDDataJNI::cancelDataCallback);
|
||||
public static CallbackStore registerDataCallback(ConstBufferCallback callback) {
|
||||
int uid = AddressableLEDDataJNI.registerDataCallback(callback);
|
||||
return new CallbackStore(uid, AddressableLEDDataJNI::cancelDataCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,7 +139,7 @@ public class AddressableLEDSim {
|
||||
* @return the LED data
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return AddressableLEDDataJNI.getData(m_index);
|
||||
return getGlobalData(getStart(), getLength());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,11 +148,32 @@ public class AddressableLEDSim {
|
||||
* @param data the new data
|
||||
*/
|
||||
public void setData(byte[] data) {
|
||||
AddressableLEDDataJNI.setData(m_index, data);
|
||||
setGlobalData(getStart(), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the global LED data.
|
||||
*
|
||||
* @param start start, in LEDs
|
||||
* @param length length, in LEDs
|
||||
* @return the LED data
|
||||
*/
|
||||
public static byte[] getGlobalData(int start, int length) {
|
||||
return AddressableLEDDataJNI.getData(start, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the global LED data.
|
||||
*
|
||||
* @param start start, in LEDs
|
||||
* @param data the new data
|
||||
*/
|
||||
public static void setGlobalData(int start, byte[] data) {
|
||||
AddressableLEDDataJNI.setData(start, data);
|
||||
}
|
||||
|
||||
/** Reset all simulation data for this LED object. */
|
||||
public void resetData() {
|
||||
AddressableLEDDataJNI.resetData(m_index);
|
||||
AddressableLEDDataJNI.resetData(m_channel);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user