diff --git a/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java b/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java new file mode 100644 index 0000000000..fb3499695a --- /dev/null +++ b/hal/src/main/java/edu/wpi/first/hal/AddressableLEDJNI.java @@ -0,0 +1,23 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package edu.wpi.first.hal; + +@SuppressWarnings("AbbreviationAsWordInName") +public class AddressableLEDJNI extends JNIWrapper { + public static native int initialize(int pwmHandle); + public static native void free(int handle); + + public static native void setLength(int handle, int length); + public static native void setData(int handle, byte[] data); + + public static native void setBitTiming(int handle, int lowTime0, int highTime0, int lowTime1, int highTime1); + public static native void setSyncTime(int handle, int syncTime); + + public static native void start(int handle); + public static native void stop(int handle); +} diff --git a/hal/src/main/native/athena/AddressableLED.cpp b/hal/src/main/native/athena/AddressableLED.cpp new file mode 100644 index 0000000000..44e16b183a --- /dev/null +++ b/hal/src/main/native/athena/AddressableLED.cpp @@ -0,0 +1,277 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "hal/AddressableLED.h" + +#include + +#include + +#include "ConstantsInternal.h" +#include "DigitalInternal.h" +#include "PortsInternal.h" +#include "hal/ChipObject.h" +#include "hal/handles/HandlesInternal.h" +#include "hal/handles/LimitedHandleResource.h" + +using namespace hal; + +constexpr int32_t kMaxStringSize = 5460; + +extern "C" { +NiFpga_Status NiFpga_ClientFunctionCall(NiFpga_Session session, uint32_t group, + uint32_t functionId, + const void* inBuffer, + size_t inBufferSize, void* outBuffer, + size_t outBufferSize); +} // extern "C" + +namespace { +struct AddressableLED { + std::unique_ptr led; + void* ledBuffer; + size_t ledBufferSize; + int32_t stringLength = 1; +}; +} // namespace + +static LimitedHandleResource< + HAL_AddressableLEDHandle, AddressableLED, kNumAddressableLEDs, + HAL_HandleEnum::AddressableLED>* addressableLEDHandles; + +namespace hal { +namespace init { +void InitializeAddressableLED() { + static LimitedHandleResource + alH; + addressableLEDHandles = &alH; +} +} // namespace init +} // namespace hal + +// Shim for broken ChipObject function +static const uint32_t clientFeature_hostMemoryBuffer = 0; +static const uint32_t hostMemoryBufferFunction_open = 2; + +// Input arguments for HMB open +struct AtomicHMBOpenInputs { + const char* memoryName; +}; + +// Output arguments for HMB open +struct AtomicHMBOpenOutputs { + size_t size; + void* virtualAddress; +}; + +static NiFpga_Status OpenHostMemoryBuffer(NiFpga_Session session, + const char* memoryName, + void** virtualAddress, size_t* size) { + struct AtomicHMBOpenOutputs outputs; + + struct AtomicHMBOpenInputs inputs; + inputs.memoryName = memoryName; + + NiFpga_Status retval = NiFpga_ClientFunctionCall( + session, clientFeature_hostMemoryBuffer, hostMemoryBufferFunction_open, + &inputs, sizeof(struct AtomicHMBOpenInputs), &outputs, + sizeof(struct AtomicHMBOpenOutputs)); + if (NiFpga_IsError(retval)) { + return retval; + } + *virtualAddress = outputs.virtualAddress; + if (size != NULL) { + *size = outputs.size; + } + return retval; +} + +extern "C" { + +HAL_AddressableLEDHandle HAL_InitializeAddressableLED( + HAL_DigitalHandle outputPort, int32_t* status) { + auto digitalPort = + hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM); + + if (!digitalPort) { + *status = HAL_HANDLE_ERROR; + return HAL_kInvalidHandle; + } + + auto handle = addressableLEDHandles->Allocate(); + + if (handle == HAL_kInvalidHandle) { + *status = NO_AVAILABLE_RESOURCES; + return HAL_kInvalidHandle; + } + + auto led = addressableLEDHandles->Get(handle); + + if (!led) { + *status = HAL_HANDLE_ERROR; + return HAL_kInvalidHandle; + } + + led->led.reset(tLED::create(status)); + + if (*status != 0) { + addressableLEDHandles->Free(handle); + return HAL_kInvalidHandle; + } + + led->led->writeOutputSelect(digitalPort->channel, status); + + if (*status != 0) { + addressableLEDHandles->Free(handle); + return HAL_kInvalidHandle; + } + + led->ledBuffer = nullptr; + led->ledBufferSize = 0; + + uint32_t session = led->led->getSystemInterface()->getHandle(); + + *status = OpenHostMemoryBuffer(session, "HMB_0_LED", &led->ledBuffer, + &led->ledBufferSize); + + if (*status != 0) { + addressableLEDHandles->Free(handle); + return HAL_kInvalidHandle; + } + + return handle; +} + +void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) { + addressableLEDHandles->Free(handle); +} + +void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle, + HAL_DigitalHandle outputPort, + int32_t* status) { + auto digitalPort = + hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM); + + if (!digitalPort) { + *status = HAL_HANDLE_ERROR; + return; + } + + auto led = addressableLEDHandles->Get(handle); + if (!led) { + *status = HAL_HANDLE_ERROR; + return; + } + + led->led->writeOutputSelect(digitalPort->channel, status); +} + +void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle, + int32_t length, int32_t* status) { + auto led = addressableLEDHandles->Get(handle); + if (!led) { + *status = HAL_HANDLE_ERROR; + return; + } + + if (length > kMaxStringSize) { + *status = PARAMETER_OUT_OF_RANGE; + return; + } + + led->led->strobeReset(status); + + while (led->led->readPixelWriteIndex(status) != 0) { + } + + if (*status != 0) { + return; + } + + led->led->writeStringLength(length, status); + + led->stringLength = length; +} + +static_assert(sizeof(HAL_AddressableLEDData) == sizeof(uint32_t), + "LED Data must be 32 bit"); + +void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle, + const struct HAL_AddressableLEDData* data, + int32_t length, int32_t* status) { + auto led = addressableLEDHandles->Get(handle); + if (!led) { + *status = HAL_HANDLE_ERROR; + return; + } + + if (length > led->stringLength) { + *status = PARAMETER_OUT_OF_RANGE; + return; + } + + std::memcpy(led->ledBuffer, data, length * sizeof(HAL_AddressableLEDData)); + + asm("dmb"); + + led->led->strobeLoad(status); +} + +void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle, + int32_t lowTime0NanoSeconds, + int32_t highTime0NanoSeconds, + int32_t lowTime1NanoSeconds, + int32_t highTime1NanoSeconds, + int32_t* status) { + auto led = addressableLEDHandles->Get(handle); + if (!led) { + *status = HAL_HANDLE_ERROR; + return; + } + + led->led->writeLowBitTickTiming(1, highTime0NanoSeconds / 25, status); + led->led->writeLowBitTickTiming(0, lowTime0NanoSeconds / 25, status); + led->led->writeHighBitTickTiming(1, highTime1NanoSeconds / 25, status); + led->led->writeHighBitTickTiming(0, lowTime1NanoSeconds / 25, status); +} + +void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle, + int32_t syncTimeMicroSeconds, + int32_t* status) { + auto led = addressableLEDHandles->Get(handle); + if (!led) { + *status = HAL_HANDLE_ERROR; + return; + } + + led->led->writeSyncTiming(syncTimeMicroSeconds, status); +} + +void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle, + int32_t* status) { + auto led = addressableLEDHandles->Get(handle); + if (!led) { + *status = HAL_HANDLE_ERROR; + return; + } + + led->led->strobeStart(status); +} + +void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle, + int32_t* status) { + auto led = addressableLEDHandles->Get(handle); + if (!led) { + *status = HAL_HANDLE_ERROR; + return; + } + + led->led->strobeAbort(status); +} +} // extern "C" diff --git a/hal/src/main/native/athena/DigitalInternal.h b/hal/src/main/native/athena/DigitalInternal.h index b486f4849c..2cb9b3c88c 100644 --- a/hal/src/main/native/athena/DigitalInternal.h +++ b/hal/src/main/native/athena/DigitalInternal.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -96,6 +96,20 @@ bool remapDigitalSource(HAL_Handle digitalSourceHandle, HAL_AnalogTriggerType analogTriggerType, uint8_t& channel, uint8_t& module, bool& analogTrigger); +/** + * Remap the Digital Channel to map to the bitfield channel of the FPGA + */ +constexpr int32_t remapDigitalChannelToBitfieldChannel(int32_t channel) { + // First 10 are headers + if (channel < kNumDigitalHeaders) return channel; + // 2nd group of 16 are mxp. So if mxp port, add 6, since they start at 10 + else if (channel < kNumDigitalMXPChannels) + return channel + 6; + // Assume SPI, so remove MXP channels + else + return channel - kNumDigitalMXPChannels; +} + /** * Map DIO channel numbers from their physical number (10 to 26) to their * position in the bit field. diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp index 804d68ac75..2dedb041f5 100644 --- a/hal/src/main/native/athena/HAL.cpp +++ b/hal/src/main/native/athena/HAL.cpp @@ -42,6 +42,7 @@ using namespace hal; namespace hal { namespace init { void InitializeHAL() { + InitializeAddressableLED(); InitializeAccelerometer(); InitializeAnalogAccumulator(); InitializeAnalogGyro(); diff --git a/hal/src/main/native/athena/HALInitializer.h b/hal/src/main/native/athena/HALInitializer.h index 12640a8d4e..acce386524 100644 --- a/hal/src/main/native/athena/HALInitializer.h +++ b/hal/src/main/native/athena/HALInitializer.h @@ -19,6 +19,7 @@ static inline void CheckInit() { } extern void InitializeAccelerometer(); +extern void InitializeAddressableLED(); extern void InitializeAnalogAccumulator(); extern void InitializeAnalogGyro(); extern void InitializeAnalogInput(); diff --git a/hal/src/main/native/athena/PortsInternal.h b/hal/src/main/native/athena/PortsInternal.h index d76f33df5c..98fc690862 100644 --- a/hal/src/main/native/athena/PortsInternal.h +++ b/hal/src/main/native/athena/PortsInternal.h @@ -36,5 +36,6 @@ constexpr int32_t kNumSolenoidChannels = 8; constexpr int32_t kNumPDPModules = 63; constexpr int32_t kNumPDPChannels = 16; constexpr int32_t kNumDutyCycles = tDutyCycle::kNumSystems; +constexpr int32_t kNumAddressableLEDs = tLED::kNumSystems; } // namespace hal diff --git a/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp b/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp new file mode 100644 index 0000000000..2ca0e4b291 --- /dev/null +++ b/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp @@ -0,0 +1,143 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include + +#include + +#include "HALUtil.h" +#include "edu_wpi_first_hal_AddressableLEDJNI.h" +#include "hal/AddressableLED.h" + +using namespace frc; +using namespace wpi::java; + +extern "C" { +/* + * Class: edu_wpi_first_hal_AddressableLEDJNI + * Method: initialize + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_edu_wpi_first_hal_AddressableLEDJNI_initialize + (JNIEnv* env, jclass, jint handle) +{ + int32_t status = 0; + auto ret = HAL_InitializeAddressableLED( + static_cast(handle), &status); + CheckStatus(env, status); + return ret; +} + +/* + * Class: edu_wpi_first_hal_AddressableLEDJNI + * Method: free + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_AddressableLEDJNI_free + (JNIEnv* env, jclass, jint handle) +{ + HAL_FreeAddressableLED(static_cast(handle)); +} + +/* + * Class: edu_wpi_first_hal_AddressableLEDJNI + * Method: setLength + * Signature: (II)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_AddressableLEDJNI_setLength + (JNIEnv* env, jclass, jint handle, jint length) +{ + int32_t status = 0; + HAL_SetAddressableLEDLength(static_cast(handle), + length, &status); + CheckStatus(env, status); +} + +/* + * Class: edu_wpi_first_hal_AddressableLEDJNI + * Method: setData + * Signature: (I[B)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_AddressableLEDJNI_setData + (JNIEnv* env, jclass, jint handle, jbyteArray arr) +{ + int32_t status = 0; + JByteArrayRef jArrRef{env, arr}; + auto arrRef = jArrRef.array(); + HAL_WriteAddressableLEDData( + static_cast(handle), + reinterpret_cast(arrRef.data()), + arrRef.size() / 4, &status); + CheckStatus(env, status); +} + +/* + * Class: edu_wpi_first_hal_AddressableLEDJNI + * Method: setBitTiming + * Signature: (IIIII)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_AddressableLEDJNI_setBitTiming + (JNIEnv* env, jclass, jint handle, jint lowTime0, jint highTime0, + jint lowTime1, jint highTime1) +{ + int32_t status = 0; + HAL_SetAddressableLEDBitTiming(static_cast(handle), + lowTime0, highTime0, lowTime1, highTime1, + &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(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(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(handle), + &status); + CheckStatus(env, status); +} +} // extern "C" diff --git a/hal/src/main/native/include/hal/AddressableLED.h b/hal/src/main/native/include/hal/AddressableLED.h new file mode 100644 index 0000000000..8e583d54a0 --- /dev/null +++ b/hal/src/main/native/include/hal/AddressableLED.h @@ -0,0 +1,54 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include "hal/AddressableLEDTypes.h" +#include "hal/Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +HAL_AddressableLEDHandle HAL_InitializeAddressableLED( + HAL_DigitalHandle outputPort, int32_t* status); + +void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle); + +void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle, + HAL_DigitalHandle outputPort, + int32_t* status); + +void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle, + int32_t length, int32_t* status); + +void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle, + const struct HAL_AddressableLEDData* data, + int32_t length, int32_t* status); + +void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle, + int32_t lowTime0NanoSeconds, + int32_t highTime0NanoSeconds, + int32_t lowTime1NanoSeconds, + int32_t highTime1NanoSeconds, + int32_t* status); + +void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle, + int32_t syncTimeMicroSeconds, + int32_t* status); + +void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle, + int32_t* status); + +void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle, + int32_t* status); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/hal/src/main/native/include/hal/AddressableLEDTypes.h b/hal/src/main/native/include/hal/AddressableLEDTypes.h new file mode 100644 index 0000000000..f1fb4fcc0e --- /dev/null +++ b/hal/src/main/native/include/hal/AddressableLEDTypes.h @@ -0,0 +1,17 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +struct HAL_AddressableLEDData { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t padding; +}; diff --git a/hal/src/main/native/include/hal/ChipObject.h b/hal/src/main/native/include/hal/ChipObject.h index 5e4bed10a8..9b321c2028 100644 --- a/hal/src/main/native/include/hal/ChipObject.h +++ b/hal/src/main/native/include/hal/ChipObject.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/hal/src/main/native/include/hal/Types.h b/hal/src/main/native/include/hal/Types.h index 53bcd6d098..1ebbe2d306 100644 --- a/hal/src/main/native/include/hal/Types.h +++ b/hal/src/main/native/include/hal/Types.h @@ -61,6 +61,8 @@ typedef HAL_Handle HAL_DMAHandle; typedef HAL_Handle HAL_DutyCycleHandle; +typedef HAL_Handle HAL_AddressableLEDHandle; + typedef HAL_CANHandle HAL_PDPHandle; typedef int32_t HAL_Bool; diff --git a/hal/src/main/native/include/hal/handles/HandlesInternal.h b/hal/src/main/native/include/hal/handles/HandlesInternal.h index ccaf997050..511433e1d3 100644 --- a/hal/src/main/native/include/hal/handles/HandlesInternal.h +++ b/hal/src/main/native/include/hal/handles/HandlesInternal.h @@ -68,6 +68,7 @@ enum class HAL_HandleEnum { SerialPort = 20, DutyCycle = 21, DMA = 22, + AddressableLED = 23, }; /** diff --git a/hal/src/main/native/sim/AddressableLED.cpp b/hal/src/main/native/sim/AddressableLED.cpp new file mode 100644 index 0000000000..63d23d9929 --- /dev/null +++ b/hal/src/main/native/sim/AddressableLED.cpp @@ -0,0 +1,45 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "hal/AddressableLED.h" + +extern "C" { +HAL_AddressableLEDHandle HAL_InitializeAddressableLED( + HAL_DigitalHandle outputPort, int32_t* status) { + return HAL_kInvalidHandle; +} + +void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {} + +void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle, + HAL_DigitalHandle outputPort, + int32_t* status) {} + +void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle, + int32_t length, int32_t* status) {} + +void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle, + const struct HAL_AddressableLEDData* data, + int32_t length, int32_t* status) {} + +void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle, + int32_t lowTime0NanoSeconds, + int32_t highTime0NanoSeconds, + int32_t lowTime1NanoSeconds, + int32_t highTime1NanoSeconds, + int32_t* status) {} + +void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle, + int32_t syncTimeMicroSeconds, + int32_t* status) {} + +void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle, + int32_t* status) {} + +void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle, + int32_t* status) {} +} // extern "C" diff --git a/myRobot/build.gradle b/myRobot/build.gradle index 2e548f556b..fa34957510 100644 --- a/myRobot/build.gradle +++ b/myRobot/build.gradle @@ -149,6 +149,7 @@ model { } myRobotCppStatic(NativeExecutableSpec) { targetBuildTypes 'debug' + nativeUtils.excludeBinariesFromStrip(it) sources { cpp { source { diff --git a/wpilibc/src/main/native/cpp/AddressableLED.cpp b/wpilibc/src/main/native/cpp/AddressableLED.cpp new file mode 100644 index 0000000000..3a835274fa --- /dev/null +++ b/wpilibc/src/main/native/cpp/AddressableLED.cpp @@ -0,0 +1,101 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "frc/AddressableLED.h" + +#include + +#include "frc/PWM.h" +#include "frc/WPIErrors.h" + +using namespace frc; + +AddressableLED::AddressableLED(PWM& output) + : m_pwmOutput{&output, NullDeleter()} { + Init(); +} + +AddressableLED::AddressableLED(PWM* output) + : m_pwmOutput{output, NullDeleter()} { + if (m_pwmOutput == nullptr) { + wpi_setWPIError(NullParameter); + } else { + Init(); + } +} + +AddressableLED::AddressableLED(std::shared_ptr output) + : m_pwmOutput{std::move(output)} { + Init(); +} + +AddressableLED::AddressableLED(int port) + : m_pwmOutput{std::make_shared(port)} { + if (!m_pwmOutput->StatusIsFatal()) { + Init(); + } +} + +AddressableLED::~AddressableLED() { HAL_FreeAddressableLED(m_handle); } + +void AddressableLED::Init() { + int32_t status = 0; + m_handle = HAL_InitializeAddressableLED(m_pwmOutput->m_handle, &status); + wpi_setHALError(status); +} + +void AddressableLED::SetLength(int length) { + int32_t status = 0; + HAL_SetAddressableLEDLength(m_handle, length, &status); + wpi_setHALError(status); +} + +static_assert(sizeof(AddressableLED::LEDData) == sizeof(HAL_AddressableLEDData), + "LED Structs MUST be the same size"); + +void AddressableLED::SetData(wpi::ArrayRef ledData) { + int32_t status = 0; + HAL_WriteAddressableLEDData(m_handle, ledData.begin(), ledData.size(), + &status); + wpi_setHALError(status); +} + +void AddressableLED::SetData(std::initializer_list ledData) { + int32_t status = 0; + HAL_WriteAddressableLEDData(m_handle, ledData.begin(), ledData.size(), + &status); + wpi_setHALError(status); +} + +void AddressableLED::SetBitTiming(units::nanosecond_t lowTime0, + units::nanosecond_t highTime0, + units::nanosecond_t lowTime1, + units::nanosecond_t highTime1) { + int32_t status = 0; + HAL_SetAddressableLEDBitTiming( + m_handle, lowTime0.to(), highTime0.to(), + lowTime1.to(), highTime1.to(), &status); + wpi_setHALError(status); +} + +void AddressableLED::SetSyncTime(units::microsecond_t syncTime) { + int32_t status = 0; + HAL_SetAddressableLEDSyncTime(m_handle, syncTime.to(), &status); + wpi_setHALError(status); +} + +void AddressableLED::Start() { + int32_t status = 0; + HAL_StartAddressableLEDOutput(m_handle, &status); + wpi_setHALError(status); +} + +void AddressableLED::Stop() { + int32_t status = 0; + HAL_StopAddressableLEDOutput(m_handle, &status); + wpi_setHALError(status); +} diff --git a/wpilibc/src/main/native/include/frc/AddressableLED.h b/wpilibc/src/main/native/include/frc/AddressableLED.h new file mode 100644 index 0000000000..3af9008bfa --- /dev/null +++ b/wpilibc/src/main/native/include/frc/AddressableLED.h @@ -0,0 +1,141 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include +#include +#include +#include + +#include "frc/ErrorBase.h" + +namespace frc { +class PWM; + +/** + * A class for driving addressable LEDs, such as WS2812s and NeoPixels. + */ +class AddressableLED : public ErrorBase { + public: + class LEDData : public HAL_AddressableLEDData { + public: + LEDData() : LEDData(0, 0, 0) {} + LEDData(int _r, int _g, int _b) { + r = _r; + g = _g; + b = _b; + padding = 0; + } + }; + + /** + * Constructs a new driver from a PWM output. + * + * @param output the pwm output to use + */ + explicit AddressableLED(PWM& output); + + /** + * Constructs a new driver from a PWM output. + * + * @param output the pwm output to use + */ + explicit AddressableLED(PWM* output); + + /** + * Constructs a new driver from a PWM output. + * + * @param output the pwm output to use + */ + explicit AddressableLED(std::shared_ptr output); + + /** + * Constructs a new driver for a specific port. + * + * @param port the output port to use (Must be a PWM port) + */ + explicit AddressableLED(int port); + + ~AddressableLED() override; + + /** + * Sets the length of the LED strip. + * + *

