2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
#include "hal/AddressableLED.h"
|
|
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
#include <fmt/format.h>
|
|
|
|
|
|
2019-11-17 15:05:56 -08:00
|
|
|
#include "DigitalInternal.h"
|
|
|
|
|
#include "HALInitializer.h"
|
2021-05-01 13:22:08 -07:00
|
|
|
#include "HALInternal.h"
|
2019-11-17 15:05:56 -08:00
|
|
|
#include "PortsInternal.h"
|
|
|
|
|
#include "hal/Errors.h"
|
|
|
|
|
#include "hal/handles/HandlesInternal.h"
|
|
|
|
|
#include "hal/handles/LimitedHandleResource.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;
|
|
|
|
|
|
2020-12-28 01:19:59 -08:00
|
|
|
namespace hal::init {
|
2019-11-17 15:05:56 -08:00
|
|
|
void InitializeAddressableLED() {
|
|
|
|
|
static LimitedHandleResource<HAL_AddressableLEDHandle, AddressableLED,
|
|
|
|
|
kNumAddressableLEDs,
|
|
|
|
|
HAL_HandleEnum::AddressableLED>
|
|
|
|
|
dcH;
|
|
|
|
|
ledHandles = &dcH;
|
|
|
|
|
}
|
2020-12-28 01:19:59 -08:00
|
|
|
} // namespace hal::init
|
2019-11-17 15:05:56 -08:00
|
|
|
|
2019-11-17 16:39:38 -08:00
|
|
|
extern "C" {
|
|
|
|
|
HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
|
|
|
|
|
HAL_DigitalHandle outputPort, int32_t* status) {
|
2019-11-17 15:05:56 -08:00
|
|
|
hal::init::CheckInit();
|
|
|
|
|
|
2019-11-18 15:25:04 -08:00
|
|
|
auto digitalPort =
|
|
|
|
|
hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM);
|
|
|
|
|
|
|
|
|
|
if (!digitalPort) {
|
|
|
|
|
// If DIO was passed, channel error, else generic error
|
|
|
|
|
if (getHandleType(outputPort) == hal::HAL_HandleEnum::DIO) {
|
|
|
|
|
*status = HAL_LED_CHANNEL_ERROR;
|
|
|
|
|
} else {
|
|
|
|
|
*status = HAL_HANDLE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
return HAL_kInvalidHandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (digitalPort->channel >= kNumPWMHeaders) {
|
|
|
|
|
*status = HAL_LED_CHANNEL_ERROR;
|
|
|
|
|
return HAL_kInvalidHandle;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-17 15:05:56 -08:00
|
|
|
HAL_AddressableLEDHandle handle = ledHandles->Allocate();
|
|
|
|
|
if (handle == HAL_kInvalidHandle) {
|
|
|
|
|
*status = NO_AVAILABLE_RESOURCES;
|
|
|
|
|
return HAL_kInvalidHandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2019-11-18 15:25:04 -08:00
|
|
|
SimAddressableLEDData[index].outputPort = digitalPort->channel;
|
2019-11-17 15:05:56 -08:00
|
|
|
SimAddressableLEDData[index].length = 1;
|
|
|
|
|
SimAddressableLEDData[index].running = false;
|
|
|
|
|
SimAddressableLEDData[index].initialized = true;
|
|
|
|
|
led->index = index;
|
|
|
|
|
return handle;
|
2019-11-17 16:39:38 -08:00
|
|
|
}
|
|
|
|
|
|
2019-11-17 15:05:56 -08:00
|
|
|
void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
|
|
|
|
|
auto led = ledHandles->Get(handle);
|
|
|
|
|
ledHandles->Free(handle);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (!led) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-17 15:05:56 -08:00
|
|
|
SimAddressableLEDData[led->index].running = false;
|
|
|
|
|
SimAddressableLEDData[led->index].initialized = false;
|
|
|
|
|
}
|
2019-11-17 16:39:38 -08:00
|
|
|
|
[hal, wpilib] AddressableLED: add support for other color orders (#7102)
Many LED strips use different color order (GRB in particular is common).
This makes the change at the HAL level. This solves 2 problems; first, no code needs to change in the high level drivers, which was challenging for C++, and second, simulation will behave properly as no conversion is needed. The HAL will accept an array of data objects in the same order no matter what the selected output order is, and will convert before sending it to the FPGA for output.
To accomplish this, NEON bulk load/interleave instructions are utilized. The low level implementation (load, store, and alignment functions) come from the Simd Library. The high level implementations are inspired by the image conversion functions in the simd library, but have diverged significantly.
Much of the implementation uses templates and inlined functions rather than runtime parameters; This is a trade off between the size of the generated code and the amount of function calls done at runtime. Currently, the entire conversion operation is inlined.
2025-02-07 15:36:41 -05:00
|
|
|
void HAL_SetAddressableLEDColorOrder(HAL_AddressableLEDHandle handle,
|
|
|
|
|
HAL_AddressableLEDColorOrder colorOrder,
|
|
|
|
|
int32_t* status) {}
|
|
|
|
|
|
2019-11-17 16:39:38 -08:00
|
|
|
void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
|
|
|
|
|
HAL_DigitalHandle outputPort,
|
2019-11-17 15:05:56 -08:00
|
|
|
int32_t* status) {
|
|
|
|
|
auto led = ledHandles->Get(handle);
|
|
|
|
|
if (!led) {
|
|
|
|
|
*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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
|
2019-11-17 15:05:56 -08:00
|
|
|
int32_t length, int32_t* status) {
|
|
|
|
|
auto led = ledHandles->Get(handle);
|
|
|
|
|
if (!led) {
|
|
|
|
|
*status = HAL_HANDLE_ERROR;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-01 13:22:08 -07:00
|
|
|
if (length > HAL_kAddressableLEDMaxLength || length < 0) {
|
2019-11-17 15:05:56 -08:00
|
|
|
*status = PARAMETER_OUT_OF_RANGE;
|
2021-06-06 16:13:58 -07:00
|
|
|
hal::SetLastError(
|
|
|
|
|
status,
|
|
|
|
|
fmt::format(
|
|
|
|
|
"LED length must be less than or equal to {}. {} was requested",
|
|
|
|
|
HAL_kAddressableLEDMaxLength, length));
|
2019-11-17 15:05:56 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SimAddressableLEDData[led->index].length = length;
|
|
|
|
|
}
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
|
|
|
|
|
const struct HAL_AddressableLEDData* data,
|
2019-11-17 15:05:56 -08:00
|
|
|
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;
|
2021-06-06 16:13:58 -07:00
|
|
|
hal::SetLastError(
|
|
|
|
|
status,
|
|
|
|
|
fmt::format(
|
|
|
|
|
"Data length must be less than or equal to {}. {} was requested",
|
2023-08-28 15:15:14 -07:00
|
|
|
SimAddressableLEDData[led->index].length.Get(), length));
|
2019-11-17 15:05:56 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SimAddressableLEDData[led->index].SetData(data, length);
|
|
|
|
|
}
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
|
2025-02-10 07:23:04 -08:00
|
|
|
int32_t highTime0, int32_t lowTime0,
|
|
|
|
|
int32_t highTime1, int32_t lowTime1,
|
2019-11-17 16:39:38 -08:00
|
|
|
int32_t* status) {}
|
|
|
|
|
|
|
|
|
|
void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
|
2025-02-10 07:23:04 -08:00
|
|
|
int32_t syncTime, int32_t* status) {}
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle,
|
2019-11-17 15:05:56 -08:00
|
|
|
int32_t* status) {
|
|
|
|
|
auto led = ledHandles->Get(handle);
|
|
|
|
|
if (!led) {
|
|
|
|
|
*status = HAL_HANDLE_ERROR;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SimAddressableLEDData[led->index].running = true;
|
|
|
|
|
}
|
2019-11-17 16:39:38 -08:00
|
|
|
|
|
|
|
|
void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle,
|
2019-11-17 15:05:56 -08:00
|
|
|
int32_t* status) {
|
|
|
|
|
auto led = ledHandles->Get(handle);
|
|
|
|
|
if (!led) {
|
|
|
|
|
*status = HAL_HANDLE_ERROR;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SimAddressableLEDData[led->index].running = false;
|
|
|
|
|
}
|
2019-11-17 16:39:38 -08:00
|
|
|
} // extern "C"
|