blob: 5e55c93e14d3557d93c2b7aaceabdca7eb1e4186 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "hal/AddressableLED.h"
6
7#include <cstring>
8
Austin Schuh812d0d12021-11-04 20:16:48 -07009#include <fmt/format.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080010
11#include "ConstantsInternal.h"
12#include "DigitalInternal.h"
James Kuszmaulcf324122023-01-14 14:07:17 -080013#include "FPGACalls.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080014#include "HALInitializer.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070015#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080016#include "PortsInternal.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080017#include "hal/AddressableLEDTypes.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080018#include "hal/ChipObject.h"
19#include "hal/handles/HandlesInternal.h"
20#include "hal/handles/LimitedHandleResource.h"
21
22using namespace hal;
23
24namespace {
25struct AddressableLED {
26 std::unique_ptr<tLED> led;
27 void* ledBuffer;
28 size_t ledBufferSize;
29 int32_t stringLength = 1;
30};
31} // namespace
32
33static LimitedHandleResource<
34 HAL_AddressableLEDHandle, AddressableLED, kNumAddressableLEDs,
35 HAL_HandleEnum::AddressableLED>* addressableLEDHandles;
36
Austin Schuh812d0d12021-11-04 20:16:48 -070037namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080038void InitializeAddressableLED() {
39 static LimitedHandleResource<HAL_AddressableLEDHandle, AddressableLED,
40 kNumAddressableLEDs,
41 HAL_HandleEnum::AddressableLED>
42 alH;
43 addressableLEDHandles = &alH;
44}
Austin Schuh812d0d12021-11-04 20:16:48 -070045} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080046
James Kuszmaulcf324122023-01-14 14:07:17 -080047static constexpr const char* HmbName = "HMB_0_LED";
48
Brian Silverman8fce7482020-01-05 13:18:21 -080049extern "C" {
50
51HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
52 HAL_DigitalHandle outputPort, int32_t* status) {
53 hal::init::CheckInit();
54
55 auto digitalPort =
56 hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM);
57
58 if (!digitalPort) {
59 // If DIO was passed, channel error, else generic error
60 if (getHandleType(outputPort) == hal::HAL_HandleEnum::DIO) {
61 *status = HAL_LED_CHANNEL_ERROR;
62 } else {
63 *status = HAL_HANDLE_ERROR;
64 }
65 return HAL_kInvalidHandle;
66 }
67
68 if (digitalPort->channel >= kNumPWMHeaders) {
69 *status = HAL_LED_CHANNEL_ERROR;
70 return HAL_kInvalidHandle;
71 }
72
73 auto handle = addressableLEDHandles->Allocate();
74
75 if (handle == HAL_kInvalidHandle) {
76 *status = NO_AVAILABLE_RESOURCES;
77 return HAL_kInvalidHandle;
78 }
79
80 auto led = addressableLEDHandles->Get(handle);
81
82 if (!led) {
83 *status = HAL_HANDLE_ERROR;
84 return HAL_kInvalidHandle;
85 }
86
87 led->led.reset(tLED::create(status));
88
89 if (*status != 0) {
90 addressableLEDHandles->Free(handle);
91 return HAL_kInvalidHandle;
92 }
93
94 led->led->writeOutputSelect(digitalPort->channel, status);
95
96 if (*status != 0) {
97 addressableLEDHandles->Free(handle);
98 return HAL_kInvalidHandle;
99 }
100
101 led->ledBuffer = nullptr;
102 led->ledBufferSize = 0;
103
104 uint32_t session = led->led->getSystemInterface()->getHandle();
105
James Kuszmaulcf324122023-01-14 14:07:17 -0800106 *status = hal::HAL_NiFpga_OpenHmb(session, HmbName, &led->ledBufferSize,
107 &led->ledBuffer);
Brian Silverman8fce7482020-01-05 13:18:21 -0800108
109 if (*status != 0) {
110 addressableLEDHandles->Free(handle);
111 return HAL_kInvalidHandle;
112 }
113
114 return handle;
115}
116
117void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800118 auto led = addressableLEDHandles->Get(handle);
119 if (!led) {
120 return;
121 }
122 uint32_t session = led->led->getSystemInterface()->getHandle();
123 hal::HAL_NiFpga_CloseHmb(session, HmbName);
Brian Silverman8fce7482020-01-05 13:18:21 -0800124 addressableLEDHandles->Free(handle);
125}
126
127void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
128 HAL_DigitalHandle outputPort,
129 int32_t* status) {
130 auto digitalPort =
131 hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM);
132
133 if (!digitalPort) {
134 *status = HAL_HANDLE_ERROR;
135 return;
136 }
137
138 auto led = addressableLEDHandles->Get(handle);
139 if (!led) {
140 *status = HAL_HANDLE_ERROR;
141 return;
142 }
143
144 led->led->writeOutputSelect(digitalPort->channel, status);
145}
146
147void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
148 int32_t length, int32_t* status) {
149 auto led = addressableLEDHandles->Get(handle);
150 if (!led) {
151 *status = HAL_HANDLE_ERROR;
152 return;
153 }
154
Austin Schuh812d0d12021-11-04 20:16:48 -0700155 if (length > HAL_kAddressableLEDMaxLength || length < 0) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800156 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700157 hal::SetLastError(
158 status,
159 fmt::format(
160 "LED length must be less than or equal to {}. {} was requested",
161 HAL_kAddressableLEDMaxLength, length));
Brian Silverman8fce7482020-01-05 13:18:21 -0800162 return;
163 }
164
165 led->led->strobeReset(status);
166
167 while (led->led->readPixelWriteIndex(status) != 0) {
168 }
169
170 if (*status != 0) {
171 return;
172 }
173
174 led->led->writeStringLength(length, status);
175
176 led->stringLength = length;
177}
178
179static_assert(sizeof(HAL_AddressableLEDData) == sizeof(uint32_t),
180 "LED Data must be 32 bit");
181
182void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
183 const struct HAL_AddressableLEDData* data,
184 int32_t length, int32_t* status) {
185 auto led = addressableLEDHandles->Get(handle);
186 if (!led) {
187 *status = HAL_HANDLE_ERROR;
188 return;
189 }
190
Austin Schuh812d0d12021-11-04 20:16:48 -0700191 if (length > led->stringLength || length < 0) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800192 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700193 hal::SetLastError(
194 status,
195 fmt::format(
196 "Data length must be less than or equal to {}. {} was requested",
197 led->stringLength, length));
Brian Silverman8fce7482020-01-05 13:18:21 -0800198 return;
199 }
200
201 std::memcpy(led->ledBuffer, data, length * sizeof(HAL_AddressableLEDData));
202
203 asm("dmb");
204
205 led->led->strobeLoad(status);
206}
207
208void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
209 int32_t lowTime0NanoSeconds,
210 int32_t highTime0NanoSeconds,
211 int32_t lowTime1NanoSeconds,
212 int32_t highTime1NanoSeconds,
213 int32_t* status) {
214 auto led = addressableLEDHandles->Get(handle);
215 if (!led) {
216 *status = HAL_HANDLE_ERROR;
217 return;
218 }
219
220 led->led->writeLowBitTickTiming(1, highTime0NanoSeconds / 25, status);
221 led->led->writeLowBitTickTiming(0, lowTime0NanoSeconds / 25, status);
222 led->led->writeHighBitTickTiming(1, highTime1NanoSeconds / 25, status);
223 led->led->writeHighBitTickTiming(0, lowTime1NanoSeconds / 25, status);
224}
225
226void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
227 int32_t syncTimeMicroSeconds,
228 int32_t* status) {
229 auto led = addressableLEDHandles->Get(handle);
230 if (!led) {
231 *status = HAL_HANDLE_ERROR;
232 return;
233 }
234
235 led->led->writeSyncTiming(syncTimeMicroSeconds, status);
236}
237
238void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle,
239 int32_t* status) {
240 auto led = addressableLEDHandles->Get(handle);
241 if (!led) {
242 *status = HAL_HANDLE_ERROR;
243 return;
244 }
245
246 led->led->strobeStart(status);
247}
248
249void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle,
250 int32_t* status) {
251 auto led = addressableLEDHandles->Get(handle);
252 if (!led) {
253 *status = HAL_HANDLE_ERROR;
254 return;
255 }
256
257 led->led->strobeAbort(status);
258}
259} // extern "C"