Calling this is an expensive call, so its best to call it once, then + * just update data. + * + * @param length the strip length + */ + void SetLength(int length); + + /** + * Sets the led output data. + * + *

If the output is enabled, this will start writing the next data cycle. + * It is safe to call, even while output is enabled. + * + * @param ledData the buffer to write + */ + void SetData(wpi::ArrayRef ledData); + + /** + * Sets the led output data. + * + *

If the output is enabled, this will start writing the next data cycle. + * It is safe to call, even while output is enabled. + * + * @param ledData the buffer to write + */ + void SetData(std::initializer_list ledData); + + /** + * Sets the bit timing. + * + *

By default, the driver is set up to drive WS2812s, so nothing needs to + * be set for those. + * + * @param lowTime0 low time for 0 bit + * @param highTime0 high time for 0 bit + * @param lowTime1 low time for 1 bit + * @param highTime1 high time for 1 bit + */ + void SetBitTiming(units::nanosecond_t lowTime0, units::nanosecond_t highTime0, + units::nanosecond_t lowTime1, + units::nanosecond_t highTime1); + + /** + * Sets the sync time. + * + *

The sync time is the time to hold output so LEDs enable. Default set for + * WS2812. + * + * @param syncTimeMicroSeconds the sync time + */ + void SetSyncTime(units::microsecond_t syncTime); + + /** + * Starts the output. + * + *

The output writes continously. + */ + void Start(); + + /** + * Stops the output. + */ + void Stop(); + + private: + void Init(); + + std::shared_ptr m_pwmOutput; + hal::Handle m_handle; +}; +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/PWM.h b/wpilibc/src/main/native/include/frc/PWM.h index 58e18d2c1f..56afd983f0 100644 --- a/wpilibc/src/main/native/include/frc/PWM.h +++ b/wpilibc/src/main/native/include/frc/PWM.h @@ -17,7 +17,7 @@ #include "frc/smartdashboard/SendableHelper.h" namespace frc { - +class AddressableLED; class SendableBuilder; /** @@ -39,6 +39,7 @@ class SendableBuilder; */ class PWM : public MotorSafety, public Sendable, public SendableHelper { public: + friend class AddressableLED; /** * Represents the amount to multiply the minimum servo-pulse pwm period by. */ diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLED.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLED.java new file mode 100644 index 0000000000..ad9bd6ec53 --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLED.java @@ -0,0 +1,122 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package edu.wpi.first.wpilibj; + +import edu.wpi.first.hal.AddressableLEDJNI; + +/** + * A class for driving addressable LEDs, such as WS2812s and NeoPixels. + */ +public class AddressableLED implements AutoCloseable { + private final PWM m_pwmOutput; + private int m_handle; + private final boolean m_ownsPwm; + + /** + * Constructs a new driver from a PWM output. + * + * @param output the pwm output to use + */ + public AddressableLED(PWM output) { + m_pwmOutput = output; + m_ownsPwm = false; + init(); + } + + /** + * Constructs a new driver for a specific port. + * + * @param port the output port to use (Must be a PWM port) + */ + public AddressableLED(int port) { + m_pwmOutput = new PWM(port); + m_ownsPwm = true; + init(); + } + + private void init() { + m_handle = AddressableLEDJNI.initialize(m_pwmOutput.m_handle); + } + + @Override + public void close() { + if (m_handle != 0) { + AddressableLEDJNI.free(m_handle); + } + if (m_ownsPwm) { + m_pwmOutput.close(); + } + } + + /** + * Sets the length of the LED strip. + * + *

