[hal, wpilib] Update Addressable LED support (#8100)

This commit is contained in:
Peter Johnson
2025-07-21 21:52:10 -07:00
committed by GitHub
parent 8aa312fb6f
commit f3af50fc8e
40 changed files with 857 additions and 1104 deletions

View File

@@ -25,7 +25,6 @@ void glass::DisplayLEDDisplay(LEDDisplayModel* model, int index) {
wpi::SmallVector<LEDDisplayModel::Data, 64> dataBuf;
auto data = model->GetData(dataBuf);
int length = data.size();
bool running = model->IsRunning();
auto& storage = GetStorage();
int& numColumns = storage.GetInt("columns", 10);
@@ -35,7 +34,6 @@ void glass::DisplayLEDDisplay(LEDDisplayModel* model, int index) {
ImGui::PushItemWidth(ImGui::GetFontSize() * 7);
ImGui::LabelText("Length", "%d", length);
ImGui::LabelText("Running", "%s", running ? "Yes" : "No");
ImGui::InputInt("Columns", &numColumns);
{
static const char* options[] = {"Row Major", "Column Major"};
@@ -64,16 +62,9 @@ void glass::DisplayLEDDisplay(LEDDisplayModel* model, int index) {
if (length > static_cast<int>(iData->colors.size())) {
iData->colors.resize(length);
}
if (!running) {
iData->colors[0] = IM_COL32(128, 128, 128, 255);
for (int j = 0; j < length; ++j) {
iData->values[j] = -1;
}
} else {
for (int j = 0; j < length; ++j) {
iData->values[j] = j + 1;
iData->colors[j] = IM_COL32(data[j].r, data[j].g, data[j].b, 255);
}
for (int j = 0; j < length; ++j) {
iData->values[j] = j + 1;
iData->colors[j] = IM_COL32(data[j].r, data[j].g, data[j].b, 255);
}
LEDConfig config;

View File

@@ -30,15 +30,9 @@ void glass::DisplayPWM(PWMModel* model, int index, bool outputsEnabled) {
wpi::format_to_n_c_str(label, sizeof(label), "PWM[{}]###name", index);
}
int led = model->GetAddressableLED();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
if (led >= 0) {
ImGui::LabelText(label, "LED[%d]", led);
} else {
float val = outputsEnabled ? data->GetValue() : 0;
data->LabelText(label, "%0.3f", val);
}
float val = outputsEnabled ? data->GetValue() : 0;
data->LabelText(label, "%0.3f", val);
if (PopupEditName("name", &name)) {
data->SetName(name);
}

View File

@@ -23,11 +23,8 @@ class LEDDisplayModel : public glass::Model {
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t padding;
};
virtual bool IsRunning() = 0;
virtual std::span<const Data> GetData(wpi::SmallVectorImpl<Data>& buf) = 0;
};

View File

@@ -16,9 +16,6 @@ class DoubleSource;
class PWMModel : public Model {
public:
// returns -1 if not an addressable LED
virtual int GetAddressableLED() const = 0;
virtual DoubleSource* GetSpeedData() = 0;
virtual void SetSpeed(double val) = 0;

View File

@@ -4,6 +4,8 @@
package edu.wpi.first.hal;
import java.nio.ByteBuffer;
/**
* Addressable LED HAL JNI Methods.
*
@@ -18,13 +20,13 @@ public class AddressableLEDJNI extends JNIWrapper {
public static final int COLOR_ORDER_GRB = 5;
/**
* Initialize Addressable LED using a PWM Digital handle.
* Create a new instance of an Addressable LED port.
*
* @param pwmHandle handle of the digital port for PWM
* @param channel the smartio channel
* @return Addressable LED handle
* @see "HAL_InitializeAddressableLED"
*/
public static native int initialize(int pwmHandle);
public static native int initialize(int channel);
/**
* Free the Addressable LED Handle.
@@ -35,82 +37,116 @@ public class AddressableLEDJNI extends JNIWrapper {
public static native void free(int handle);
/**
* Sets the color order for the addressable LED output. The default order is GRB.
* Sets the start buffer location used for the LED strip.
*
* <p>This will take effect on the next call to {@link #setData(int, byte[])}.
* <p>All addressable LEDs use a single backing buffer 1024 LEDs in size. The max length for a
* single output is 1024 LEDs (with an offset of zero).
*
* @param handle the Addressable LED handle
* @param colorOrder the color order
* @param start the strip start, in LEDs
*/
public static native void setColorOrder(int handle, int colorOrder);
public static native void setStart(int handle, int start);
/**
* Sets the length of the LED strip.
*
* <p>The max length is 5460 LEDs.
* <p>All addressable LEDs use a single backing buffer 1024 LEDs in size. The max length for a
* single output is 1024 LEDs (with an offset of zero).
*
* @param handle the Addressable LED handle
* @param length the strip length
* @see "HAL_SetAddressableLEDLength"
* @param length the strip length, in LEDs
*/
public static native void setLength(int handle, int 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>All addressable LEDs use a single backing buffer 1024 LEDs in size. This function may be
* used to set part of or all of the buffer.
*
* @param handle the Addressable LED handle
* @param outStart the strip start in the backing buffer, in LEDs
* @param colorOrder the color order
* @param data the buffer to write
* @see "HAL_WriteAddressableLEDData"
*/
public static native void setData(int handle, byte[] data);
public static void setData(int outStart, int colorOrder, byte[] data) {
setData(outStart, colorOrder, data, 0, data.length);
}
/**
* Sets the bit timing.
* Sets the led output data.
*
* <p>By default, the driver is set up to drive WS2812B and WS2815, so nothing needs to be set for
* those.
* <p>All addressable LEDs use a single backing buffer 1024 LEDs in size. This function may be
* used to set part of or all of the buffer.
*
* @param handle the Addressable LED handle
* @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)
* @see "HAL_SetAddressableLEDBitTiming"
* @param outStart the strip start in the backing buffer, in LEDs
* @param colorOrder the color order
* @param data the buffer to write
* @param start offset into data, in bytes
* @param len Length of data, in bytes
*/
public static native void setBitTiming(
int handle, int highTime0, int lowTime0, int highTime1, int lowTime1);
public static native void setData(int outStart, int colorOrder, byte[] data, int start, int len);
/**
* Sets the sync time.
* Sets the led output data.
*
* <p>The sync time is the time to hold output so LEDs enable. Default set for WS2812B and WS2815.
* <p>All addressable LEDs use a single backing buffer 1024 LEDs in size. This function may be
* used to set part of or all of the buffer.
*
* @param handle the Addressable LED handle
* @param syncTime the sync time in microseconds (default 280 μs)
* @see "HAL_SetAddressableLEDSyncTime"
* @param outStart the strip start in the backing buffer, in LEDs
* @param colorOrder the color order
* @param data the buffer to write
*/
public static native void setSyncTime(int handle, int syncTime);
public static void setData(int outStart, int colorOrder, ByteBuffer data) {
int pos = data.position();
setData(outStart, colorOrder, data, pos, data.capacity() - pos);
}
/**
* Starts the output.
* Sets the led output data.
*
* <p>The output writes continuously.
* <p>All addressable LEDs use a single backing buffer 1024 LEDs in size. This function may be
* used to set part of or all of the buffer.
*
* @param handle the Addressable LED handle
* @see "HAL_StartAddressableLEDOutput"
* @param outStart the strip start in the backing buffer, in LEDs
* @param colorOrder the color order
* @param data the buffer to write
* @param start offset into data, in bytes
* @param len Length of data, in bytes
*/
public static native void start(int handle);
public static void setData(int outStart, int colorOrder, ByteBuffer data, int start, int len) {
if (data.isDirect()) {
if (start < 0) {
throw new IndexOutOfBoundsException("start must be >= 0");
}
if (len < 0) {
throw new IndexOutOfBoundsException("len must be >= 0");
}
if ((start + len) > data.capacity()) {
throw new IndexOutOfBoundsException("start + len must be smaller than buffer capacity");
}
setDataFromBuffer(outStart, colorOrder, data, start, len);
} else if (data.hasArray()) {
setData(outStart, colorOrder, data.array(), data.arrayOffset() + start, len);
} else {
throw new UnsupportedOperationException("ByteBuffer must be direct or have a backing array");
}
}
/**
* Stops the output.
* Sets the led output data.
*
* @param handle the Addressable LED handle
* @see "HAL_StopAddressableLEDOutput"
* <p>All addressable LEDs use a single backing buffer 1024 LEDs in size. This function may be
* used to set part of or all of the buffer.
*
* @param outStart the strip start in the backing buffer, in LEDs
* @param colorOrder the color order
* @param data the buffer to write
* @param start offset into data, in bytes
* @param len Length of data, in bytes
*/
public static native void stop(int handle);
private static native void setDataFromBuffer(
int outStart, int colorOrder, ByteBuffer data, int start, int len);
/** Utility class. */
private AddressableLEDJNI() {}

View File

@@ -17,14 +17,14 @@ public class AddressableLEDDataJNI extends JNIWrapper {
public static native void setInitialized(int index, boolean initialized);
public static native int registerOutputPortCallback(
public static native int registerStartCallback(
int index, NotifyCallback callback, boolean initialNotify);
public static native void cancelOutputPortCallback(int index, int uid);
public static native void cancelStartCallback(int index, int uid);
public static native int getOutputPort(int index);
public static native int getStart(int index);
public static native void setOutputPort(int index, int outputPort);
public static native void setStart(int index, int start);
public static native int registerLengthCallback(
int index, NotifyCallback callback, boolean initialNotify);
@@ -35,27 +35,16 @@ public class AddressableLEDDataJNI extends JNIWrapper {
public static native void setLength(int index, int length);
public static native int registerRunningCallback(
int index, NotifyCallback callback, boolean initialNotify);
public static native int registerDataCallback(ConstBufferCallback callback);
public static native void cancelRunningCallback(int index, int uid);
public static native void cancelDataCallback(int uid);
public static native boolean getRunning(int index);
public static native byte[] getData(int start, int length);
public static native void setRunning(int index, boolean running);
public static native int registerDataCallback(int index, ConstBufferCallback callback);
public static native void cancelDataCallback(int index, int uid);
public static native byte[] getData(int index);
public static native void setData(int index, byte[] data);
public static native void setData(int start, byte[] data);
public static native void resetData(int index);
public static native int findForChannel(int channel);
/** Utility class. */
private AddressableLEDDataJNI() {}
}

View File

@@ -13,7 +13,7 @@
using namespace hal;
using namespace wpi::java;
static_assert(sizeof(jbyte) * 4 == sizeof(HAL_AddressableLEDData));
static_assert(sizeof(jbyte) * 3 == sizeof(HAL_AddressableLEDData));
static_assert(edu_wpi_first_hal_AddressableLEDJNI_COLOR_ORDER_RGB ==
HAL_ALED_RGB);
@@ -36,12 +36,12 @@ extern "C" {
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_AddressableLEDJNI_initialize
(JNIEnv* env, jclass, jint handle)
(JNIEnv* env, jclass, jint channel)
{
int32_t status = 0;
auto ret = HAL_InitializeAddressableLED(
static_cast<HAL_DigitalHandle>(handle), &status);
CheckStatus(env, status);
auto stack = wpi::java::GetJavaStackTrace(env, "edu.wpi.first");
auto ret = HAL_InitializeAddressableLED(channel, stack.c_str(), &status);
CheckStatusForceThrow(env, status);
return ret;
}
@@ -61,17 +61,16 @@ Java_edu_wpi_first_hal_AddressableLEDJNI_free
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
* Method: setColorOrder
* Method: setStart
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_AddressableLEDJNI_setColorOrder
(JNIEnv* env, jclass, jint handle, jint colorOrder)
Java_edu_wpi_first_hal_AddressableLEDJNI_setStart
(JNIEnv* env, jclass, jint handle, jint start)
{
int32_t status = 0;
HAL_SetAddressableLEDColorOrder(
static_cast<HAL_AddressableLEDHandle>(handle),
static_cast<HAL_AddressableLEDColorOrder>(colorOrder), &status);
HAL_SetAddressableLEDStart(static_cast<HAL_AddressableLEDHandle>(handle),
start, &status);
CheckStatus(env, status);
}
@@ -93,80 +92,79 @@ Java_edu_wpi_first_hal_AddressableLEDJNI_setLength
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
* Method: setData
* Signature: (I[B)V
* Signature: (II[BII)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_AddressableLEDJNI_setData
(JNIEnv* env, jclass, jint handle, jbyteArray arr)
(JNIEnv* env, jclass, jint outStart, jint colorOrder, jbyteArray data,
jint start, jint len)
{
if (!data) {
ThrowNullPointerException(env, "data is null");
return;
}
if (start < 0) {
ThrowIndexOutOfBoundsException(env, "start must be >= 0");
return;
}
if (len < 0) {
ThrowIndexOutOfBoundsException(env, "len must be >= 0");
return;
}
JSpan<const jbyte> cdata{env, data};
if (static_cast<unsigned int>(start + len) > cdata.size()) {
ThrowIndexOutOfBoundsException(
env, "start + len must be smaller than array length");
return;
}
if ((len % 3) != 0) {
ThrowIndexOutOfBoundsException(env, "len must be a multiple of 3");
return;
}
auto rawdata = cdata.uarray().subspan(start, len);
int32_t status = 0;
JSpan<const jbyte> jArrRef{env, arr};
HAL_WriteAddressableLEDData(
static_cast<HAL_AddressableLEDHandle>(handle),
reinterpret_cast<const HAL_AddressableLEDData*>(jArrRef.data()),
jArrRef.size() / 4, &status);
CheckStatus(env, status);
HAL_SetAddressableLEDData(
outStart, rawdata.size() / 3,
static_cast<HAL_AddressableLEDColorOrder>(colorOrder),
reinterpret_cast<const HAL_AddressableLEDData*>(rawdata.data()), &status);
}
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
* Method: setBitTiming
* Signature: (IIIII)V
* Method: setDataFromBuffer
* Signature: (IILjava/lang/Object;II)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_AddressableLEDJNI_setBitTiming
(JNIEnv* env, jclass, jint handle, jint highTime0, jint lowTime0,
jint highTime1, jint lowTime1)
Java_edu_wpi_first_hal_AddressableLEDJNI_setDataFromBuffer
(JNIEnv* env, jclass, jint outStart, jint colorOrder, jobject data,
jint start, jint len)
{
if (!data) {
ThrowNullPointerException(env, "data is null");
return;
}
if (start < 0) {
ThrowIndexOutOfBoundsException(env, "start must be >= 0");
return;
}
if (len < 0) {
ThrowIndexOutOfBoundsException(env, "len must be >= 0");
return;
}
JSpan<const jbyte> cdata{env, data, static_cast<size_t>(start + len)};
if (!cdata) {
ThrowIllegalArgumentException(env, "data must be a native ByteBuffer");
return;
}
if ((len % 3) != 0) {
ThrowIndexOutOfBoundsException(env, "len must be a multiple of 3");
return;
}
auto rawdata = cdata.uarray().subspan(start, len);
int32_t status = 0;
HAL_SetAddressableLEDBitTiming(static_cast<HAL_AddressableLEDHandle>(handle),
highTime0, lowTime0, highTime1, lowTime1,
&status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
* Method: setSyncTime
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_AddressableLEDJNI_setSyncTime
(JNIEnv* env, jclass, jint handle, jint syncTime)
{
int32_t status = 0;
HAL_SetAddressableLEDSyncTime(static_cast<HAL_AddressableLEDHandle>(handle),
syncTime, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
* Method: start
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_AddressableLEDJNI_start
(JNIEnv* env, jclass, jint handle)
{
int32_t status = 0;
HAL_StartAddressableLEDOutput(static_cast<HAL_AddressableLEDHandle>(handle),
&status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_hal_AddressableLEDJNI
* Method: stop
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_AddressableLEDJNI_stop
(JNIEnv* env, jclass, jint handle)
{
int32_t status = 0;
HAL_StopAddressableLEDOutput(static_cast<HAL_AddressableLEDHandle>(handle),
&status);
CheckStatus(env, status);
HAL_SetAddressableLEDData(
outStart, rawdata.size() / 3,
static_cast<HAL_AddressableLEDColorOrder>(colorOrder),
reinterpret_cast<const HAL_AddressableLEDData*>(rawdata.data()), &status);
}
} // extern "C"

View File

@@ -40,6 +40,7 @@ static_assert(edu_wpi_first_hal_HALUtil_RUNTIME_SYSTEMCORE ==
static JavaVM* jvm = nullptr;
static JException illegalArgExCls;
static JException indexOobExCls;
static JException boundaryExCls;
static JException allocationExCls;
static JException halHandleExCls;
@@ -68,6 +69,7 @@ static const JClassInit classes[] = {
static const JExceptionInit exceptions[] = {
{"java/lang/IllegalArgumentException", &illegalArgExCls},
{"java/lang/IndexOutOfBoundsException", &indexOobExCls},
{"edu/wpi/first/hal/util/BoundaryException", &boundaryExCls},
{"edu/wpi/first/hal/util/AllocationException", &allocationExCls},
{"edu/wpi/first/hal/util/HalHandleException", &halHandleExCls},
@@ -159,6 +161,10 @@ void ThrowIllegalArgumentException(JNIEnv* env, std::string_view msg) {
illegalArgExCls.Throw(env, msg);
}
void ThrowIndexOutOfBoundsException(JNIEnv* env, std::string_view msg) {
indexOobExCls.Throw(env, msg);
}
void ThrowBoundaryException(JNIEnv* env, double value, double lower,
double upper) {
static jmethodID getMessage = nullptr;

View File

@@ -46,6 +46,7 @@ void ThrowNullPointerException(JNIEnv* env, std::string_view msg);
void ThrowCANStreamOverflowException(JNIEnv* env, jobjectArray messages,
jint length);
void ThrowIllegalArgumentException(JNIEnv* env, std::string_view msg);
void ThrowIndexOutOfBoundsException(JNIEnv* env, std::string_view msg);
void ThrowBoundaryException(JNIEnv* env, double value, double lower,
double upper);

View File

@@ -11,7 +11,7 @@
#include "edu_wpi_first_hal_simulation_AddressableLEDDataJNI.h"
#include "hal/simulation/AddressableLEDData.h"
static_assert(sizeof(jbyte) * 4 == sizeof(HAL_AddressableLEDData));
static_assert(sizeof(jbyte) * 3 == sizeof(HAL_AddressableLEDData));
using namespace hal;
using namespace wpi::java;
@@ -71,53 +71,52 @@ Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_setInitialized
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: registerOutputPortCallback
* Method: registerStartCallback
* Signature: (ILjava/lang/Object;Z)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_registerOutputPortCallback
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_registerStartCallback
(JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
{
return sim::AllocateCallback(
env, index, callback, initialNotify,
&HALSIM_RegisterAddressableLEDOutputPortCallback);
return sim::AllocateCallback(env, index, callback, initialNotify,
&HALSIM_RegisterAddressableLEDStartCallback);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: cancelOutputPortCallback
* Method: cancelStartCallback
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_cancelOutputPortCallback
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_cancelStartCallback
(JNIEnv* env, jclass, jint index, jint handle)
{
return sim::FreeCallback(env, handle, index,
&HALSIM_CancelAddressableLEDOutputPortCallback);
&HALSIM_CancelAddressableLEDStartCallback);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: getOutputPort
* Method: getStart
* Signature: (I)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_getOutputPort
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_getStart
(JNIEnv*, jclass, jint index)
{
return HALSIM_GetAddressableLEDOutputPort(index);
return HALSIM_GetAddressableLEDStart(index);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: setOutputPort
* Method: setStart
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_setOutputPort
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_setStart
(JNIEnv*, jclass, jint index, jint value)
{
HALSIM_SetAddressableLEDOutputPort(index, value);
HALSIM_SetAddressableLEDStart(index, value);
}
/*
@@ -170,96 +169,50 @@ Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_setLength
HALSIM_SetAddressableLEDLength(index, value);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: registerRunningCallback
* Signature: (ILjava/lang/Object;Z)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_registerRunningCallback
(JNIEnv* env, jclass, jint index, jobject callback, jboolean initialNotify)
{
return sim::AllocateCallback(env, index, callback, initialNotify,
&HALSIM_RegisterAddressableLEDRunningCallback);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: cancelRunningCallback
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_cancelRunningCallback
(JNIEnv* env, jclass, jint index, jint handle)
{
return sim::FreeCallback(env, handle, index,
&HALSIM_CancelAddressableLEDRunningCallback);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: getRunning
* Signature: (I)Z
*/
JNIEXPORT jboolean JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_getRunning
(JNIEnv*, jclass, jint index)
{
return HALSIM_GetAddressableLEDRunning(index);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: setRunning
* Signature: (IZ)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_setRunning
(JNIEnv*, jclass, jint index, jboolean value)
{
HALSIM_SetAddressableLEDRunning(index, value);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: registerDataCallback
* Signature: (ILjava/lang/Object;)I
* Signature: (Ljava/lang/Object;)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_registerDataCallback
(JNIEnv* env, jclass, jint index, jobject callback)
(JNIEnv* env, jclass, jobject callback)
{
return sim::AllocateConstBufferCallback(
env, index, callback, &HALSIM_RegisterAddressableLEDDataCallback);
env, -1, callback,
[](int32_t, HAL_ConstBufferCallback callback, void* param) {
return HALSIM_RegisterAddressableLEDDataCallback(callback, param);
});
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: cancelDataCallback
* Signature: (II)V
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_cancelDataCallback
(JNIEnv* env, jclass, jint index, jint handle)
(JNIEnv* env, jclass, jint handle)
{
sim::FreeConstBufferCallback(env, handle, index,
&HALSIM_CancelAddressableLEDDataCallback);
sim::FreeConstBufferCallback(env, handle, -1, [](int32_t, int32_t uid) {
HALSIM_CancelAddressableLEDDataCallback(uid);
});
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: getData
* Signature: (I)[B
* Signature: (II)[B
*/
JNIEXPORT jbyteArray JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_getData
(JNIEnv* env, jclass, jint index)
(JNIEnv* env, jclass, jint start, jint length)
{
auto data =
std::make_unique<HAL_AddressableLEDData[]>(HAL_kAddressableLEDMaxLength);
int32_t length = HALSIM_GetAddressableLEDData(index, data.get());
length = HALSIM_GetAddressableLEDData(start, length, data.get());
return MakeJByteArray(
env, std::span(reinterpret_cast<jbyte*>(data.get()), length * 4));
env, std::span(reinterpret_cast<jbyte*>(data.get()), length * 3));
}
/*
@@ -269,13 +222,13 @@ Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_getData
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_setData
(JNIEnv* env, jclass, jint index, jbyteArray arr)
(JNIEnv* env, jclass, jint start, jbyteArray arr)
{
JSpan<const jbyte> jArrRef{env, arr};
auto arrRef = jArrRef.array();
HALSIM_SetAddressableLEDData(
index, reinterpret_cast<const HAL_AddressableLEDData*>(arrRef.data()),
arrRef.size() / 4);
start, arrRef.size() / 3,
reinterpret_cast<const HAL_AddressableLEDData*>(arrRef.data()));
}
/*
@@ -290,16 +243,4 @@ Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_resetData
HALSIM_ResetAddressableLEDData(index);
}
/*
* Class: edu_wpi_first_hal_simulation_AddressableLEDDataJNI
* Method: findForChannel
* Signature: (I)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_findForChannel
(JNIEnv*, jclass, jint channel)
{
return HALSIM_FindAddressableLEDForChannel(channel);
}
} // extern "C"

View File

@@ -20,14 +20,16 @@ extern "C" {
#endif
/**
* Initialize Addressable LED using a PWM Digital handle.
* Creates a new instance of an addressable LED.
*
* @param[in] outputPort handle of the digital port for PWM
* @param[in] channel the smartio channel
* @param[in] allocationLocation the location where the allocation is occurring
* (can be null)
* @param[out] status the error code, or 0 for success
* @return Addressable LED handle
*/
HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
HAL_DigitalHandle outputPort, int32_t* status);
int32_t channel, const char* allocationLocation, int32_t* status);
/**
* Free the Addressable LED Handle.
@@ -37,34 +39,26 @@ HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle);
/**
* Sets the color order for the addressable LED output. The default order is
* GRB. This will take effect on the next call to HAL_WriteAddressableLEDData().
* @param[in] handle the Addressable LED handle
* @param[in] colorOrder the color order
* @param[out] status the error code, or 0 for success
*/
void HAL_SetAddressableLEDColorOrder(HAL_AddressableLEDHandle handle,
HAL_AddressableLEDColorOrder colorOrder,
int32_t* status);
/**
* Set the Addressable LED PWM Digital port.
* Sets the start buffer location used for the LED strip.
*
* <p>All addressable LEDs use a single backing buffer 1024 LEDs in size.
* The max length for a single output is 1024 LEDs (with an offset of zero).
*
* @param[in] handle the Addressable LED handle
* @param[in] outputPort The digital handle of the PWM port
* @param[in] start the strip start, in LEDs
* @param[out] status the error code, or 0 for success
*/
void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
HAL_DigitalHandle outputPort,
int32_t* status);
void HAL_SetAddressableLEDStart(HAL_AddressableLEDHandle handle, int32_t start,
int32_t* status);
/**
* Sets the length of the LED strip.
*
* <p>The max length is 5460 LEDs.
* <p>All addressable LEDs use a single backing buffer 1024 LEDs in size.
* The max length for a single output is 1024 LEDs (with an offset of zero).
*
* @param[in] handle the Addressable LED handle
* @param[in] length the strip length
* @param[in] length the strip length, in LEDs
* @param[out] status the error code, or 0 for success
*/
void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
@@ -73,68 +67,19 @@ void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
/**
* 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>All addressable LEDs use a single backing buffer 1024 LEDs in size.
* This function may be used to set part of or all of the buffer.
*
* @param[in] handle the Addressable LED handle
* @param[in] start the strip start, in LEDs
* @param[in] length the strip length, in LEDs
* @param[in] colorOrder the color order
* @param[in] data the buffer to write
* @param[in] length the strip length
* @param[out] status the error code, or 0 for success
*/
void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
const struct HAL_AddressableLEDData* data,
int32_t length, int32_t* status);
/**
* Sets the bit timing.
*
* <p>By default, the driver is set up to drive WS2812B and WS2815, so nothing
* needs to be set for those.
*
* @param[in] handle the Addressable LED handle
* @param[in] highTime0 high time for 0 bit in nanoseconds (default 400 ns)
* @param[in] lowTime0 low time for 0 bit in nanoseconds (default 900 ns)
* @param[in] highTime1 high time for 1 bit in nanoseconds (default 900 ns)
* @param[in] lowTime1 low time for 1 bit in nanoseconds (default 600 ns)
* @param[out] status the error code, or 0 for success
*/
void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
int32_t highTime0, int32_t lowTime0,
int32_t highTime1, int32_t lowTime1,
int32_t* status);
/**
* Sets the sync time.
*
* <p>The sync time is the time to hold output so LEDs enable. Default set for
* WS2812B and WS2815.
*
* @param[in] handle the Addressable LED handle
* @param[in] syncTime the sync time in microseconds (default 280 μs)
* @param[out] status the error code, or 0 for success
*/
void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
int32_t syncTime, int32_t* status);
/**
* Starts the output.
*
* <p>The output writes continuously.
*
* @param[in] handle the Addressable LED handle
* @param[out] status the error code, or 0 for success
*/
void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle,
int32_t* status);
/**
* Stops the output.
*
* @param[in] handle the Addressable LED handle
* @param[out] status the error code, or 0 for success
*/
void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle,
int32_t* status);
void HAL_SetAddressableLEDData(int32_t start, int32_t length,
HAL_AddressableLEDColorOrder colorOrder,
const struct HAL_AddressableLEDData* data,
int32_t* status);
#ifdef __cplusplus
} // extern "C"

View File

@@ -7,15 +7,14 @@
#include <hal/Types.h>
#include <stdint.h>
/** max length of LED strip supported by FPGA. */
#define HAL_kAddressableLEDMaxLength 5460
/** max length of LED strip supported by device. */
#define HAL_kAddressableLEDMaxLength 1024
/** structure for holding one LED's color data. */
struct HAL_AddressableLEDData {
uint8_t b; ///< blue value
uint8_t g; ///< green value
uint8_t r; ///< red value
uint8_t padding;
};
/**

View File

@@ -12,8 +12,6 @@
extern "C" {
#endif
int32_t HALSIM_FindAddressableLEDForChannel(int32_t channel);
void HALSIM_ResetAddressableLEDData(int32_t index);
int32_t HALSIM_RegisterAddressableLEDInitializedCallback(
@@ -23,12 +21,13 @@ void HALSIM_CancelAddressableLEDInitializedCallback(int32_t index, int32_t uid);
HAL_Bool HALSIM_GetAddressableLEDInitialized(int32_t index);
void HALSIM_SetAddressableLEDInitialized(int32_t index, HAL_Bool initialized);
int32_t HALSIM_RegisterAddressableLEDOutputPortCallback(
int32_t index, HAL_NotifyCallback callback, void* param,
HAL_Bool initialNotify);
void HALSIM_CancelAddressableLEDOutputPortCallback(int32_t index, int32_t uid);
int32_t HALSIM_GetAddressableLEDOutputPort(int32_t index);
void HALSIM_SetAddressableLEDOutputPort(int32_t index, int32_t outputPort);
int32_t HALSIM_RegisterAddressableLEDStartCallback(int32_t index,
HAL_NotifyCallback callback,
void* param,
HAL_Bool initialNotify);
void HALSIM_CancelAddressableLEDStartCallback(int32_t index, int32_t uid);
int32_t HALSIM_GetAddressableLEDStart(int32_t index);
void HALSIM_SetAddressableLEDStart(int32_t index, int32_t start);
int32_t HALSIM_RegisterAddressableLEDLengthCallback(int32_t index,
HAL_NotifyCallback callback,
@@ -38,21 +37,13 @@ void HALSIM_CancelAddressableLEDLengthCallback(int32_t index, int32_t uid);
int32_t HALSIM_GetAddressableLEDLength(int32_t index);
void HALSIM_SetAddressableLEDLength(int32_t index, int32_t length);
int32_t HALSIM_RegisterAddressableLEDRunningCallback(
int32_t index, HAL_NotifyCallback callback, void* param,
HAL_Bool initialNotify);
void HALSIM_CancelAddressableLEDRunningCallback(int32_t index, int32_t uid);
HAL_Bool HALSIM_GetAddressableLEDRunning(int32_t index);
void HALSIM_SetAddressableLEDRunning(int32_t index, HAL_Bool running);
int32_t HALSIM_RegisterAddressableLEDDataCallback(
int32_t index, HAL_ConstBufferCallback callback, void* param);
void HALSIM_CancelAddressableLEDDataCallback(int32_t index, int32_t uid);
int32_t HALSIM_GetAddressableLEDData(int32_t index,
HAL_ConstBufferCallback callback, void* param);
void HALSIM_CancelAddressableLEDDataCallback(int32_t uid);
int32_t HALSIM_GetAddressableLEDData(int32_t start, int32_t length,
struct HAL_AddressableLEDData* data);
void HALSIM_SetAddressableLEDData(int32_t index,
const struct HAL_AddressableLEDData* data,
int32_t length);
void HALSIM_SetAddressableLEDData(int32_t start, int32_t length,
const struct HAL_AddressableLEDData* data);
void HALSIM_RegisterAddressableLEDAllCallbacks(int32_t index,
HAL_NotifyCallback callback,

View File

@@ -12,108 +12,90 @@
#include "PortsInternal.h"
#include "hal/Errors.h"
#include "hal/handles/HandlesInternal.h"
#include "hal/handles/LimitedHandleResource.h"
#include "hal/handles/IndexedHandleResource.h"
#include "mockdata/AddressableLEDDataInternal.h"
using namespace hal;
namespace {
struct AddressableLED {
uint8_t index;
};
} // namespace
static LimitedHandleResource<HAL_AddressableLEDHandle, AddressableLED,
kNumAddressableLEDs,
HAL_HandleEnum::AddressableLED>* ledHandles;
namespace hal::init {
void InitializeAddressableLED() {
static LimitedHandleResource<HAL_AddressableLEDHandle, AddressableLED,
kNumAddressableLEDs,
HAL_HandleEnum::AddressableLED>
dcH;
ledHandles = &dcH;
}
void InitializeAddressableLED() {}
} // namespace hal::init
extern "C" {
HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
HAL_DigitalHandle outputPort, int32_t* status) {
int32_t channel, const char* allocationLocation, int32_t* status) {
hal::init::CheckInit();
auto digitalPort =
hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM);
if (channel < 0 || channel >= kNumAddressableLEDs) {
*status = RESOURCE_OUT_OF_RANGE;
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for AddressableLED",
0, kNumAddressableLEDs, channel);
return HAL_kInvalidHandle;
}
if (!digitalPort) {
// If DIO was passed, channel error, else generic error
if (getHandleType(outputPort) == hal::HAL_HandleEnum::DIO) {
*status = HAL_LED_CHANNEL_ERROR;
HAL_DigitalHandle handle;
auto port = digitalChannelHandles->Allocate(
channel, HAL_HandleEnum::AddressableLED, &handle, status);
if (*status != 0) {
if (port) {
hal::SetLastErrorPreviouslyAllocated(status, "PWM or DIO", channel,
port->previousAllocation);
} else {
*status = HAL_HANDLE_ERROR;
hal::SetLastErrorIndexOutOfRange(status,
"Invalid Index for AddressableLED", 0,
kNumAddressableLEDs, channel);
}
return HAL_kInvalidHandle;
return HAL_kInvalidHandle; // failed to allocate. Pass error back.
}
if (digitalPort->channel >= kNumPWMHeaders) {
*status = HAL_LED_CHANNEL_ERROR;
return HAL_kInvalidHandle;
}
port->channel = static_cast<uint8_t>(channel);
HAL_AddressableLEDHandle handle = ledHandles->Allocate();
if (handle == HAL_kInvalidHandle) {
*status = NO_AVAILABLE_RESOURCES;
return HAL_kInvalidHandle;
}
SimAddressableLEDData[channel].start = 0;
SimAddressableLEDData[channel].length = 0;
SimAddressableLEDData[channel].initialized = true;
port->previousAllocation = allocationLocation ? allocationLocation : "";
auto led = ledHandles->Get(handle);
if (!led) { // would only occur on thread issue
*status = HAL_HANDLE_ERROR;
return HAL_kInvalidHandle;
}
int16_t index = getHandleIndex(handle);
SimAddressableLEDData[index].outputPort = digitalPort->channel;
SimAddressableLEDData[index].length = 1;
SimAddressableLEDData[index].running = false;
SimAddressableLEDData[index].initialized = true;
led->index = index;
return handle;
}
void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
auto led = ledHandles->Get(handle);
ledHandles->Free(handle);
if (!led) {
auto port =
digitalChannelHandles->Get(handle, HAL_HandleEnum::AddressableLED);
// no status, so no need to check for a proper free.
digitalChannelHandles->Free(handle, HAL_HandleEnum::AddressableLED);
if (port == nullptr) {
return;
}
SimAddressableLEDData[led->index].running = false;
SimAddressableLEDData[led->index].initialized = false;
SimAddressableLEDData[port->channel].initialized = false;
}
void HAL_SetAddressableLEDColorOrder(HAL_AddressableLEDHandle handle,
HAL_AddressableLEDColorOrder colorOrder,
int32_t* status) {}
void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
HAL_DigitalHandle outputPort,
int32_t* status) {
auto led = ledHandles->Get(handle);
if (!led) {
void HAL_SetAddressableLEDStart(HAL_AddressableLEDHandle handle, int32_t start,
int32_t* status) {
auto port =
digitalChannelHandles->Get(handle, HAL_HandleEnum::AddressableLED);
if (!port) {
*status = HAL_HANDLE_ERROR;
return;
}
if (auto port = digitalChannelHandles->Get(outputPort, HAL_HandleEnum::PWM)) {
SimAddressableLEDData[led->index].outputPort = port->channel;
} else {
SimAddressableLEDData[led->index].outputPort = -1;
if (start > HAL_kAddressableLEDMaxLength || start < 0) {
*status = PARAMETER_OUT_OF_RANGE;
hal::SetLastError(
status,
fmt::format(
"LED start must be less than or equal to {}. {} was requested",
HAL_kAddressableLEDMaxLength, start));
return;
}
SimAddressableLEDData[port->channel].start = start;
}
void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
int32_t length, int32_t* status) {
auto led = ledHandles->Get(handle);
if (!led) {
auto port =
digitalChannelHandles->Get(handle, HAL_HandleEnum::AddressableLED);
if (!port) {
*status = HAL_HANDLE_ERROR;
return;
}
@@ -126,54 +108,13 @@ void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
HAL_kAddressableLEDMaxLength, length));
return;
}
SimAddressableLEDData[led->index].length = length;
SimAddressableLEDData[port->channel].length = length;
}
void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
const struct HAL_AddressableLEDData* data,
int32_t length, int32_t* status) {
auto led = ledHandles->Get(handle);
if (!led) {
*status = HAL_HANDLE_ERROR;
return;
}
if (length > SimAddressableLEDData[led->index].length) {
*status = PARAMETER_OUT_OF_RANGE;
hal::SetLastError(
status,
fmt::format(
"Data length must be less than or equal to {}. {} was requested",
SimAddressableLEDData[led->index].length.Get(), length));
return;
}
SimAddressableLEDData[led->index].SetData(data, length);
}
void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
int32_t highTime0, int32_t lowTime0,
int32_t highTime1, int32_t lowTime1,
int32_t* status) {}
void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
int32_t syncTime, int32_t* status) {}
void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle,
int32_t* status) {
auto led = ledHandles->Get(handle);
if (!led) {
*status = HAL_HANDLE_ERROR;
return;
}
SimAddressableLEDData[led->index].running = true;
}
void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle,
int32_t* status) {
auto led = ledHandles->Get(handle);
if (!led) {
*status = HAL_HANDLE_ERROR;
return;
}
SimAddressableLEDData[led->index].running = false;
void HAL_SetAddressableLEDData(int32_t start, int32_t length,
HAL_AddressableLEDColorOrder colorOrder,
const struct HAL_AddressableLEDData* data,
int32_t* status) {
SimAddressableLEDDataBuffer->SetData(start, length, data);
}
} // extern "C"

View File

@@ -32,7 +32,7 @@ constexpr int32_t kNumREVPDHChannels = 24;
constexpr int32_t kNumPDSimModules = kNumREVPDHModules;
constexpr int32_t kNumPDSimChannels = kNumREVPDHChannels;
constexpr int32_t kNumDutyCycles = 6;
constexpr int32_t kNumAddressableLEDs = 1;
constexpr int32_t kNumAddressableLEDs = 6;
constexpr int32_t kNumREVPHModules = 63;
constexpr int32_t kNumREVPHChannels = 16;
constexpr int32_t kSPIAccelerometers = 5;

View File

@@ -14,62 +14,64 @@ namespace hal::init {
void InitializeAddressableLEDData() {
static AddressableLEDData sad[kNumAddressableLEDs];
::hal::SimAddressableLEDData = sad;
static AddressableLEDDataBuffer buf;
::hal::SimAddressableLEDDataBuffer = &buf;
}
} // namespace hal::init
AddressableLEDData* hal::SimAddressableLEDData;
AddressableLEDDataBuffer* hal::SimAddressableLEDDataBuffer;
void AddressableLEDData::ResetData() {
initialized.Reset(false);
outputPort.Reset(-1);
length.Reset(1);
running.Reset(false);
data.Reset();
start.Reset(0);
length.Reset(0);
}
void AddressableLEDData::SetData(const HAL_AddressableLEDData* d, int32_t len) {
len = (std::min)(HAL_kAddressableLEDMaxLength, len);
void AddressableLEDDataBuffer::SetData(int32_t start, int32_t len,
const HAL_AddressableLEDData* d) {
if ((start + len) > HAL_kAddressableLEDMaxLength) {
len = HAL_kAddressableLEDMaxLength - start;
}
if (len <= 0) {
return;
}
{
std::scoped_lock lock(m_dataMutex);
std::memcpy(m_data, d, len * sizeof(d[0]));
std::memcpy(&m_data[start], d, len * sizeof(d[0]));
}
data(reinterpret_cast<const uint8_t*>(d), len * sizeof(d[0]));
}
int32_t AddressableLEDData::GetData(HAL_AddressableLEDData* d) {
int32_t AddressableLEDDataBuffer::GetData(int32_t start, int32_t len,
HAL_AddressableLEDData* d) {
if ((start + len) > HAL_kAddressableLEDMaxLength) {
len = HAL_kAddressableLEDMaxLength - start;
}
if (len <= 0) {
return 0;
}
std::scoped_lock lock(m_dataMutex);
int32_t len = length;
if (d) {
std::memcpy(d, m_data, len * sizeof(d[0]));
std::memcpy(d, &m_data[start], len * sizeof(d[0]));
}
return len;
}
extern "C" {
int32_t HALSIM_FindAddressableLEDForChannel(int32_t channel) {
for (int i = 0; i < kNumAddressableLEDs; ++i) {
if (SimAddressableLEDData[i].initialized &&
SimAddressableLEDData[i].outputPort == channel) {
return i;
}
}
return -1;
}
void HALSIM_ResetAddressableLEDData(int32_t index) {
SimAddressableLEDData[index].ResetData();
}
int32_t HALSIM_GetAddressableLEDData(int32_t index,
int32_t HALSIM_GetAddressableLEDData(int32_t start, int32_t length,
struct HAL_AddressableLEDData* data) {
return SimAddressableLEDData[index].GetData(data);
return SimAddressableLEDDataBuffer->GetData(start, length, data);
}
void HALSIM_SetAddressableLEDData(int32_t index,
const struct HAL_AddressableLEDData* data,
int32_t length) {
SimAddressableLEDData[index].SetData(data, length);
void HALSIM_SetAddressableLEDData(int32_t start, int32_t length,
const struct HAL_AddressableLEDData* data) {
SimAddressableLEDDataBuffer->SetData(start, length, data);
}
#define DEFINE_CAPI(TYPE, CAPINAME, LOWERNAME) \
@@ -77,14 +79,14 @@ void HALSIM_SetAddressableLEDData(int32_t index,
SimAddressableLEDData, LOWERNAME)
DEFINE_CAPI(HAL_Bool, Initialized, initialized)
DEFINE_CAPI(int32_t, OutputPort, outputPort)
DEFINE_CAPI(int32_t, Start, start)
DEFINE_CAPI(int32_t, Length, length)
DEFINE_CAPI(HAL_Bool, Running, running)
#undef DEFINE_CAPI
#define DEFINE_CAPI(TYPE, CAPINAME, LOWERNAME) \
HAL_SIMCALLBACKREGISTRY_DEFINE_CAPI(TYPE, HALSIM, AddressableLED##CAPINAME, \
SimAddressableLEDData, LOWERNAME)
#define DEFINE_CAPI(TYPE, CAPINAME, LOWERNAME) \
HAL_SIMCALLBACKREGISTRY_DEFINE_CAPI_NOINDEX( \
TYPE, HALSIM, AddressableLED##CAPINAME, SimAddressableLEDDataBuffer, \
LOWERNAME)
DEFINE_CAPI(HAL_ConstBufferCallback, Data, data)
@@ -97,8 +99,7 @@ void HALSIM_RegisterAddressableLEDAllCallbacks(int32_t index,
void* param,
HAL_Bool initialNotify) {
REGISTER(initialized);
REGISTER(outputPort);
REGISTER(start);
REGISTER(length);
REGISTER(running);
}
} // extern "C"

View File

@@ -15,26 +15,29 @@
namespace hal {
class AddressableLEDData {
HAL_SIMDATAVALUE_DEFINE_NAME(Initialized)
HAL_SIMDATAVALUE_DEFINE_NAME(OutputPort)
HAL_SIMDATAVALUE_DEFINE_NAME(Start)
HAL_SIMDATAVALUE_DEFINE_NAME(Length)
HAL_SIMDATAVALUE_DEFINE_NAME(Running)
public:
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetInitializedName> initialized{
false};
SimDataValue<int32_t, HAL_MakeInt, GetStartName> start{0};
SimDataValue<int32_t, HAL_MakeInt, GetLengthName> length{0};
void ResetData();
};
extern AddressableLEDData* SimAddressableLEDData;
class AddressableLEDDataBuffer {
HAL_SIMDATAVALUE_DEFINE_NAME(Data)
wpi::recursive_spinlock m_dataMutex;
HAL_AddressableLEDData m_data[HAL_kAddressableLEDMaxLength];
public:
void SetData(const HAL_AddressableLEDData* d, int32_t len);
int32_t GetData(HAL_AddressableLEDData* d);
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetInitializedName> initialized{
false};
SimDataValue<int32_t, HAL_MakeInt, GetOutputPortName> outputPort{-1};
SimDataValue<int32_t, HAL_MakeInt, GetLengthName> length{1};
SimDataValue<HAL_Bool, HAL_MakeBoolean, GetRunningName> running{false};
void SetData(int32_t start, int32_t len, const HAL_AddressableLEDData* d);
int32_t GetData(int32_t start, int32_t len, HAL_AddressableLEDData* d);
SimCallbackRegistry<HAL_ConstBufferCallback, GetDataName> data;
void ResetData();
};
extern AddressableLEDData* SimAddressableLEDData;
extern AddressableLEDDataBuffer* SimAddressableLEDDataBuffer;
} // namespace hal

View File

@@ -4,85 +4,152 @@
#include "hal/AddressableLED.h"
#include <stdint.h>
#include <cstdio>
#include <cstring>
#include <memory>
#include <thread>
#include <fmt/format.h>
#include <networktables/NetworkTableInstance.h>
#include <networktables/RawTopic.h>
#include "HALInitializer.h"
#include "HALInternal.h"
#include "PortsInternal.h"
#include "SmartIo.h"
#include "SystemServerInternal.h"
#include "hal/AddressableLEDTypes.h"
#include "hal/Errors.h"
#include "hal/cpp/fpga_clock.h"
using namespace hal;
#define LEDS_PREFIX "/leds/"
namespace {
constexpr const char* kRawKey = LEDS_PREFIX "raw";
struct AddressableLEDs {
explicit AddressableLEDs(nt::NetworkTableInstance inst)
: rawPub{inst.GetRawTopic(kRawKey).Publish(
"raw", {.periodic = 0.005, .sendAll = true})} {}
nt::RawPublisher rawPub;
uint8_t s_buffer[HAL_kAddressableLEDMaxLength * 3];
};
static AddressableLEDs* leds;
} // namespace
namespace hal::init {
void InitializeAddressableLED() {}
void InitializeAddressableLED() {
static AddressableLEDs leds_static{hal::GetSystemServer()};
leds = &leds_static;
}
} // namespace hal::init
extern "C" {
HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
HAL_DigitalHandle outputPort, int32_t* status) {
int32_t channel, const char* allocationLocation, int32_t* status) {
hal::init::CheckInit();
*status = HAL_HANDLE_ERROR;
return HAL_kInvalidHandle;
if (channel < 0 || channel >= kNumSmartIo) {
*status = RESOURCE_OUT_OF_RANGE;
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for AddressableLED",
0, kNumSmartIo, channel);
return HAL_kInvalidHandle;
}
HAL_DigitalHandle handle;
auto port = smartIoHandles->Allocate(channel, HAL_HandleEnum::AddressableLED,
&handle, status);
if (*status != 0) {
if (port) {
hal::SetLastErrorPreviouslyAllocated(status, "SmartIo", channel,
port->previousAllocation);
} else {
hal::SetLastErrorIndexOutOfRange(
status, "Invalid Index for AddressableLED", 0, kNumSmartIo, channel);
}
return HAL_kInvalidHandle; // failed to allocate. Pass error back.
}
port->channel = channel;
*status = port->InitializeMode(SmartIoMode::AddressableLED);
if (*status != 0) {
smartIoHandles->Free(handle, HAL_HandleEnum::AddressableLED);
return HAL_kInvalidHandle;
}
port->previousAllocation = allocationLocation ? allocationLocation : "";
return handle;
}
void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {}
void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
auto port = smartIoHandles->Get(handle, HAL_HandleEnum::AddressableLED);
if (port == nullptr) {
return;
}
void HAL_SetAddressableLEDColorOrder(HAL_AddressableLEDHandle handle,
HAL_AddressableLEDColorOrder colorOrder,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
smartIoHandles->Free(handle, HAL_HandleEnum::AddressableLED);
// Wait for no other object to hold this handle.
auto start = hal::fpga_clock::now();
while (port.use_count() != 1) {
auto current = hal::fpga_clock::now();
if (start + std::chrono::seconds(1) < current) {
std::puts("AddressableLED handle free timeout");
std::fflush(stdout);
break;
}
std::this_thread::yield();
}
}
void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
HAL_DigitalHandle outputPort,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
void HAL_SetAddressableLEDStart(HAL_AddressableLEDHandle handle, int32_t start,
int32_t* status) {
auto port = smartIoHandles->Get(handle, HAL_HandleEnum::AddressableLED);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
*status = port->SetLedStart(start);
}
void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
int32_t length, int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
auto port = smartIoHandles->Get(handle, HAL_HandleEnum::AddressableLED);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
*status = port->SetLedLength(length);
}
static_assert(sizeof(HAL_AddressableLEDData) == sizeof(uint32_t),
"LED Data must be 32 bit");
static_assert(sizeof(HAL_AddressableLEDData) == 3, "LED Data must be 3 bytes");
void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
const struct HAL_AddressableLEDData* data,
int32_t length, int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
}
void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
int32_t highTime0, int32_t lowTime0,
int32_t highTime1, int32_t lowTime1,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
}
void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
int32_t syncTime, int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
}
void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
}
void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
void HAL_SetAddressableLEDData(int32_t start, int32_t length,
HAL_AddressableLEDColorOrder colorOrder,
const struct HAL_AddressableLEDData* data,
int32_t* status) {
if (start < 0 || start >= HAL_kAddressableLEDMaxLength || length < 0 ||
(start + length) >= HAL_kAddressableLEDMaxLength) {
*status = PARAMETER_OUT_OF_RANGE;
return;
}
// TODO: handle color order
std::memcpy(&leds->s_buffer[start * 3], data, length * 3);
leds->rawPub.Set(leds->s_buffer);
}
} // extern "C"

View File

@@ -29,7 +29,7 @@ constexpr int32_t kNumCTREPDPChannels = 16;
constexpr int32_t kNumREVPDHModules = 63;
constexpr int32_t kNumREVPDHChannels = 24;
constexpr int32_t kNumDutyCycles = 0;
constexpr int32_t kNumAddressableLEDs = 0;
constexpr int32_t kNumAddressableLEDs = 6;
constexpr int32_t kNumREVPHModules = 63;
constexpr int32_t kNumREVPHChannels = 16;

View File

@@ -8,6 +8,8 @@
#include "HALInitializer.h"
#include "SystemServerInternal.h"
#include "hal/AddressableLEDTypes.h"
#include "hal/Errors.h"
namespace hal {
@@ -42,6 +44,10 @@ int32_t SmartIo::InitializeMode(SmartIoMode mode) {
inst.GetIntegerTopic(subTableString + "valset").Publish(options);
periodSetPublisher =
inst.GetIntegerTopic(subTableString + "periodset").Publish(options);
ledcountPublisher =
inst.GetIntegerTopic(subTableString + "ledcount").Publish(options);
ledoffsetPublisher =
inst.GetIntegerTopic(subTableString + "ledoffset").Publish(options);
currentMode = mode;
switch (mode) {
@@ -50,6 +56,10 @@ int32_t SmartIo::InitializeMode(SmartIoMode mode) {
case SmartIoMode::PwmOutput:
setPublisher.Set(0);
break;
case SmartIoMode::AddressableLED:
ledcountPublisher.Set(0);
ledoffsetPublisher.Set(0);
break;
// These don't need to set any value
case SmartIoMode::DigitalInput:
@@ -189,4 +199,26 @@ int32_t SmartIo::GetCounter(int32_t* value) {
return 0;
}
int32_t SmartIo::SetLedStart(int32_t start) {
if (currentMode != SmartIoMode::AddressableLED) {
return INCOMPATIBLE_STATE;
}
if (start < 0 || start >= HAL_kAddressableLEDMaxLength) {
return PARAMETER_OUT_OF_RANGE;
}
ledoffsetPublisher.Set(start);
return 0;
}
int32_t SmartIo::SetLedLength(int32_t length) {
if (currentMode != SmartIoMode::AddressableLED) {
return INCOMPATIBLE_STATE;
}
if (length < 0 || length >= HAL_kAddressableLEDMaxLength) {
return PARAMETER_OUT_OF_RANGE;
}
ledcountPublisher.Set(length);
return 0;
}
} // namespace hal

View File

@@ -25,6 +25,7 @@ enum class SmartIoMode {
PwmOutput = 4,
SingleCounterRising = 5,
SingleCounterFalling = 6,
AddressableLED = 13,
};
enum class PwmOutputPeriod {
@@ -46,6 +47,9 @@ struct SmartIo {
nt::IntegerPublisher periodSetPublisher;
nt::IntegerSubscriber periodGetSubscriber;
nt::IntegerPublisher ledcountPublisher;
nt::IntegerPublisher ledoffsetPublisher;
int32_t InitializeMode(SmartIoMode mode);
int32_t SwitchDioDirection(bool input);
@@ -63,6 +67,9 @@ struct SmartIo {
int32_t GetAnalogInput(uint16_t* value);
int32_t GetCounter(int32_t* count);
int32_t SetLedStart(int32_t start);
int32_t SetLedLength(int32_t length);
};
extern DigitalHandleResource<HAL_DigitalHandle, SmartIo, kNumSmartIo>*

View File

@@ -8,32 +8,27 @@
extern "C" {
int32_t HALSIM_FindAddressableLEDForChannel(int32_t channel) {
return 0;
}
void HALSIM_ResetAddressableLEDData(int32_t index) {}
int32_t HALSIM_GetAddressableLEDData(int32_t index,
int32_t HALSIM_GetAddressableLEDData(int32_t start, int32_t length,
struct HAL_AddressableLEDData* data) {
return 0;
}
void HALSIM_SetAddressableLEDData(int32_t index,
const struct HAL_AddressableLEDData* data,
int32_t length) {}
void HALSIM_SetAddressableLEDData(int32_t start, int32_t length,
const struct HAL_AddressableLEDData* data) {}
#define DEFINE_CAPI(TYPE, CAPINAME, RETURN) \
HAL_SIMDATAVALUE_STUB_CAPI(TYPE, HALSIM, AddressableLED##CAPINAME, RETURN)
DEFINE_CAPI(HAL_Bool, Initialized, false)
DEFINE_CAPI(int32_t, OutputPort, 0)
DEFINE_CAPI(int32_t, Start, 0)
DEFINE_CAPI(int32_t, Length, 0)
DEFINE_CAPI(HAL_Bool, Running, false)
#undef DEFINE_CAPI
#define DEFINE_CAPI(TYPE, CAPINAME, RETURN) \
HAL_SIMCALLBACKREGISTRY_STUB_CAPI(TYPE, HALSIM, AddressableLED##CAPINAME)
#define DEFINE_CAPI(TYPE, CAPINAME, RETURN) \
HAL_SIMCALLBACKREGISTRY_STUB_CAPI_NOINDEX(TYPE, HALSIM, \
AddressableLED##CAPINAME)
DEFINE_CAPI(HAL_ConstBufferCallback, Data, data)

View File

@@ -19,22 +19,22 @@ using namespace halsimgui;
namespace {
class AddressableLEDModel : public glass::LEDDisplayModel {
public:
explicit AddressableLEDModel(int32_t index) : m_index{index} {}
explicit AddressableLEDModel(int32_t channel) : m_channel{channel} {}
void Update() override {}
bool Exists() override {
return HALSIM_GetAddressableLEDInitialized(m_index);
return HALSIM_GetAddressableLEDInitialized(m_channel);
}
bool IsRunning() override { return HALSIM_GetAddressableLEDRunning(m_index); }
std::span<const Data> GetData(wpi::SmallVectorImpl<Data>&) override {
size_t length = HALSIM_GetAddressableLEDData(m_index, m_data);
size_t length = HALSIM_GetAddressableLEDData(
HALSIM_GetAddressableLEDStart(m_channel),
HALSIM_GetAddressableLEDLength(m_channel), m_data);
return {reinterpret_cast<Data*>(m_data), length};
}
private:
int32_t m_index;
int32_t m_channel;
HAL_AddressableLEDData m_data[HAL_kAddressableLEDMaxLength];
};
@@ -49,7 +49,7 @@ class AddressableLEDsModel : public glass::LEDDisplaysModel {
size_t GetNumLEDDisplays() override { return m_models.size(); }
void ForEachLEDDisplay(
wpi::function_ref<void(glass::LEDDisplayModel& model, int index)> func)
wpi::function_ref<void(glass::LEDDisplayModel& model, int channel)> func)
override;
private:
@@ -83,7 +83,7 @@ bool AddressableLEDsModel::Exists() {
}
void AddressableLEDsModel::ForEachLEDDisplay(
wpi::function_ref<void(glass::LEDDisplayModel& model, int index)> func) {
wpi::function_ref<void(glass::LEDDisplayModel& model, int channel)> func) {
for (int i = 0; i < static_cast<int>(m_models.size()); ++i) {
if (m_models[i]) {
func(*m_models[i], i);

View File

@@ -28,9 +28,6 @@ class PWMSimModel : public glass::PWMModel {
bool Exists() override { return HALSIM_GetPWMInitialized(m_index); }
void SetAddressableLED(int led) { m_led = led; }
int GetAddressableLED() const override { return m_led; }
glass::DoubleSource* GetSpeedData() override { return &m_speed; }
void SetSpeed(double val) override {
@@ -39,7 +36,6 @@ class PWMSimModel : public glass::PWMModel {
private:
int32_t m_index;
int m_led = -1;
PWMPulseMicrosecondSource m_speed;
};
@@ -68,21 +64,10 @@ void PWMsSimModel::Update() {
if (!model) {
model = std::make_unique<PWMSimModel>(i);
}
model->SetAddressableLED(-1);
} else {
model.reset();
}
}
static const int32_t numLED = HAL_GetNumAddressableLEDs();
for (int32_t i = 0; i < numLED; ++i) {
if (HALSIM_GetAddressableLEDInitialized(i)) {
int32_t channel = HALSIM_GetAddressableLEDOutputPort(i);
if (channel >= 0 && channel < numPWM && m_sources[channel]) {
m_sources[channel]->SetAddressableLED(i);
}
}
}
}
void PWMsSimModel::ForEachPWM(

View File

@@ -31,12 +31,10 @@ HALSimWSProviderAddressableLED::~HALSimWSProviderAddressableLED() {
void HALSimWSProviderAddressableLED::RegisterCallbacks() {
m_initCbKey = REGISTER(Initialized, "<init", bool, boolean);
m_outputPortCbKey = REGISTER(OutputPort, "<output_port", int32_t, int);
m_startCbKey = REGISTER(Start, "<start", int32_t, int);
m_lengthCbKey = REGISTER(Length, "<length", int32_t, int);
m_runningCbKey = REGISTER(Running, "<running", bool, boolean);
m_dataCbKey = HALSIM_RegisterAddressableLEDDataCallback(
0,
[](const char* name, void* param, const unsigned char* buffer,
unsigned int count) {
auto provider = static_cast<HALSimWSProviderAddressableLED*>(param);
@@ -66,15 +64,13 @@ void HALSimWSProviderAddressableLED::CancelCallbacks() {
void HALSimWSProviderAddressableLED::DoCancelCallbacks() {
HALSIM_CancelAddressableLEDInitializedCallback(m_channel, m_initCbKey);
HALSIM_CancelAddressableLEDOutputPortCallback(m_channel, m_outputPortCbKey);
HALSIM_CancelAddressableLEDStartCallback(m_channel, m_startCbKey);
HALSIM_CancelAddressableLEDLengthCallback(m_channel, m_lengthCbKey);
HALSIM_CancelAddressableLEDRunningCallback(m_channel, m_runningCbKey);
HALSIM_CancelAddressableLEDDataCallback(m_channel, m_dataCbKey);
HALSIM_CancelAddressableLEDDataCallback(m_dataCbKey);
m_initCbKey = 0;
m_outputPortCbKey = 0;
m_startCbKey = 0;
m_lengthCbKey = 0;
m_runningCbKey = 0;
m_dataCbKey = 0;
}
} // namespace wpilibws

View File

@@ -21,9 +21,8 @@ class HALSimWSProviderAddressableLED : public HALSimWSHalChanProvider {
private:
int32_t m_initCbKey = 0;
int32_t m_outputPortCbKey = 0;
int32_t m_startCbKey = 0;
int32_t m_lengthCbKey = 0;
int32_t m_runningCbKey = 0;
int32_t m_dataCbKey = 0;
};
} // namespace wpilibws

View File

@@ -4,6 +4,8 @@
#include "frc/AddressableLED.h"
#include <algorithm>
#include <hal/AddressableLED.h>
#include <hal/HALBase.h>
#include <hal/PWM.h>
@@ -12,39 +14,39 @@
#include <wpi/StackTrace.h>
#include "frc/Errors.h"
#include "frc/SensorUtil.h"
using namespace frc;
AddressableLED::AddressableLED(int port) : m_port{port} {
AddressableLED::AddressableLED(int channel) : m_channel{channel} {
if (!SensorUtil::CheckDigitalChannel(channel)) {
throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}", channel);
}
int32_t status = 0;
auto stack = wpi::GetStackTrace(1);
m_pwmHandle = HAL_InitializePWMPort(port, stack.c_str(), &status);
FRC_CheckErrorStatus(status, "Port {}", port);
if (m_pwmHandle == HAL_kInvalidHandle) {
return;
}
m_handle = HAL_InitializeAddressableLED(channel, stack.c_str(), &status);
FRC_CheckErrorStatus(status, "Channel {}", channel);
m_handle = HAL_InitializeAddressableLED(m_pwmHandle, &status);
FRC_CheckErrorStatus(status, "Port {}", port);
if (m_handle == HAL_kInvalidHandle) {
HAL_FreePWMPort(m_pwmHandle);
}
HAL_ReportUsage("IO", port, "AddressableLED");
HAL_ReportUsage("IO", channel, "AddressableLED");
}
void AddressableLED::SetColorOrder(AddressableLED::ColorOrder order) {
m_colorOrder = order;
}
void AddressableLED::SetStart(int start) {
m_start = start;
int32_t status = 0;
HAL_SetAddressableLEDColorOrder(
m_handle, static_cast<HAL_AddressableLEDColorOrder>(order), &status);
FRC_CheckErrorStatus(status, "Port {} Color order {}", m_port, order);
HAL_SetAddressableLEDStart(m_handle, start, &status);
FRC_CheckErrorStatus(status, "Channel {} start {}", m_channel, start);
}
void AddressableLED::SetLength(int length) {
m_length = length;
int32_t status = 0;
HAL_SetAddressableLEDLength(m_handle, length, &status);
FRC_CheckErrorStatus(status, "Port {} length {}", m_port, length);
FRC_CheckErrorStatus(status, "Channel {} length {}", m_channel, length);
}
static_assert(sizeof(AddressableLED::LEDData) == sizeof(HAL_AddressableLEDData),
@@ -52,45 +54,25 @@ static_assert(sizeof(AddressableLED::LEDData) == sizeof(HAL_AddressableLEDData),
void AddressableLED::SetData(std::span<const LEDData> ledData) {
int32_t status = 0;
HAL_WriteAddressableLEDData(m_handle, ledData.data(), ledData.size(),
&status);
FRC_CheckErrorStatus(status, "Port {}", m_port);
HAL_SetAddressableLEDData(
m_start, std::min(static_cast<size_t>(m_length), ledData.size()),
static_cast<HAL_AddressableLEDColorOrder>(m_colorOrder), ledData.data(),
&status);
FRC_CheckErrorStatus(status, "Port {}", m_channel);
}
void AddressableLED::SetData(std::initializer_list<LEDData> ledData) {
int32_t status = 0;
HAL_WriteAddressableLEDData(m_handle, ledData.begin(), ledData.size(),
&status);
FRC_CheckErrorStatus(status, "Port {}", m_port);
SetData(std::span{ledData.begin(), ledData.end()});
}
void AddressableLED::SetBitTiming(units::nanosecond_t highTime0,
units::nanosecond_t lowTime0,
units::nanosecond_t highTime1,
units::nanosecond_t lowTime1) {
void AddressableLED::SetGlobalData(int start, ColorOrder colorOrder,
std::span<const LEDData> ledData) {
int32_t status = 0;
HAL_SetAddressableLEDBitTiming(
m_handle, highTime0.to<int32_t>(), lowTime0.to<int32_t>(),
highTime1.to<int32_t>(), lowTime1.to<int32_t>(), &status);
FRC_CheckErrorStatus(status, "Port {}", m_port);
}
void AddressableLED::SetSyncTime(units::microsecond_t syncTime) {
int32_t status = 0;
HAL_SetAddressableLEDSyncTime(m_handle, syncTime.to<int32_t>(), &status);
FRC_CheckErrorStatus(status, "Port {}", m_port);
}
void AddressableLED::Start() {
int32_t status = 0;
HAL_StartAddressableLEDOutput(m_handle, &status);
FRC_CheckErrorStatus(status, "Port {}", m_port);
}
void AddressableLED::Stop() {
int32_t status = 0;
HAL_StopAddressableLEDOutput(m_handle, &status);
FRC_CheckErrorStatus(status, "Port {}", m_port);
HAL_SetAddressableLEDData(
start, ledData.size(),
static_cast<HAL_AddressableLEDColorOrder>(colorOrder), ledData.data(),
&status);
FRC_CheckErrorStatus(status, "");
}
void AddressableLED::LEDData::SetHSV(int h, int s, int v) {

View File

@@ -9,108 +9,90 @@
#include <hal/simulation/AddressableLEDData.h>
#include "frc/AddressableLED.h"
using namespace frc;
using namespace frc::sim;
AddressableLEDSim::AddressableLEDSim() : m_index{0} {}
AddressableLEDSim::AddressableLEDSim(int channel) : m_channel{channel} {}
AddressableLEDSim::AddressableLEDSim(const AddressableLED& addressableLED)
: m_index{0} {}
AddressableLEDSim AddressableLEDSim::CreateForChannel(int pwmChannel) {
int index = HALSIM_FindAddressableLEDForChannel(pwmChannel);
if (index < 0) {
throw std::out_of_range("no addressable LED found for PWM channel");
}
return AddressableLEDSim{index};
}
AddressableLEDSim AddressableLEDSim::CreateForIndex(int index) {
return AddressableLEDSim{index};
}
: m_channel{addressableLED.GetChannel()} {}
std::unique_ptr<CallbackStore> AddressableLEDSim::RegisterInitializedCallback(
NotifyCallback callback, bool initialNotify) {
auto store = std::make_unique<CallbackStore>(
m_index, -1, callback, &HALSIM_CancelAddressableLEDInitializedCallback);
m_channel, -1, callback, &HALSIM_CancelAddressableLEDInitializedCallback);
store->SetUid(HALSIM_RegisterAddressableLEDInitializedCallback(
m_index, &CallbackStoreThunk, store.get(), initialNotify));
m_channel, &CallbackStoreThunk, store.get(), initialNotify));
return store;
}
bool AddressableLEDSim::GetInitialized() const {
return HALSIM_GetAddressableLEDInitialized(m_index);
return HALSIM_GetAddressableLEDInitialized(m_channel);
}
void AddressableLEDSim::SetInitialized(bool initialized) {
HALSIM_SetAddressableLEDInitialized(m_index, initialized);
HALSIM_SetAddressableLEDInitialized(m_channel, initialized);
}
std::unique_ptr<CallbackStore> AddressableLEDSim::RegisterOutputPortCallback(
std::unique_ptr<CallbackStore> AddressableLEDSim::RegisterStartCallback(
NotifyCallback callback, bool initialNotify) {
auto store = std::make_unique<CallbackStore>(
m_index, -1, callback, &HALSIM_CancelAddressableLEDOutputPortCallback);
store->SetUid(HALSIM_RegisterAddressableLEDOutputPortCallback(
m_index, &CallbackStoreThunk, store.get(), initialNotify));
m_channel, -1, callback, &HALSIM_CancelAddressableLEDStartCallback);
store->SetUid(HALSIM_RegisterAddressableLEDStartCallback(
m_channel, &CallbackStoreThunk, store.get(), initialNotify));
return store;
}
int AddressableLEDSim::GetOutputPort() const {
return HALSIM_GetAddressableLEDOutputPort(m_index);
int AddressableLEDSim::GetStart() const {
return HALSIM_GetAddressableLEDStart(m_channel);
}
void AddressableLEDSim::SetOutputPort(int outputPort) {
HALSIM_SetAddressableLEDOutputPort(m_index, outputPort);
void AddressableLEDSim::SetStart(int start) {
HALSIM_SetAddressableLEDStart(m_channel, start);
}
std::unique_ptr<CallbackStore> AddressableLEDSim::RegisterLengthCallback(
NotifyCallback callback, bool initialNotify) {
auto store = std::make_unique<CallbackStore>(
m_index, -1, callback, &HALSIM_CancelAddressableLEDLengthCallback);
m_channel, -1, callback, &HALSIM_CancelAddressableLEDLengthCallback);
store->SetUid(HALSIM_RegisterAddressableLEDLengthCallback(
m_index, &CallbackStoreThunk, store.get(), initialNotify));
m_channel, &CallbackStoreThunk, store.get(), initialNotify));
return store;
}
int AddressableLEDSim::GetLength() const {
return HALSIM_GetAddressableLEDLength(m_index);
return HALSIM_GetAddressableLEDLength(m_channel);
}
void AddressableLEDSim::SetLength(int length) {
HALSIM_SetAddressableLEDLength(m_index, length);
HALSIM_SetAddressableLEDLength(m_channel, length);
}
std::unique_ptr<CallbackStore> AddressableLEDSim::RegisterRunningCallback(
NotifyCallback callback, bool initialNotify) {
auto store = std::make_unique<CallbackStore>(
m_index, -1, callback, &HALSIM_CancelAddressableLEDRunningCallback);
store->SetUid(HALSIM_RegisterAddressableLEDRunningCallback(
m_index, &CallbackStoreThunk, store.get(), initialNotify));
return store;
int AddressableLEDSim::GetData(struct HAL_AddressableLEDData* data) const {
return GetGlobalData(GetStart(), GetLength(), data);
}
int AddressableLEDSim::GetRunning() const {
return HALSIM_GetAddressableLEDRunning(m_index);
}
void AddressableLEDSim::SetRunning(bool running) {
HALSIM_SetAddressableLEDRunning(m_index, running);
void AddressableLEDSim::SetData(struct HAL_AddressableLEDData* data) {
SetGlobalData(GetStart(), GetLength(), data);
}
std::unique_ptr<CallbackStore> AddressableLEDSim::RegisterDataCallback(
ConstBufferCallback callback, bool initialNotify) {
auto store = std::make_unique<CallbackStore>(
m_index, -1, callback, &HALSIM_CancelAddressableLEDDataCallback);
-1, callback, &HALSIM_CancelAddressableLEDDataCallback);
store->SetUid(HALSIM_RegisterAddressableLEDDataCallback(
m_index, &ConstBufferCallbackStoreThunk, store.get()));
&ConstBufferCallbackStoreThunk, store.get()));
return store;
}
int AddressableLEDSim::GetData(struct HAL_AddressableLEDData* data) const {
return HALSIM_GetAddressableLEDData(m_index, data);
int AddressableLEDSim::GetGlobalData(int start, int length,
struct HAL_AddressableLEDData* data) {
return HALSIM_GetAddressableLEDData(start, length, data);
}
void AddressableLEDSim::SetData(struct HAL_AddressableLEDData* data,
int length) {
HALSIM_SetAddressableLEDData(m_index, data, length);
void AddressableLEDSim::SetGlobalData(int start, int length,
struct HAL_AddressableLEDData* data) {
HALSIM_SetAddressableLEDData(start, length, data);
}

View File

@@ -9,7 +9,6 @@
#include <hal/AddressableLED.h>
#include <hal/AddressableLEDTypes.h>
#include <hal/PWM.h>
#include <hal/Types.h>
#include <units/time.h>
@@ -21,15 +20,15 @@ namespace frc {
/**
* A class for driving addressable LEDs, such as WS2812B, WS2815, and NeoPixels.
*
* By default, the timing supports WS2812B and WS2815 LEDs, but is configurable
* using SetBitTiming()
*
* Some LEDs use a different color order than the default GRB. The color order
* is configurable using SetColorOrder().
*
* <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.
* 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.
*/
class AddressableLED {
public:
@@ -52,7 +51,6 @@ class AddressableLED {
r = _r;
g = _g;
b = _b;
padding = 0;
}
/**
@@ -101,15 +99,22 @@ class AddressableLED {
};
/**
* 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)
* @param channel the output channel to use
*/
explicit AddressableLED(int port);
explicit AddressableLED(int channel);
AddressableLED(AddressableLED&&) = default;
AddressableLED& operator=(AddressableLED&&) = default;
/**
* Gets the channel for this addressable LED.
*
* @return channel
*/
int GetChannel() const { return m_channel; }
/**
* Sets the color order for this AddressableLED. The default order is GRB.
*
@@ -119,79 +124,59 @@ class AddressableLED {
*/
void SetColorOrder(ColorOrder order);
/**
* Sets the display start of the LED strip in the global buffer.
*
* @param start the strip start, in LEDs
*/
void SetStart(int start);
/**
* Gets the display start of the LED strip in the global buffer.
*
* @return the strip start, in LEDs
*/
int GetStart() const { return m_start; }
/**
* Sets the length of the LED strip.
*
* <p>Calling this is an expensive call, so its 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
*/
void SetLength(int 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.
* 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().
*
* @param ledData the buffer to write
*/
void SetData(std::span<const LEDData> ledData);
/**
* 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.
* 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().
*
* @param ledData the buffer to write
*/
void SetData(std::initializer_list<LEDData> ledData);
/**
* 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 (default 400 ns)
* @param lowTime0 low time for 0 bit (default 900 ns)
* @param highTime1 high time for 1 bit (default 900 ns)
* @param lowTime1 low time for 1 bit (default 600 ns)
* @param start the start location, in LEDs
* @param colorOrder the color order
* @param ledData the buffer to write
*/
void SetBitTiming(units::nanosecond_t highTime0, units::nanosecond_t lowTime0,
units::nanosecond_t highTime1,
units::nanosecond_t 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 (default 280us)
*/
void SetSyncTime(units::microsecond_t syncTime);
/**
* Starts the output.
*
* <p>The output writes continuously.
*/
void Start();
/**
* Stops the output.
*/
void Stop();
static void SetGlobalData(int start, ColorOrder colorOrder,
std::span<const LEDData> ledData);
private:
hal::Handle<HAL_DigitalHandle, HAL_FreePWMPort> m_pwmHandle;
hal::Handle<HAL_AddressableLEDHandle, HAL_FreeAddressableLED> m_handle;
int m_port;
int m_channel;
int m_start{0};
int m_length{0};
ColorOrder m_colorOrder{kGRB};
};
constexpr auto format_as(AddressableLED::ColorOrder order) {

View File

@@ -22,9 +22,11 @@ namespace sim {
class AddressableLEDSim {
public:
/**
* Constructs for the first addressable LED.
* Constructs an addressable LED for a specific channel.
*
* @param channel output channel
*/
AddressableLEDSim();
explicit AddressableLEDSim(int channel);
/**
* Constructs from an AddressableLED object.
@@ -33,25 +35,6 @@ class AddressableLEDSim {
*/
explicit AddressableLEDSim(const AddressableLED& addressableLED);
/**
* Creates an AddressableLEDSim for a PWM channel.
*
* @param pwmChannel PWM channel
* @return Simulated object
* @throws std::out_of_range if no AddressableLED is configured for that
* channel
*/
static AddressableLEDSim CreateForChannel(int pwmChannel);
/**
* Creates an AddressableLEDSim for a simulated index.
* The index is incremented for each simulated AddressableLED.
*
* @param index simulator index
* @return Simulated object
*/
static AddressableLEDSim CreateForIndex(int index);
/**
* Register a callback on the Initialized property.
*
@@ -79,30 +62,30 @@ class AddressableLEDSim {
void SetInitialized(bool 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
* @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 CallbackStore object associated with this callback
*/
[[nodiscard]]
std::unique_ptr<CallbackStore> RegisterOutputPortCallback(
NotifyCallback callback, bool initialNotify);
std::unique_ptr<CallbackStore> RegisterStartCallback(NotifyCallback callback,
bool initialNotify);
/**
* Get the output port.
* Get the start.
*
* @return the output port
* @return the start
*/
int GetOutputPort() const;
int GetStart() const;
/**
* Change the output port.
* Change the start.
*
* @param outputPort the new output port
* @param start the new start
*/
void SetOutputPort(int outputPort);
void SetStart(int start);
/**
* Register a callback on the length.
@@ -130,44 +113,6 @@ class AddressableLEDSim {
*/
void SetLength(int 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 CallbackStore object associated with this callback
*/
[[nodiscard]]
std::unique_ptr<CallbackStore> RegisterRunningCallback(
NotifyCallback callback, bool initialNotify);
/**
* Check if the LEDs are running.
*
* @return true if they are
*/
int GetRunning() const;
/**
* Change whether the LEDs are active.
*
* @param running the new value
*/
void SetRunning(bool running);
/**
* Register a callback on the LED data.
*
* @param callback the callback that will be called whenever the LED data is
* changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the CallbackStore object associated with this callback
*/
[[nodiscard]]
std::unique_ptr<CallbackStore> RegisterDataCallback(
ConstBufferCallback callback, bool initialNotify);
/**
* Get the LED data.
*
@@ -180,14 +125,44 @@ class AddressableLEDSim {
* Change the LED data.
*
* @param data the new data
* @param length the length of the LED data
*/
void SetData(struct HAL_AddressableLEDData* data, int length);
void SetData(struct HAL_AddressableLEDData* data);
/**
* Register a callback on the LED data.
*
* @param callback the callback that will be called whenever the LED data is
* changed
* @param initialNotify if true, the callback will be run on the initial value
* @return the CallbackStore object associated with this callback
*/
[[nodiscard]]
static std::unique_ptr<CallbackStore> RegisterDataCallback(
ConstBufferCallback callback, bool initialNotify);
/**
* Get the global LED data.
*
* @param start the start of the LED data
* @param length the length of the LED data
* @param data output parameter to fill with LED data
* @return the length of the LED data
*/
static int GetGlobalData(int start, int length,
struct HAL_AddressableLEDData* data);
/**
* Change the global LED data.
*
* @param start the start of the LED data
* @param length the length of the LED data
* @param data the new data
*/
static void SetGlobalData(int start, int length,
struct HAL_AddressableLEDData* data);
private:
explicit AddressableLEDSim(int index) : m_index{index} {}
int m_index;
int m_channel;
};
} // namespace sim
} // namespace frc

View File

@@ -1031,7 +1031,6 @@ void AssertIndexColor(std::span<AddressableLED::LEDData> data, int index,
Color color) {
frc::Color8Bit color8bit{color};
EXPECT_EQ(0, data[index].padding);
EXPECT_EQ(color8bit.red, data[index].r & 0xFF);
EXPECT_EQ(color8bit.green, data[index].g & 0xFF);
EXPECT_EQ(color8bit.blue, data[index].b & 0xFF);

View File

@@ -18,7 +18,7 @@ TEST(AddressableLEDSimTest, InitializationCallback) {
HAL_Initialize(500, 0);
BooleanCallback callback;
AddressableLEDSim sim;
AddressableLEDSim sim{0};
auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
EXPECT_FALSE(callback.WasTriggered());
@@ -29,6 +29,26 @@ TEST(AddressableLEDSimTest, InitializationCallback) {
EXPECT_TRUE(callback.GetLastValue());
}
TEST(AddressableLEDSimTest, SetStart) {
HAL_Initialize(500, 0);
AddressableLED led{0};
AddressableLEDSim sim{led};
IntCallback callback;
auto cb = sim.RegisterStartCallback(callback.GetCallback(), false);
EXPECT_EQ(0, sim.GetStart()); // Defaults to 0
std::array<AddressableLED::LEDData, 50> ledData;
led.SetStart(1);
led.SetData(ledData);
EXPECT_EQ(1, sim.GetStart());
EXPECT_TRUE(callback.WasTriggered());
EXPECT_EQ(1, callback.GetLastValue());
}
TEST(AddressableLEDSimTest, SetLength) {
HAL_Initialize(500, 0);
@@ -38,7 +58,7 @@ TEST(AddressableLEDSimTest, SetLength) {
auto cb = sim.RegisterLengthCallback(callback.GetCallback(), false);
EXPECT_EQ(1, sim.GetLength()); // Defaults to 1 led
EXPECT_EQ(0, sim.GetLength()); // Defaults to 0 leds
std::array<AddressableLED::LEDData, 50> ledData;
led.SetLength(ledData.max_size());
@@ -49,51 +69,26 @@ TEST(AddressableLEDSimTest, SetLength) {
EXPECT_EQ(50, callback.GetLastValue());
}
TEST(AddressableLEDSimTest, SetRunning) {
HAL_Initialize(500, 0);
AddressableLEDSim sim = AddressableLEDSim::CreateForIndex(0);
BooleanCallback callback;
auto cb = sim.RegisterRunningCallback(callback.GetCallback(), false);
AddressableLED led{0};
EXPECT_FALSE(sim.GetRunning());
led.Start();
EXPECT_TRUE(sim.GetRunning());
EXPECT_TRUE(callback.WasTriggered());
EXPECT_TRUE(callback.GetLastValue());
callback.Reset();
led.Stop();
EXPECT_FALSE(sim.GetRunning());
EXPECT_TRUE(callback.WasTriggered());
EXPECT_FALSE(callback.GetLastValue());
}
TEST(AddressableLEDSimTest, SetData) {
AddressableLED led{0};
AddressableLEDSim sim = AddressableLEDSim::CreateForChannel(0);
AddressableLEDSim sim{0};
bool callbackHit = false;
std::array<AddressableLED::LEDData, 3> setData;
auto cb = sim.RegisterDataCallback(
[&](std::string_view, const unsigned char* buffer, unsigned int count) {
ASSERT_EQ(count, 12u);
ASSERT_EQ(count, 9u);
EXPECT_EQ(0, buffer[0]);
EXPECT_EQ(0, buffer[1]);
EXPECT_EQ(255u, buffer[2]);
EXPECT_EQ(0, buffer[3]);
EXPECT_EQ(255u, buffer[4]);
EXPECT_EQ(0, buffer[5]);
EXPECT_EQ(0, buffer[4]);
EXPECT_EQ(255u, buffer[5]);
EXPECT_EQ(0, buffer[6]);
EXPECT_EQ(255u, buffer[6]);
EXPECT_EQ(0, buffer[7]);
EXPECT_EQ(255u, buffer[8]);
EXPECT_EQ(0, buffer[9]);
EXPECT_EQ(0, buffer[10]);
EXPECT_EQ(0, buffer[11]);
EXPECT_EQ(0, buffer[8]);
callbackHit = true;
},
@@ -115,17 +110,14 @@ TEST(AddressableLEDSimTest, SetData) {
EXPECT_EQ(0xFF, simData[0].r);
EXPECT_EQ(0x00, simData[0].g);
EXPECT_EQ(0x00, simData[0].b);
EXPECT_EQ(0x00, simData[0].padding);
EXPECT_EQ(0x00, simData[1].r);
EXPECT_EQ(0xFF, simData[1].g);
EXPECT_EQ(0x00, simData[1].b);
EXPECT_EQ(0x00, simData[1].padding);
EXPECT_EQ(0x00, simData[2].r);
EXPECT_EQ(0x00, simData[2].g);
EXPECT_EQ(0xFF, simData[2].b);
EXPECT_EQ(0x00, simData[2].padding);
}
} // namespace frc::sim

View File

@@ -37,5 +37,5 @@ TEST(SimInitializationTest, AllInitialize) {
(void)rrsim;
DutyCycleSim dcsim = DutyCycleSim::CreateForChannel(0);
(void)dcsim;
AddressableLEDSim adLED;
AddressableLEDSim adLED{0};
}

View File

@@ -6,10 +6,8 @@
Robot::Robot() {
// Default to a length of 60, start empty output
// Length is expensive to set, so only set it once, then just update data
m_led.SetLength(kLength);
m_led.SetData(m_ledBuffer);
m_led.Start();
}
void Robot::RobotPeriodic() {

View File

@@ -18,9 +18,8 @@ class Robot : public frc::TimedRobot {
private:
static constexpr int kLength = 60;
// PWM port 9
// Must be a PWM header, not MXP or DIO
frc::AddressableLED m_led{9};
// SmartIO port 1
frc::AddressableLED m_led{1};
std::array<frc::AddressableLED::LEDData, kLength>
m_ledBuffer; // Reuse the buffer

View File

@@ -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);
}
}

View File

@@ -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;
}
/**

View File

@@ -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);
}
}

View File

@@ -21,7 +21,7 @@ class AddressableLEDSimTest {
@Test
void testInitialization() {
HAL.initialize(500, 0);
AddressableLEDSim sim = new AddressableLEDSim();
AddressableLEDSim sim = new AddressableLEDSim(0);
assertFalse(sim.getInitialized());
BooleanCallback initializedCallback = new BooleanCallback();
@@ -36,11 +36,11 @@ class AddressableLEDSimTest {
@Test
void testLength() {
AddressableLEDSim sim = new AddressableLEDSim();
AddressableLEDSim sim = new AddressableLEDSim(0);
IntCallback callback = new IntCallback();
try (CallbackStore cb = sim.registerLengthCallback(callback, false);
AddressableLED led = new AddressableLED(0)) {
assertEquals(1, sim.getLength()); // Defaults to 1 led
assertEquals(0, sim.getLength()); // Defaults to 0 leds
AddressableLEDBuffer ledData = new AddressableLEDBuffer(50);
led.setLength(ledData.getLength());
@@ -52,37 +52,14 @@ class AddressableLEDSimTest {
}
}
@Test
void testSetRunning() {
AddressableLEDSim sim = AddressableLEDSim.createForIndex(0);
BooleanCallback callback = new BooleanCallback();
try (CallbackStore cb = sim.registerRunningCallback(callback, false);
AddressableLED led = new AddressableLED(0)) {
assertFalse(sim.getRunning());
led.start();
assertTrue(sim.getRunning());
assertTrue(callback.wasTriggered());
assertTrue(callback.getSetValue());
callback.reset();
led.stop();
assertFalse(sim.getRunning());
assertTrue(callback.wasTriggered());
assertFalse(callback.getSetValue());
}
}
@Test
void testSetData() {
AddressableLEDSim sim = new AddressableLEDSim();
AddressableLEDSim sim = new AddressableLEDSim(0);
BufferCallback callback = new BufferCallback();
try (AddressableLED led = new AddressableLED(0);
CallbackStore cb = sim.registerDataCallback(callback)) {
assertFalse(sim.getRunning());
assertEquals(1, sim.getLength()); // Defaults to 1 led
CallbackStore cb = AddressableLEDSim.registerDataCallback(callback)) {
assertEquals(0, sim.getLength()); // Defaults to 0 leds
AddressableLEDBuffer ledData = new AddressableLEDBuffer(3);
led.setLength(ledData.getLength());
@@ -94,22 +71,19 @@ class AddressableLEDSimTest {
byte[] data = sim.getData();
System.out.println(Arrays.toString(data));
assertEquals(12, data.length);
assertEquals(9, data.length);
assertEquals((byte) 0, data[0]);
assertEquals((byte) 0, data[1]);
assertEquals((byte) 255, data[2]);
assertEquals((byte) 0, data[3]);
assertEquals((byte) 255, data[4]);
assertEquals((byte) 0, data[5]);
assertEquals((byte) 0, data[4]);
assertEquals((byte) 255, data[5]);
assertEquals((byte) 0, data[6]);
assertEquals((byte) 255, data[6]);
assertEquals((byte) 0, data[7]);
assertEquals((byte) 255, data[8]);
assertEquals((byte) 0, data[9]);
assertEquals((byte) 0, data[10]);
assertEquals((byte) 0, data[11]);
assertEquals((byte) 0, data[8]);
assertTrue(callback.wasTriggered());
data = callback.getSetValue();
@@ -117,17 +91,14 @@ class AddressableLEDSimTest {
assertEquals((byte) 0, data[0]);
assertEquals((byte) 0, data[1]);
assertEquals((byte) 255, data[2]);
assertEquals((byte) 0, data[3]);
assertEquals((byte) 255, data[4]);
assertEquals((byte) 0, data[5]);
assertEquals((byte) 0, data[4]);
assertEquals((byte) 255, data[5]);
assertEquals((byte) 0, data[6]);
assertEquals((byte) 255, data[6]);
assertEquals((byte) 0, data[7]);
assertEquals((byte) 255, data[8]);
assertEquals((byte) 0, data[9]);
assertEquals((byte) 0, data[10]);
assertEquals((byte) 0, data[11]);
assertEquals((byte) 0, data[8]);
}
}
}

View File

@@ -31,19 +31,16 @@ public class Robot extends TimedRobot {
/** Called once at the beginning of the robot program. */
public Robot() {
// PWM port 9
// Must be a PWM header, not MXP or DIO
m_led = new AddressableLED(9);
// SmartIO port 1
m_led = new AddressableLED(1);
// Reuse buffer
// Default to a length of 60, start empty output
// Length is expensive to set, so only set it once, then just update data
// Default to a length of 60
m_ledBuffer = new AddressableLEDBuffer(60);
m_led.setLength(m_ledBuffer.getLength());
// Set the data
m_led.setData(m_ledBuffer);
m_led.start();
}
@Override