Calling this is an expensive call, so its best to call it once, then just update data. + * + * @param length the strip length + */ + public void setLength(int length) { + AddressableLEDJNI.setLength(m_handle, length); + } + + /** + * Sets the led output data. + * + *

If the output is enabled, this will start writing the next data cycle. + * It is safe to call, even while output is enabled. + * + * @param buffer the buffer to write + */ + public void setData(AddressableLEDBuffer buffer) { + AddressableLEDJNI.setData(m_handle, buffer.m_buffer); + } + + /** + * Sets the bit timing. + * + *

By default, the driver is set up to drive WS2812s, so nothing needs to be set for those. + * + * @param lowTime0NanoSeconds low time for 0 bit + * @param highTime0NanoSeconds high time for 0 bit + * @param lowTime1NanoSeconds low time for 1 bit + * @param highTime1NanoSeconds high time for 1 bit + */ + public void setBitTiming(int lowTime0NanoSeconds, int highTime0NanoSeconds, + int lowTime1NanoSeconds, int highTime1NanoSeconds) { + AddressableLEDJNI.setBitTiming(m_handle, lowTime0NanoSeconds, + highTime0NanoSeconds, lowTime1NanoSeconds, + highTime1NanoSeconds); + } + + /** + * Sets the sync time. + * + *

The sync time is the time to hold output so LEDs enable. Default set for WS2812. + * + * @param syncTimeMicroSeconds the sync time + */ + public void setSyncTime(int syncTimeMicroSeconds) { + AddressableLEDJNI.setSyncTime(m_handle, syncTimeMicroSeconds); + } + + /** + * Starts the output. + * + *

The output writes continously. + */ + public void start() { + AddressableLEDJNI.start(m_handle); + } + + /** + * Stops the output. + */ + public void stop() { + AddressableLEDJNI.stop(m_handle); + } +} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLEDBuffer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLEDBuffer.java new file mode 100644 index 0000000000..926ec13a40 --- /dev/null +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/AddressableLEDBuffer.java @@ -0,0 +1,40 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package edu.wpi.first.wpilibj; + +/** + * Buffer storage for Addressable LEDs. + */ +public class AddressableLEDBuffer { + byte[] m_buffer; + + /** + * Constructs a new LED buffer with the specified length. + * + * @param length The length of the buffer in pixels + */ + public AddressableLEDBuffer(int length) { + m_buffer = new byte[length * 4]; + } + + /** + * Sets a specific led in the buffer. + * + * @param index the index to write + * @param r the r value + * @param g the g value + * @param b the b value + */ + @SuppressWarnings("ParameterName") + public void setLED(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; + } +} diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java index 22eed18557..8febd2cc4a 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java @@ -46,7 +46,9 @@ public class PWM extends MotorSafety implements Sendable, AutoCloseable { } private final int m_channel; - private int m_handle; + + // Package private to use from AddressableLED + int m_handle; /** * Allocate a PWM given a channel.